Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This choice is definitely one that you will see both ways in embedded, and is arguable the most debatable one here. In the end, we this choice was made because:

...

Code Block
typedef void (*I2C_WriteFuncPtr)(int device_addr, int reg_addr, int data);
typedef int (*I2C_ReadFuncPtr)(int device_addr, int reg_addr);

typedef struct {
I2C_WriteFuncPtr local_I2C_Write;
I2C_ReadFuncPtr local_I2C_Read;
} lsm6dso_t;

lsm6dso_t imu; //DEFINE OBJECT HERE

int lsmdso_read_reg(uint8_t reg)
{
  return local_I2C_Read(...);
}


(main.c)

extern
lsm6dso_t imu;
// FIND DEFINITION FROM DRIVER
imu = {.I2C_WriteFuncPtr = HAL_i2c_MemWrite, .I2C_ReadFuncPtr = HAL_i2c_MemRead};

...

Also note the method of updating these values does utilize extern. For devices where more than one of these devices may be present on the bus, have the driver and its APIs have a device select parameter to dynamically update to a different device, do not make multiple instances of the device struct. In the above example, you may notice the first parameter is “device address”. This would allow you to have multiple lsm6dso’s on the bus (as they’d have the different i2c addresses), but they both use the same, local lsm6dso object, which works because this object only contains function pointers to i2c read and write, which will be the same no matter which instance of the hardware you are refencing.

Info

Note that this requires function signatures that match those of the function pointers defined in the driver. Make these as general as possible, and keep in mind wrappers may need to be made around the HAL in use to ensure their signature is the same