Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ISM330DHCX

Platform-agnostic C driver for the ST ISM330DHCX — a 6-axis industrial-grade IMU (3-axis accelerometer + 3-axis gyroscope + on-chip temperature sensor) with FIFO, interrupt sources, machine-learning core (MLC), finite state machine (FSM), sensor-hub master, OIS mode, and dual-bus (SPI/I²C) digital interface.

The driver is bus-agnostic: register access goes through two callbacks, readReg and writeReg, that the application wraps around either SPI mode 0/3 (≤ 10 MHz) or I²C Fast-mode+ (≤ 1 MHz).

At a glance

Channels 3-axis accel + 3-axis gyro + temp sensor
Accel ranges ±2 g · ±4 g · ±8 g · ±16 g
Gyro ranges ±125 · ±250 · ±500 · ±1000 · ±2000 dps
ODR (both) 12.5 Hz → 6.667 kHz (accel also has 1.6 Hz low-power)
FIFO 9 kB total, tagged-frame samples, configurable batch rates
Bus SPI (mode 0/3, ≤ 10 MHz) or I²C (≤ 1 MHz)
WHO_AM_I 0x6B
Package LGA-14, 2.5×3 mm

Activity-detection blocks (tap, free-fall, wake-up, 6D/4D, MLC, FSM, sensor-hub, OIS, pedometer) exist on the chip but are not wrapped by this driver — see Coverage scope.

Repository layout

ISM330DHCx/
├── include/
│   ├── ism330dhcx.h                Public API
│   ├── ism330dhcx_types.h          HAL, error codes, config + decoded structs, all enums
│   └── ism330dhcx_registers.h      Register addresses + bit shifts/masks
├── src/
│   └── ism330dhcx.c                Implementation
├── example/
│   └── main.c                      ESP-IDF I²C polling example
├── test/
│   └── system/
│       └── main.c                  Hardware-in-the-loop system test (I²C)
├── docs/
│   ├── ism330dhcx_datasheet.pdf
│   └── ism330dhcx_notes.md         Datasheet extract: register-bank model, FIFO tags, self-test thresholds
├── CMakeLists.txt                  Tri-mode (ESP-IDF / Zephyr / plain CMake)
├── Justfile                        format / format_check / build
├── .clang-format                   Tabs, Allman, no col limit
├── LICENSE
└── README.md

Public API summary

Everything is namespaced under ism330dhcx*. Returns ISM330DHCXError. Caller allocates the ISM330DHCXDevice and provides the HAL.

Lifecycle

ISM330DHCXError ism330dhcxInit(ISM330DHCXDevice* dev, const ISM330DHCXConfig* config, const ISM330DHCXHAL* hal);
ISM330DHCXError ism330dhcxDeinit(ISM330DHCXDevice* dev);
ISM330DHCXError ism330dhcxReset(ISM330DHCXDevice* dev);
ISM330DHCXError ism330dhcxResetHardware(ISM330DHCXDevice* dev);    /* requires hal.resetSet */
ISM330DHCXError ism330dhcxReloadCalibration(ISM330DHCXDevice* dev);
ISM330DHCXError ism330dhcxGetWHOAMI(const ISM330DHCXDevice* dev, uint8_t* who_am_i);

Accelerometer / gyroscope / temperature / timestamp

ISM330DHCXError ism330dhcxSetAccelConfig(ISM330DHCXDevice* dev, const ISM330DHCXAccelConfig* config);
ISM330DHCXError ism330dhcxSetAccelODR(ISM330DHCXDevice* dev, ISM330DHCXAccelODR odr);
ISM330DHCXError ism330dhcxSetAccelFullScale(ISM330DHCXDevice* dev, ISM330DHCXAccelFullScale fs);
ISM330DHCXError ism330dhcxReadAccelRaw(const ISM330DHCXDevice* dev, ISM330DHCXAxesRaw* out);
ISM330DHCXError ism330dhcxReadAccelMilliG(const ISM330DHCXDevice* dev, ISM330DHCXAxesMilli* out);

ISM330DHCXError ism330dhcxSetGyroConfig(ISM330DHCXDevice* dev, const ISM330DHCXGyroConfig* config);
ISM330DHCXError ism330dhcxSetGyroODR(ISM330DHCXDevice* dev, ISM330DHCXGyroODR odr);
ISM330DHCXError ism330dhcxSetGyroFullScale(ISM330DHCXDevice* dev, ISM330DHCXGyroFullScale fs);
ISM330DHCXError ism330dhcxReadGyroRaw(const ISM330DHCXDevice* dev, ISM330DHCXAxesRaw* out);
ISM330DHCXError ism330dhcxReadGyroMilliDPS(const ISM330DHCXDevice* dev, ISM330DHCXAxesMilli* out);

ISM330DHCXError ism330dhcxReadTempRaw(const ISM330DHCXDevice* dev, int16_t* raw);
ISM330DHCXError ism330dhcxReadTempMilliCelsius(const ISM330DHCXDevice* dev, int32_t* milli_celsius);
ISM330DHCXError ism330dhcxReadTimestamp(const ISM330DHCXDevice* dev, uint32_t* ticks);

