C标准没有规定一个位域序列占用多少内存或者位域的顺序。在你的例子中,一些编译器可能决定使用32位用于位域,即使你明确地期望它覆盖16位。因此,使用位字段可以将您锁定到特定的编译器和特定的编译标志。
使用大于的类型
unsigned char
也有实现定义的效果,但在实践中它更便携。在现实世界中,只有两种选择
uintNN_t
:big-endian或little-endian,通常对于给定的CPU,每个人都使用相同的顺序,因为这是CPU本机使用的顺序。 (某些体系结构,如mips和arm支持两种端点,但通常人们会在各种CPU模型中坚持使用一个字节序。)如果您正在访问CPU自己的寄存器,那么它的字节顺序可能也是CPU的一部分。另一方面,如果您正在访问外围设备,则需要注意。
您正在访问的设备的文档将告诉您一次寻址的内存单元有多大(显示示例中为2个字节)以及这些位的排列方式。例如,它可能表明该寄存器是一个16位寄存器,使用16位加载/存储指令访问,无论CPU的字节顺序是什么,
data1
包含5个低位,
data2
包括接下来的3,
data3
接下来的4和
data4
接下来4.在这种情况下,您将寄存器声明为
uint16_t
。
typedef volatile uint16_t data_port_t;
data_port_t *port = GET_DATA_PORT_ADDRESS();
</code>
设备中的内存地址几乎总是需要声明
volatile
,因为编译器在适当的时间读取和写入它们很重要。
要访问寄存器的各部分,请使用位移和位掩码运算符。例如:
#define DATA2_WIDTH 3
define DATA2_OFFSET 5
define DATA2_MAX (((uint16_t)1 << DATA2_WIDTH) - 1) // in binary: 0000000000000111
define DATA2_MASK (DATA2_MAX << DATA2_OFFSET) // in binary: 0000000011100000
void set_data2(data_port_t port, unsigned new_field_value)
{
assert(new_field_value <= DATA2_MAX);
uint16_t old_register_value = port;
// First, mask out the data2 bits from the current register value.
uint16_t new_register_value = (old_register_value & ~DATA2_MASK);
// Then mask in the new value for data2.
new_register_value |= (new_field_value << DATA2_OFFSET);
*port = new_register_value;
}
</code>
显然,你可以缩短代码。我把它分成了各个小步骤,这样逻辑应该很容易理解。我在下面加了一个较短的版本。除非在非优化模式下,任何有价值的编译器都应编译为相同的代码。注意上面,我使用了一个中间变量,而不是做两个赋值
port
因为要做两个作业
port
会改变行为:它会导致设备看到中间值(和另一个读取,因为
|=
既是读又写)。这是较短的版本和读取功能:
void set_data2(data_port_t port, unsigned new_field_value)
{
assert(new_field_value <= DATA2_MAX);
port = (port & ~(((uint16_t)1 << DATA2_WIDTH) - 1) << DATA2_OFFSET))
| (new_field_value << DATA2_OFFSET);
}
unsigned get_data2(data_port port)
{
return (*port >> DATA2_OFFSET) & DATA2_MASK;
}
</code>
#define CAN0 (*(CAN_REG_FILE *)CAN_BASE_ADDRESS)
</code>
</pre>
这里没有功能。函数声明将具有返回类型,后跟括号中的参数列表。这取了价值
CAN_BASE_ADDRESS
,可能是某种类型的指针,然后将指针强制转换为指针
CAN_REG_FILE
,最后取消引用指针。换句话说,它访问由给定地址的CAN寄存器文件
CAN_BASE_ADDRESS
。例如,可能有类似的声明
然后你可以做的事情
CAN0.foo = 42;
printf(“CAN0 status: %d\n”, (int)CAN0.status);
</code>