在没有uint8_t数据类型的MCU上使用uint8_t进行结构

时间:2018-11-08 08:33:56

标签: c embedded uint8t

我是嵌入式软件开发人员,并且想与外部设备连接。该设备通过SPI发送数据。该数据的结构是由外部设备制造商预先定义的,无法进行编辑。制造商正在为某些Header文件提供所有通过SPI发送的所有数据的typedef。 制造商还提供了API以正确的方式处理接收到的数据包(我可以访问该API的源)。

由于我的问题: 类型定义的结构包含许多uint8_t数据类型。不幸的是,我们的MCU不支持uint8_t数据类型,因为最小的类型是16位宽(因此,即使一个char也具有16位)。

要正确使用API​​,必须在结构中填充通过SPI接收的数据。由于传入的数据是字节数据包,因此我们不能仅将这些数据复制到结构中,因为对于这些8位类型,我们的结构使用16位。 结果,我们需要做很多移位操作才能正确分配接收到的数据。

示例:(制造商的typedef结构)

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;

现在,我不确定该解决方案是否会起作用,因为位域内的位顺序不一定是源文件中的顺序。 (或者是否所有位域的大小都相同?)

希望我对问题的描述足够好,让您理解。

感谢和问候。

3 个答案:

答案 0 :(得分:5)

您的编译器手册应该描述位字段的布局方式。请仔细阅读。还有一种叫做__attribute__((byte_peripheral))的东西也应该有助于将位域合理地打包在内存映射的设备中。


如果不确定 bitfields ,只需将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似乎支持的字节序?您可以重新定义这些宏。


位域很可能仍将根据位移位来实现,因此不会有太多的节省-如果寄存器中有 个字节访问函数,则编译器将知道优化{ {1}}来使用它们

答案 1 :(得分:0)

根据链接到编译器文档的字节访问是通过内部函数

  

要以8位为增量访问数据,请使用   本节中介绍的__byte()和__mov_byte()内部函数   7.5.6。

如果愿意,可以创建一个新类型来封装应该如何访问字节的方式-例如一对字节或TwoByte类,大小为16位。

为启发起见,请看一下如何在STL中针对类似问题实现std::bitset模板类。 official docs

正如我在其他答案中所述,我仍然相信您的位域可以起作用-即使它可能是特定于平台的。基本上,如果可以解决,编译器应该进行正确的移位操作。

答案 2 :(得分:-4)

位域方法可能在实践中可行。尽管您确实需要某种方法来验证或确保针对目标平台以正确的方式打包了它。当您声明自己的位域顺序取决于平台时,该位域方法将无法移植。