gpt4 book ai didi

c - 最佳常见做法 I2C 寄存器映射

转载 作者:太空狗 更新时间:2023-10-29 15:36:21 24 4
gpt4 key购买 nike

只是想知道关于 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 获取要读取的字节数。

我发现这两种方法各有优点:

结构方法:

  • (+) 寄存器偏移量在逻辑上都包含在一个结构中,而不必在定义中拼出每个寄存器。
  • (+) 使用适当大小的数据类型明确说明条目大小。
  • (-) 这不考虑广泛使用的位字段
  • (-) 这不考虑未字节映射的寄存器映射(例如 LM75),其中从偏移量 n+0x00 读取 2 个字节,而 n+0x01 是另一个寄存器,而不是高/低字节寄存器 n+0x00
  • (-) 这并没有考虑地址空间中的大差距(例如在 0x00、0x01、0x80、0xAA 处的寄存器,没有中间...)并且(我认为?)依赖于编译器优化来摆脱结构的。

定义方法:

  • (+) 每个寄存器及其位通常在一个 block 中定义,这使得找到正确的符号变得容易并且依赖于命名约定。
  • (+) 透明/不知道地址空间差距。
  • (-) 每个寄存器都必须单独定义,即使没有间隙也是如此
  • (-) 因为定义往往是全局的,所以名称通常很长,源代码中有些乱七八糟的符号名称。
  • (-) 要读取的数据大小通常是硬编码的魔数(Magic Number)或 (end - start + 1) 风格的计算,可能有很长的符号名称。
  • (o) 透明/不知道 map 中的数据大小与地址。

基本上,我正在寻找一种更智能的方法来处理这些情况。我经常发现自己为每个寄存器和每个位输入很多长得令人痛苦的符号名称,可能还有掩码和移位(后两者取决于数据类型),只是最终只使用了其中的几个(但讨厌稍后重新定义丢失的符号,这就是为什么我在一个 session 中输入所有符号的原因)。不过,我注意到要读/写的字节大小大多是魔数(Magic Number),通常需要并排阅读数据表和源代码才能理解即使是最基本的交互。

我想知道其他人是如何处理这些情况的?我在网上找到了一些例子,人们还在一个大标题中费力地输入了每个寄存器、位等,但没有什么是确定的……然而,上面的两个选项在这一点上似乎都不太聪明:(

最佳答案

警告:此处描述的方法使用位域,其在内存中的排列是特定于实现的。如果你这样做,请确保你知道你的编译器在这方面是如何工作的。

正如您所指出的,每种方法都有优点和缺点。我喜欢混合方法。您可以定义寄存器偏移量,然后使用结构来表示内容,并使用 union 来指定位或整个寄存器。在 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);
}

关于c - 最佳常见做法 I2C 寄存器映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13475828/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com