我有一个包含许多字段的24位寄存器。例如,3个高位是"模式",最低10位是"数据速率除数"等等。现在,我可以找出这个24位的内容并将其编码为单个十六进制数0xNNNNNN。但是,对于任何试图维护它的人来说,这都是不可读的。
问题是,如果我分别定义每个子字段的最佳编码方式是什么?
答案 0 :(得分:2)
经典方法是在常量值上使用<<
左移运算符,并将所有值与+
或|
结合使用。例如:
*register_address = (SYNC_MODE << 21) | ... | DEFAULT_RATE;
答案 1 :(得分:1)
&#34;标准&#34;解决此问题的方法是使用struct
和bitfield
成员。像这样:
typedef struct {
int divisor: 10;
unsigned int field1: 9;
char field2: 2;
unsigned char mode: 3;
} fields;
每个字段名称后面的数字指定该成员使用的位数。在上面的示例中,字段divisor
使用10位,可以存储-512
和511
之间的值(有符号整数),而mode
可以存储3位无符号值:{ {1}}和0
。
每个字段的值范围使用有关有符号/无符号的常规规则,但字段长度(char / int / long)限制为指定的位数。当然,7
仍然可以容纳8位,char
最多16个a.s.o.强制规则是字段类型的通常规则,考虑到它们的大小(即在short
中存储-5
会将其转换为mode
(并且实际值可能是unsigned
)。
您需要注意几个问题(其中一些问题也在文档页面的注释部分中提及bit fields
:
3
可能包含最高有效3位或最低有效3位;不过,你可以轻松地解决这个问题。您可能需要同时处理存储在mode
结构中的值。为此,您可以将结构嵌入到联合中:
fields
另一种做(同类)事情的方法是使用宏来提取字段并组成整个寄存器:
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);
第一个宏将#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)
,mode
,field2
,field1
值组合成3个字节的整数。另一组宏提取各个字段的值。所有这些都假定处理过的数字是无符号的。
divisor
(嵌入struct
)解决方案:
union
它允许编译器对要放入字段的值进行一些检查(并发出警告);此外,它在签名和未签名之间进行了正确的转换; 宏观解决方案:
[+]
对记忆对齐问题不明智,你把这些位准确地放在你想要的位置; [+]
它不会检查您在字段中输入的值的范围; (-)
使用宏来处理有符号值有点棘手;这里建议的宏只适用于无符号值;为了使用有符号的值,需要更多的移位。