我是一名嵌入式软件开发人员,我想连接到外部设备。该设备通过SPI发送数据。该数据的结构是由外部设备制造商预定义的,无法编辑。制造商提供了一些头文件,其中包含通过SPI发送的所有数据的许多typedef。制造商还提供了一个API,以正确的方式处理接收到的数据包(我可以访问API的来源(。
现在我的问题是:typedefed结构包含许多uint8_t数据类型。不幸的是,我们的MCU不支持uint8_t数据类型,因为最小的类型是16位宽的(所以即使是char也有16位(。
为了正确使用API,结构必须填充通过SPI接收的数据。由于传入的数据是字节包,我们不能直接将这些数据复制到结构中,因为我们的结构对那些8位类型使用16位。因此,我们需要进行许多位移位操作来正确分配接收到的数据。
示例:(制造商typedef struct(
typedef struct NETX_COMMUNICATION_CHANNEL_INFOtag
{
uint8_t bChannelType; //uint16_t in our system
uint8_t bChannelId; //uint16_t in our system
uint8_t bSizePositionOfHandshake; //uint16_t in our system
uint8_t bNumberOfBlocks; //uint16_t in our system
uint32_t ulSizeOfChannel;
uint16_t usCommunicationClass;
uint16_t usProtocolClass;
uint16_t usProtocolConformanceClass;
uint8_t abReserved[2]; //uint16_t in our system
} NETX_COMMUNICATION_CHANNEL_INFO;
有人能想出一个简单的解决办法吗?我真的不想为每个接收到的数据包类型编写单独的位移操作。(性能/时间/空间浪费(
我的想法(使用位字段将2xuint8_t填充到uint16_t或将4xuint8_t填充到uint32_t(
typedef struct NETX_COMMUNICATION_CHANNEL_INFOtag
{
struct packet_uint8{
uint32_t bChannelType :8;
uint32_t bChannelId :8;
uint32_t bSizePositionOfHandshake :8;
uint32_t bNumberOfBlocks :8;
}packet_uint8;
uint32_t ulSizeOfChannel;
uint16_t usCommunicationClass;
uint16_t usProtocolClass;
uint16_t usProtocolConformanceClass;
uint16_t abReserved;
} NETX_COMMUNICATION_CHANNEL_INFO;
现在我不确定这个解决方案是否有效,因为位字段中的位的顺序不一定是源文件中的顺序。(或者是否所有的位字段都有相同的大小?(
我希望我能很好地描述这个问题,让你理解。
谢谢和问候。
您的编译器手册应该描述位字段的布局方式。仔细阅读。还有一种叫做__attribute__((byte_peripheral))
的东西应该有助于在内存映射设备中合理地打包位字段。
如果您不确定位字段,只需对这些字段使用uint16_t
和带有位偏移的访问宏,例如
#define FIRST(x) ((x) >> 8)
#define SECOND(x) ((x) & 0xFF)
...
uint16_t channel_type_and_id;
...
int channel_type = FIRST(x->channel_type_and_id);
int channel_id = SECOND(x->channel_type_and_id);
然后你只需要确定平台的字节顺序。如果您需要更改MCU似乎支持的字节序?你可以重新定义这些宏。
位字段很可能仍然以位偏移的方式实现,因此不会有太多节省-如果寄存器有字节访问函数,那么编译器将知道如何优化x & 0xff
以使用它们
根据链接到编译器文档的字节访问是通过内部
要以8位为增量访问数据,请使用__第节中描述的byte((和__mov_byte((内部函数7.5.6.
如果你愿意,你可以制作一个新的类型来封装字节应该如何访问——比如一对字节或一个大小为16位的TwoByte类。
为了获得灵感,我们来看看std::bitset
模板类是如何在STL中实现的。https://en.cppreference.com/w/cpp/utility/bitset
正如我在另一个答案中发布的那样,我仍然相信你的比特字段可以工作——尽管它可能是特定于平台的。基本上,如果它成功了,编译器应该进行正确的位移位操作。
比特字段方法在实践中可能有效。尽管您确实需要一些方法来验证或确保它以适合您的目标平台的正确方式打包。位字段方法将不可移植,因为您声明位字段的顺序取决于平台。