It’s just interesting what is the best practice regarding I²C to C registration cards or rather what other people often use / prefer.
Until that moment, I usually made a lot of definitions, one for each register and one for all bits, masks, shifts, etc. However, lately I have seen some drivers use (possibly packaged) structures instead of specific ones. I think these were Linux kernel modules.
Anyway, they
struct i2c_sensor_fuu_registers { uint8_t id; uint16_t big_register; uint8_t another_register; ... } __attribute__((packed));
They will then use offsetof (or the macro) to get the i2c register, and use sizeof for the number of bytes read.
I believe that both approaches have their advantages:
structural approach:
- (+) The register offsets are all logically contained within the structure instead of writing each register in the definition.
- (+) Input size is explicitly specified using a data type of the appropriate size.
- (-) This does not take into account commonly used bit fields
- (-) This does not take into account register cards that are not mapped to bytes (for example, LM75), where one reads 2 bytes from offset n + 0x00, but n + 0x01 is a different register, not the high / low byte of register n + 0x00
- (-) This does not take into account large spaces in the address space (for example, registers 0x00, 0x01, 0x80, 0xAA, no in-betweens ...) and (I think?) Relies on optimizing the compiler to get rid of the structure.
determine the approach:
- (+) Each of the registers together with its bits is usually defined in a block, which makes it easy to find the correct character and rely on a naming convention.
- (+) Transparent / unaware of spaces in the address space.
- (-) Each of the registers must be individually defined, even if there are no spaces
- (-) Since definitions tend to be global, names are usually very long, clogging up the source code with dozens of long symbol names.
- (-) The sizes of the data being read are usually either encoded magic numbers or (end - beginning + 1), or possibly long symbol names.
- (o) Transparent / unconscious data size and address on the map.
Basically, I'm looking for a smarter way to handle these cases. I often find that I type many, many painfully long symbol names for each register, and each bit and possibly disguises and shifts (the last two depending on the data type), but simply ending up using only a few of them (but hating to redefine the missing characters later, so I print everything in one session). However, I notice that the read / write byte sizes are mostly magic numbers and usually read data and source code side by side to understand even the most basic interaction.
I wonder how other people deal with such situations. I found several examples on the Internet where people also printed every single register, bit, etc. In a large heading, but nothing completely final ... However, none of the two options above seems too smart at the moment :(
source share