如何以可读的方式使用C初始化寄存器中的位
我有一个24位寄存器,它包含许多字段。例如,上面的3位是“模式”,下面的10位是“数据速率除数”,等等。现在,我可以计算出这24位中的内容,并将其编码为单个十六进制数0xnnnn。然而,对于任何试图维护它的人来说,这是相当难以理解的如何以可读的方式使用C初始化寄存器中的位,c,bit-manipulation,C,Bit Manipulation,我有一个24位寄存器,它包含许多字段。例如,上面的3位是“模式”,下面的10位是“数据速率除数”,等等。现在,我可以计算出这24位中的内容,并将其编码为单个十六进制数0xnnnn。然而,对于任何试图维护它的人来说,这是相当难以理解的 问题是,如果我单独定义每个子字段,那么将其全部编码在一起的最佳方式是什么?经典方式是使用解决方案1 解决此问题的“标准”方法是对成员使用struct。大概是这样的: typedef struct { int divisor: 10; unsigned
问题是,如果我单独定义每个子字段,那么将其全部编码在一起的最佳方式是什么?经典方式是使用
解决方案1
解决此问题的“标准”方法是对成员使用struct
。大概是这样的:
typedef struct {
int divisor: 10;
unsigned int field1: 9;
char field2: 2;
unsigned char mode: 3;
} fields;
每个字段名后面的数字指定该成员使用的位数。在上面的示例中,除数
字段使用10位,可以存储-512
和511
(带符号整数)之间的值,而模式
可以存储3位上的无符号值:介于0
和7
之间
每个字段的值范围使用有关有符号/无符号和的常规规则,但字段长度(char/int/long)限于指定的位数。当然,char
最多可以保存8位,而short
最多可以保存16位。强制规则是字段类型的常用规则,考虑到字段的大小(即,在模式下存储-5
会将其转换为无符号
(实际值可能是3
)
您需要注意几个问题(其中一些问题也在文档页面的注释部分提到,关于:
- 结构中声明的位总量必须为24(寄存器的大小)
- 由于您的结构使用3个字节,因此此类结构数组中的某些位置可能会表现出奇怪的行为,因为它们跨越分配单元大小(通常为4或8个字节,具体取决于硬件)
- 该标准不保证分配单元中位字段的顺序;根据体系结构的不同,可能在最终的3字节包中,字段
模式
包含最高有效位3位或最低有效位3位;不过,您可以很容易地解决这一问题
您可能需要一次性处理存储在字段结构中的值。为此,您可以将该结构嵌入到联合中:
typedef union {
fields f;
unsigned int a;
} reg;
reg x;
/* Access individual fields */
x.f.mode = 2;
x.f.divisor = 42;
/* Get the entire register */
printf("%06X\n", x.a);
解决方案2
做同样事情的另一种方法是使用宏来提取字段并组成整个寄存器:
#define MAKE_REG(mode, field2, field1, divisor) \
((((mode) & 0x07) << 21) | \
(((field2) & 0x03) << 19) | \
(((field1) & 0x01FF) << 10 )| \
((divisor) & 0x03FF))
#define GET_MODE(reg) (((reg) & 0xE00000) >> 21)
#define GET_FIELD2(reg) (((reg) & 0x180000) >> 19)
#define GET_FIELD1(reg) (((reg) & 0x07FC00) >> 10)
#define GET_DIVISOR(reg) ((reg) & 0x0003FF)
#定义MAKE_REG(模式、字段2、字段1、除数)\
((模式)和0x07)>19)
#定义GET_字段1(reg)((reg)和0x07FC00)>>10)
#定义GET_除数(reg)((reg)&0x0003FF)
第一个宏将模式
,字段2
,字段1
,除数
值组合成一个3字节的整数。另一组宏提取各个字段的值。所有这些宏都假定处理后的数字是无符号的
利弊
struct
(嵌入union
)解决方案:
[+]
它允许编译器对要输入字段的值进行一些检查(并发出警告);此外,它还可以在有符号和无符号之间进行正确的转换
宏观解决方案:
[+]
内存对齐问题是不明智的,您将位放在您想要的位置
(-)
它不会检查您在字段中输入的值的范围
(-)
使用宏处理有符号值有点棘手;此处建议的宏仅适用于无符号值;使用有符号值需要更多移位
您可以使用模式3除数1的{3,0,0,1}将其初始化为若干整数
#define MAKE_REG(mode, field2, field1, divisor) \
((((mode) & 0x07) << 21) | \
(((field2) & 0x03) << 19) | \
(((field1) & 0x01FF) << 10 )| \
((divisor) & 0x03FF))
#define GET_MODE(reg) (((reg) & 0xE00000) >> 21)
#define GET_FIELD2(reg) (((reg) & 0x180000) >> 19)
#define GET_FIELD1(reg) (((reg) & 0x07FC00) >> 10)
#define GET_DIVISOR(reg) ((reg) & 0x0003FF)