Status, FIFO, interrupts, self-test

ISM330DHCXError ism330dhcxGetStatus(const ISM330DHCXDevice* dev, ISM330DHCXStatus* status);
ISM330DHCXError ism330dhcxGetInterruptSources(const ISM330DHCXDevice* dev, ISM330DHCXInterruptSources* sources);

ISM330DHCXError ism330dhcxSetFIFOConfig(ISM330DHCXDevice* dev, const ISM330DHCXFIFOConfig* config);
ISM330DHCXError ism330dhcxGetFIFOStatus(const ISM330DHCXDevice* dev, ISM330DHCXFIFOStatus* status);
ISM330DHCXError ism330dhcxFIFOFlush(ISM330DHCXDevice* dev);
ISM330DHCXError ism330dhcxReadFIFOSample(const ISM330DHCXDevice* dev, ISM330DHCXFIFOTag* tag, uint8_t data[6]);

ISM330DHCXError ism330dhcxSetINT1Config(ISM330DHCXDevice* dev, const ISM330DHCXInterruptPinConfig* config);
ISM330DHCXError ism330dhcxSetINT2Config(ISM330DHCXDevice* dev, const ISM330DHCXInterruptPinConfig* config);
ISM330DHCXError ism330dhcxReadINT1(const ISM330DHCXDevice* dev, bool* level);   /* requires hal.int1Get */
ISM330DHCXError ism330dhcxReadINT2(const ISM330DHCXDevice* dev, bool* level);

ISM330DHCXError ism330dhcxAccelSelfTest(ISM330DHCXDevice* dev);                 /* requires hal.delayMs */
ISM330DHCXError ism330dhcxGyroSelfTest(ISM330DHCXDevice* dev);

Coverage scope

The driver is intentionally scoped to raw IMU acquisition: accelerometer + gyroscope + temperature signal chain, FIFO acquisition, INT1/INT2 routing for data-ready, the per-pin polarity/drive controls, factory self-test, timestamping, and reset/identity. Activity-detection blocks (tap, free-fall, wake-up, 6D/4D), pedometer, sensor-hub master, MLC, FSM, and OIS are out of scope for the gravitometer — the chip exposes them, but this driver does not wrap them. If a future product needs them, add public functions then; the architecture won't change.

Wiring the HAL

The driver doesn't care whether you talk to the chip over SPI or I²C — both buses use the same "register address + N data bytes" pattern, and the chip auto-increments the register pointer for multi-byte transfers when CTRL3_C.IF_INC = 1 (the driver sets this at init).

I²C example

static int my_read_reg(uint8_t reg, void* data, uint16_t length)
{
    return i2c_master_transmit_receive(handle, &reg, 1, data, length, timeout_ms);
}
static int my_write_reg(uint8_t reg, const void* data, uint16_t length)
{
    /* Pack [reg | data...] and transmit; max 1-byte register address. */
}

const ISM330DHCXHAL hal = { .readReg = my_read_reg, .writeReg = my_write_reg, .delayMs = my_delay };

SPI example

static int my_read_reg(uint8_t reg, void* data, uint16_t length)
{
    /* Set the read bit (MSB of address) and clock out (1+length) bytes. */
    const uint8_t cmd = reg | 0x80U;
    return spi_xfer(&cmd, 1, data, length);
}

The chip uses 3- or 4-wire SPI in mode 0 or mode 3 (CPOL = CPHA, both edge configurations work). For 3-wire SPI, the driver leaves CTRL3_C.SIM at its default (4-wire) — flip it via a custom register write if needed.

Building

As an ESP-IDF component

Drop the repo into components/ISM330DHCx and ESP-IDF builds it automatically.

As a Zephyr module

list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/components/ISM330DHCx)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

Standalone CMake

add_subdirectory(components/ISM330DHCx)
target_link_libraries(my_app PRIVATE ism330dhcx)

Or build standalone:

just build
just format_check

System testing

test/system/main.c is a hardware-in-the-loop test that covers:

  • NULL-pointer / invalid-arg rejection
  • Use-before-init / use-after-deinit guards
  • Init / deinit / re-init lifecycle and COMM_FAIL propagation
  • WHO_AM_I returns 0x6B
  • Software reset clears the SW_RESET bit
  • Accel ODR/FS round-trip + finite samples
  • Gyro ODR/FS round-trip + finite samples
  • |g| ≈ 1 g on a stationary device
  • Temperature reads in operating range
  • Timestamp counter monotonic
  • FIFO config/status/flush
  • INT routing config writes accepted
  • Accelerometer + gyroscope factory self-test

Conformance

  • C11 source; C99 compatible
  • Style: Crab Labs C Style Guide — tabs (4-wide), Allman, camelCase functions, PascalCase types, ALL_CAPS macros/enums, snake_case variables/struct members, units in identifiers
  • Architecture: Crab Labs Driver Development Guide — HAL via function pointers, caller-allocated device handle, layered internal helpers, no platform headers, no logging, no asserts, no dynamic memory

License

BSD 3-Clause.

About

Driver for the ISM330DHCx IMU

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages