我正在定义一些寄存器(它们都是 32 位宽)以使用位域与硬件外设进行交互。起初,我通过混合
uint8_t
、uint16_t
和 uint32_t
类型来定义一切。例如:
...
// More definitions above
union {
volatile uint32_t I2C_REVNB_HI;
volatile struct {
uint16_t FUNC : 12;
uint8_t RSVD0 : 2;
uint8_t SCHEME : 2;
uint16_t RSVD1 : 16;
} I2C_REVNB_HI_bits;
}; // 0x04 - 0x08
...
// More definitions below
当位域为
bits <= 8
时,我将其分配给 uint8_t
,同样,当 8 < bits <= 16
时,我将其分配给 uint16_t
并将 16 < bits <= 32
分配给 uint32_t
。然而,这对我来说有点混乱,我将所有内容重构为uint32_t
,无论位宽如何,因为我认为这并不重要:
...
// More definitions above
union {
volatile uint32_t I2C_REVNB_HI;
volatile struct {
uint32_t FUNC : 12;
uint32_t RSVD0 : 2;
uint32_t SCHEME : 2;
uint32_t RSVD1 : 16;
} I2C_REVNB_HI_bits;
}; // 0x04 - 0x08
...
// More definitions below
我尝试使用寄存器定义的两种实现来运行我的代码,但是一切都是
uint32_t
的那个似乎不起作用;外围设备的行为不符合预期。所以我的问题是,这两种实现之间是否有什么不同?我认为不会有什么区别,因为所有位字段的长度与以前相同。我在网上找不到任何关于此的信息。
PS。我正在编写的代码是在AM335X处理器的可编程实时单元之一上运行,以与I2C外设连接,并使用clpru编译器进行编译。
clpru 编译器文档中有关位域 (p108) 的一些注释可能有用,也可能没用:
struct st {int a:4};
struct st {char a:4; int :22;};
编译器手册(第 69 页):
“除了 _Bool、signed int 和 unsigned int 之外,编译器还允许 char、signed char、unsigned char、 有符号短、无符号长、有符号长、无符号长、有符号长长、无符号长长,以及 枚举类型作为位字段类型。”
更新
我尝试检查是否按照@IanAbbot的建议分配给字段有任何差异:
i2c_refactored.I2C_REVNB_HI = 0;
i2c_refactored.I2C_REVNB_HI_bits.SCHEME = ~0;
i2c_refactored.I2C_REVNB_HI_bits.FUNC = ~0;
DEBUG_MEMORY_0.status = i2c_refactored.I2C_REVNB_HI;
i2c_original.I2C_REVNB_HI = 0;
i2c_original.I2C_REVNB_HI_bits.SCHEME = ~0;
i2c_original.I2C_REVNB_HI_bits.FUNC = ~0;
DEBUG_MEMORY_1.status = i2c_original.I2C_REVNB;
但是输出是相同的:
0x0000CFFF
(或0b00000000000000001100111111111111
)
C 标准没有指定实现如何在存储单元中布置位字段。 C 2018 6.7.2.1 11 说(添加了强调):
实现可以分配足够大的任何可寻址存储单元来容纳位字段......
因此,C 编译器可以自由地为 2 位位字段分配 1、2 个或更多字节,也可以自由地为 12 位位字段分配两个或更多字节(假设为 8 位字节),等等。它可以根据位字段的标称类型做出决定。
关于标准对选择存储单元施加的唯一约束,也在 6.7.2.1 11 中:
…如果剩余足够的空间,结构中紧随另一个位字段的位字段应被打包到同一单元的相邻位中…