在没有 uint8_t 数据类型的 MCU 上具有 uint8_t 的结构

structs with uint8_t on a MCU without uint8_t datatype

我是一名嵌入式软件开发人员,我想连接到外部设备。该设备通过 SPI 发送数据。该数据的结构由外部设备制造商预定义,无法编辑。制造商正在提供一些头文件,其中包含通过 SPI 发送的所有数据的许多类型定义。 制造商还提供了一个 API 来以正确的方式处理接收到的数据包(我可以访问那个 API 的来源)。

现在解决我的问题: typedefed 结构包含许多 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;

有人能想出解决这个问题的简单方法吗? 我真的不想为每个接收到的数据包类型编写一个单独的移位操作。 (performance/time/space-waste)

我的想法 (使用位域将 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 以使用它们

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

To access data in increments of 8 bits, use the __byte() and __mov_byte() intrinsics described in Section 7.5.6.

如果你愿意,你可以创建一个新类型来封装应该如何访问字节 - 类似于一对字节或一个 TwoByte class,其大小为 16 位。

为了获得灵感,请查看如何在 STL 中针对模拟问题实施 std::bitset 模板 class。 https://en.cppreference.com/w/cpp/utility/bitset

正如我在其他答案中发布的那样,我仍然相信您的位域 可以 工作 - 尽管它可能是特定于平台的。基本上如果它成功了,编译器应该放入正确的移位操作。