如何以可读的方式使用C初始化寄存器中的位

时间:2015-04-08 08:20:20

标签: c bit-manipulation

我有一个包含许多字段的24位寄存器。例如,3个高位是"模式",最低10位是"数据速率除数"等等。现在,我可以找出这个24位的内容并将其编码为单个十六进制数0xNNNNNN。但是,对于任何试图维护它的人来说,这都是不可读的。

问题是,如果我分别定义每个子字段的最佳编码方式是什么?

2 个答案:

答案 0 :(得分:2)

经典方法是在常量值上使用<<左移运算符,并将所有值与+|结合使用。例如:

*register_address = (SYNC_MODE << 21) | ... | DEFAULT_RATE;

答案 1 :(得分:1)

解决方案1 ​​

&#34;标准&#34;解决此问题的方法是使用structbitfield成员。像这样:

typedef struct {
    int divisor: 10;
    unsigned int field1: 9;
    char field2: 2;
    unsigned char mode: 3;
} fields;

每个字段名称后面的数字指定该成员使用的位数。在上面的示例中,字段divisor使用10位,可以存储-512511之间的值(有符号整数),而mode可以存储3位无符号值:{ {1}}和0

每个字段的值范围使用有关有符号/无符号的常规规则,但字段长度(char / int / long)限制为指定的位数。当然,7仍然可以容纳8位,char最多16个a.s.o.强制规则是字段类型的通常规则,考虑到它们的大小(即在short中存储-5会将其转换为mode(并且实际值可能是unsigned)。

您需要注意几个问题(其中一些问题也在文档页面的注释部分中提及bit fields

  • 结构中声明的总位数必须为24(寄存器的大小);
  • 因为你的结构使用3个字节,所以这些结构的数组中的某些位置可能会出现奇怪的行为,因为它们跨越分配单元大小(通常为4或8个字节,具体取决于硬件); < / LI>
  • 标准不保证分配单元中位字段的顺序;根据体系结构,在最后的3字节包中,字段3可能包含最高有效3位或最低有效3位;不过,你可以轻松地解决这个问题。

您可能需要同时处理存储在mode结构中的值。为此,您可以将结构嵌入到联合中:

fields

解决方案2

另一种做(同类)事情的方法是使用宏来提取字段并组成整个寄存器:

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) modefield2field1值组合成3个字节的整数。另一组宏提取各个字段的值。所有这些都假定处理过的数字是无符号的。

利弊

divisor(嵌入struct)解决方案:

  • union它允许编译器对要放入字段的值进行一些检查(并发出警告);此外,它在签名和未签名之间进行了正确的转换;

宏观解决方案:

  • [+]对记忆对齐问题不明智,你把这些位准确地放在你想要的位置;
  • [+]它不会检查您在字段中输入的值的范围;
  • (-)使用宏来处理有符号值有点棘手;这里建议的宏只适用于无符号值;为了使用有符号的值,需要更多的移位。