Attitude Estimation· Live
Home / Chapter 2 / 2.4

One library, any board

Lesson 2.4 · Theory · free

Video coming soon

The driver talks to the IMU only through three small board functions: read, write and delay. So the same two driver files run on the STM32 and on the ESP32, and only those three functions change.

In this lesson · 6 steps
  1. The problem: every board talks I2C its own way
  2. The idea: the layers
  3. The three board functions
  4. imu_t: the link between driver and board
  5. The files
  6. One read, twelve bytes

1The problem: every board talks I2C its own way

  1. The sensor is always the same: same address, same registers, same bytes.
  2. STM32: HAL_I2C_Mem_Read(&hi2c1, address << 1, reg, …)
  3. ESP32 (Arduino): Wire.beginTransmission, Wire.write, Wire.requestFrom, Wire.read
  4. Our code from 2.3 calls HAL directly, so it only runs on the STM32.

2The idea: the layers

  1. Your code calls only ism330dhcx_init and ism330dhcx_read.
  2. The driver knows the registers. It never calls HAL or Wire, only the three functions in imu_t.
  3. The board functions are the only board code. You write them once per board: 2.6 (STM32), 2.6b (ESP32).
Why?
Function pointer: a variable that holds a function. The driver calls imu->read(…), and whatever function you put there runs: stm32_read or esp32_read.

3The three board functions

  1. read(address, reg, data, length): reads length bytes, starting at register reg.
    STM32: HAL_I2C_Mem_Read · ESP32: Wire.write(reg), endTransmission(false), requestFrom
  2. write(address, reg, value): puts one value into register reg.
    STM32: HAL_I2C_Mem_Write · ESP32: Wire.write(reg), Wire.write(value), endTransmission()
  3. delay(ms): waits ms milliseconds, for example after the reset.
    STM32: HAL_Delay · ESP32: delay
Why?
Return values: the board functions return 0 when they worked. The driver functions (ism330dhcx_init, ism330dhcx_read) return true when they worked.

4imu_t: the link between driver and board

ism330dhcx.h · the top block (already in the file from 2.5: read it, don't paste it)

Three function types and one struct: the same block is in every driver header.

/* the three functions your board provides (return 0 = OK) */
typedef int  (*imu_read_fn) (uint8_t address, uint8_t reg, uint8_t *data, uint16_t length);
typedef int  (*imu_write_fn)(uint8_t address, uint8_t reg, uint8_t value);
typedef void (*imu_delay_fn)(uint32_t ms);

/* one sensor: its I2C address + the three board functions */
typedef struct {
  uint8_t      address;   /* 7-bit I2C address */
  imu_read_fn  read;
  imu_write_fn write;
  imu_delay_fn delay;
} imu_t;
Why?
address is the 7-bit I2C address (0x6A or 0x6B). Your board fills the struct once (2.6, 2.6b) and passes &imu to every driver call.

5The files

  1. ism330dhcx.h: the imu_t block, the address, the rate and range settings, and the two driver functions.
  2. ism330dhcx.c: the registers, ism330dhcx_init and ism330dhcx_read. No HAL, no Wire.
  3. main.c (STM32) or ch2_imu.ino (ESP32): your three board functions and the imu struct.
  4. Other IMU? Open only your two files (in 2.5 and 2.6b): lsm6dsox, mpu6050, mpu9250, icm20948 or icm42688 (.h + .c). Same imu_t, same board functions.
Why?
ESP32: the sketch is C++, the driver is C. The extern "C" lines in the header let them link without changes.

6One read, twelve bytes

  1. ism330dhcx_read calls imu->read(address, 0x22, b, 12): start at OUTX_L_G (0x22), read 12 bytes.
  2. Order: gx, gy, gz, then ax, ay, az. Each value is two bytes, low byte first.
  3. (int16_t)(b[1] << 8 | b[0]) joins two bytes into one signed number.
Why?
Why one read? All six values come from the same moment, and one transfer is faster than twelve. The sensor moves to the next register by itself: ism330dhcx_init turns that on.

Next: Lesson 2.5 adds the driver files to the STM32 project.