只是想知道关于I 2 C寄存器地图的最佳做法是什么,或者更确切地说,其他人经常/喜欢使用什么。
到目前为止,我通常已经做了很多定义,一个针对每个寄存器,一个针对所有比特、掩码、移位等。然而,最近我看到一些驱动程序使用(可能是打包)结构而不是定义的。我认为这些是Linux内核模块。
不管怎样,他们会
struct i2c_sensor_fuu_registers {
uint8_t id;
uint16_t big_register;
uint8_t another_register;
...
} __attribute__((packed));
然后,他们将使用偏移(或宏)来获取i2c寄存器,并对要读取的字节数使用大号。
我认为这两种方法都有其优点:
结构方法:
界定方法:
基本上,我在寻找一种更明智的方法来处理这些案件。我经常发现自己为每个寄存器和每个位输入了很多令人痛苦的长符号名,可能还有掩码和移位(后两个取决于数据类型),最后只使用其中的几个符号(但讨厌稍后重新定义丢失的符号,这就是为什么我在一个会话中输入所有符号)。不过,我注意到读/写字节的大小大多是神奇的数字,通常需要并行读取数据表和源代码才能理解甚至最基本的交互。
我想知道其他人是如何处理这种情况的?我在网上找到了一些例子,人们也在一个大标题中艰难地输入了每一个寄存器、位等等,但是没有什么非常明确的.然而,上述两种选择在这一点上似乎都不太明智:
发布于 2012-11-20 16:22:48
警告:这里描述的方法使用位字段,其在内存中的排列是特定于实现的。如果这样做,请确保您知道编译器在这方面的工作方式。
正如您所指出的,每种方法都有其优点和缺点。我喜欢混合的方法。您可以定义寄存器偏移量,但随后使用结构表示内容,并使用联合指定位或整个寄存器。在联合内部,使用正确的大小变量来表示寄存器的大小(正如您所提到的,有时它们不是字节可寻址的)。你不需要那么多的定义,你不太可能搞砸位转移,也不需要面具。例如:
#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);
}
https://stackoverflow.com/questions/13475828
复制相似问题