只是想知道在C中使用I²C寄存器映射的最佳做法是什么,或者更确切地说是其他人经常使用/更喜欢的。
到目前为止,我经常做很多定义,一个用于每个寄存器,一个用于所有位,掩码,移位等。 但是,最近我看到一些驱动程序使用(可能是打包的)结构而不是定义。我认为这些是Linux内核模块。
无论如何,他们会
struct i2c_sensor_fuu_registers {
uint8_t id;
uint16_t big_register;
uint8_t another_register;
...
} __attribute__((packed));
然后他们使用offsetof(或宏)来获取i2c寄存器,并使用sizeof作为要读取的字节数。
我发现这两种方法都有其优点:
结构方法:
定义方法:
基本上,我正在寻找一种更智能的方法来处理这些情况。我经常发现自己为每个寄存器和每个位输入了许多令人痛苦的长符号名称,并且可能还有掩码和移位(后两种取决于数据类型),最后只使用其中几个(但讨厌稍后重新定义缺失的符号,这就是我在一个会话中键入所有内容的原因)。 不过,我注意到读/写字节的大小大多是幻数,通常需要并排读取数据表和源代码才能理解最基本的交互。
我想知道其他人如何应对这种情况?我在网上找到了一些例子,人们也在一个大标题中粗略地输入每一个寄存器,位等等,但没有什么是明确的......但是,上面两个选项中的任何一个看起来都不太明智:(
答案 0 :(得分:2)
警告:此处描述的方法使用位域,其在内存中的排列是特定于实现的。如果这样做,请确保您知道编译器在这方面的工作原理。
正如您所指出的,每种方法都有优点和缺点。我喜欢混合方法。您可以定义寄存器偏移,但是然后使用结构作为内容,使用union来指定位或整个寄存器。在联合内部,使用正确的大小变量作为寄存器的大小(正如您所提到的,有时它们不是字节可寻址的)。你不需要那么多的定义,你不太可能弄乱位移并且不需要掩码。例如:
#define unsigned char u8;
#define unsigned short u16;
#define CTL_REG_ADDR 0x1234
typedef union {
struct {
u16 not_used:10; //top 10 bits ununsed
u16 foo_bits:3; //a multibit register
u16 bar_bit:1; //just one bit
u16 baz_bits:2; //2 more bits
} fields;
u16 raw;
} CTL_REG_DATA;
#define STATUS_REG_ADDR 0x58
typedef union {
struct {
u8 bar_bits:4; //upper nibble
u8 baz_bits:4; //lower nibble
} fields;
u8 raw;
} STATUS_REG_DATA;
//use them like the following
u16 readregister(u16);
void writeregister(u16,u16);
CTL_REG_DATA reg;
STATUS_REG_DATA rd;
rd = readregister(STATUS_REG_ADDR);
if (rd.fields.bar_bit) {
reg.raw = 0xffff; //set every bit
reg.fields.bar_bit = 0; //but clear this one bit
writeregister(CTL_REG_ADDR, reg);
}
答案 1 :(得分:1)
在我的理想世界中,硬件设计人员将提供与C ++,C和ASM兼容的头文件。一个是基于实际硬件寄存器自动生成的。通过#defines(对于ASM)和typedef'd结构(对于C和C ++)定义每个寄存器和位/字段的一个。一个表示每个位和字段的访问属性(只读,只写,写清除等)。其中包括定义每个寄存器的用途和用途及其位/字段的注释。它还需要考虑目标字节序和编译器,以确保正确排序任何寄存器和位域。
我在以前的工作中尽可能接近这个理想。我编写了一个脚本来解析寄存器描述文件(我定义的格式)并自动生成一个完整的头(结构和#defines)以及一个函数来转储所有可读的寄存器以进行调试。我在其他公司看到了类似的方法,但没有一种方法能够达到这种程度。
我要指出,如果使用typedef结构来定义寄存器布局,那么您可以轻松地解释定义中的大寄存器间隙。例如只需添加“reserved [80]”或“unused [94]”或“unmplemented [2044]”或“gap [42]”数组元素来定义间隙。无论如何,您总是将结构定义用作指向硬件基址的指针,因此它不会占用内存中任何位置的实际结构大小。
希望有所帮助。