Skip to content

Add ICM40609D IMU driver - #11765

Open
sensei-hacker wants to merge 2 commits into
iNavFlight:maintenance-10.xfrom
sensei-hacker:add-icm40609d-imu-driver
Open

Add ICM40609D IMU driver#11765
sensei-hacker wants to merge 2 commits into
iNavFlight:maintenance-10.xfrom
sensei-hacker:add-icm40609d-imu-driver

Conversation

@sensei-hacker

Copy link
Copy Markdown
Member

Summary

Adds a gyro/accel driver for the TDK ICM-40609-D, modeled structurally on the existing accgyro_icm42605.c driver, and wires it into sensor autodetect.

Changes

  • New drivers/accgyro/accgyro_icm40609d.c / .h
  • New DEVHW_ICM40609D bus enum, centralized ICM40609D_WHO_AM_I_CONST in accgyro_mpu.h
  • Wired into sensors/gyro.c / sensors/acceleration.c autodetect dispatch, gyro.h / acceleration.h enums
  • fc/settings.yaml acc_hardware table and fc/cli.c gyroNames[] updated (both order-synced with the enums)
  • CMakeLists.txt source list

All register addresses, the WHO_AM_I value (0x75 / 0x3B), power-mode bits, ODR/full-scale-range encodings, and the soft-reset sequence were verified directly against the TDK ICM-40609-D datasheet (DS-000272 rev 0.8), not copied verbatim from Betaflight's driver. Betaflight's reference implementation (betaflight/betaflight#14367) had two issues that are not carried into this driver:

  • It originally reset via the wrong register (0x4C instead of 0x11), fixed upstream in Fix ICM40609D reset register betaflight/betaflight#14415 — confirmed here against the datasheet directly.
  • Its ACCEL_FS_SEL=0 macro is labeled +-16g, but the datasheet specifies ACCEL_FS_SEL=0 is +-32g (FS_SEL=1 is +-16g) — this driver uses FS_SEL=1 for +-16g/2048 LSB/g.

This chip's Bank 0 register map also does not include an INT_CONFIG1 (0x64) register — confirmed absent from the datasheet, register map goes INT_CONFIG0 (0x63) straight to INT_SOURCE0 (0x65) — so the ICM42605-style "clear ASYNC_RESET" erratum step is intentionally omitted (see code comment).

Gyro sample rate is selected via a rate-config table (mirroring ICM42605's) driven by gyro->requestedSampleIntervalUs, rather than a fixed rate, so it doesn't cap the PID loop rate below what's configured.

Recommended test target

No shipping target currently defines USE_IMU_ICM40609D. #11701 "Add ORBITH743v2 target" (open) is the natural candidate — that board pairs an ICM42688P with an ICM40609D as its second IMU, and currently registers the ICM40609D under DEVHW_ICM42605 as a stand-in since no dedicated driver existed. Once this PR merges, that target's busdev_icm40609 registration should switch to DEVHW_ICM40609D and enable USE_IMU_ICM40609D, then be flashed/tested on real ORBITH743v2 hardware.

Testing

  • SITL build: passes (driver compiles to nothing under SITL, which only defines USE_IMU_FAKE; confirms the enum/CMake/settings wiring doesn't break the build)
  • Hardware target build: AETH743Basic, with USE_IMU_ICM40609D temporarily scaffolded in to exercise the driver's actual code path — compiles and links cleanly, no warnings. That scaffold edit was reverted before this PR; no shipping target's target.h is changed here.
  • Not verified on real ICM40609D hardware — no such board was available during development. WHO_AM_I detection and gyro/accel reading correctness on real silicon still need verification, ideally on ORBITH743v2 per above.

Code Review

Reviewed with inav-code-review agent. Two IMPORTANT findings were addressed (hard-coded 1kHz ODR replaced with a proper rate-config table; INT_CONFIG1 omission documented with a code comment) and a minor CMakeLists.txt ordering nit was fixed.

Wire in a gyro/accel driver for the TDK ICM40609D, modeled on the
existing ICM42605 driver. Register map, WHO_AM_I, and scale factors
verified directly against the ICM-40609-D datasheet (DS-000272 rev
0.8) rather than trusted from Betaflight's driver, which mislabels
ACCEL_FS_SEL=0 as +/-16g when the datasheet specifies +/-32g, and
originally reset via the wrong register (0x4C instead of 0x11,
fixed upstream in betaflight/betaflight#14415).
Add a rate-config table (mirroring ICM42605's) so the driver selects
GYRO_CONFIG0/ACCEL_CONFIG0 ODR from gyro->requestedSampleIntervalUs
instead of always running at 1kHz, which would have silently capped
the PID loop rate on any board using this chip. Also document why
the ICM42605-style INT_CONFIG1/ASYNC_RESET clear step is omitted:
that register isn't present in the ICM-40609-D register map.
@sensei-hacker sensei-hacker added this to the 10.0 milestone Aug 6, 2026
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 6, 2026

Copy link
Copy Markdown

PR Summary by Qodo

Add TDK ICM40609D IMU driver and wire into sensor autodetect

✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add new ICM40609D accel/gyro driver with datasheet-verified init, scaling, and reset.
• Integrate ICM40609D into gyro/acc autodetect, enums, CLI names, and settings tables.
• Select ODR from requested gyro sample interval to avoid silently capping loop rate.
Diagram

graph TD
  A["FC config (settings.yaml/cli.c)"] --> B["Sensor enums (gyro.h/acceleration.h)"] --> C["Autodetect dispatch (gyro.c/acceleration.c)"] --> D["ICM40609D driver"] --> E["Bus layer"] --> F["ICM-40609-D chip"]
  G["Build sources (CMakeLists.txt)"] --> D
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Parameterize ICM42605 driver for ICM40609D variant
  • ➕ Reduces duplicated init/read logic across similar TDK/Invensense IMUs
  • ➕ Centralizes common ODR/range/filter handling and bus reset patterns
  • ➖ Risk of over-generalizing two chips with subtle register-map differences
  • ➖ Harder to review/validate initially than a dedicated datasheet-driven driver
2. Introduce a shared 'ICM4x6xx' common layer plus per-chip tables
  • ➕ Keeps per-chip differences explicit (tables/constants) while sharing mechanics
  • ➕ Easier to add future related parts without copy/paste
  • ➖ Higher upfront refactor cost and churn across existing drivers
  • ➖ May be premature until there are multiple drivers needing the abstraction

Recommendation: The dedicated driver approach in this PR is appropriate because the ICM40609D has verified differences (reset register, accel FS encoding, missing INT_CONFIG1/ASYNC_RESET) that are easy to validate in a standalone implementation. Consider a follow-up refactor to extract common ICM4xxx bus/init helpers only after this driver is proven on real hardware and additional similar chips justify the abstraction.

Files changed (11) +308 / -2

Enhancement (8) +304 / -0
accgyro_icm40609d.cImplement ICM40609D accel/gyro driver with ODR selection +256/-0

Implement ICM40609D accel/gyro driver with ODR selection

• Adds a new TDK ICM-40609-D driver implementing device detect/reset via WHO_AM_I, low-noise power mode init, low-latency UI filter config, accel/gyro reads, and temperature conversion. Selects ODR based on requested gyro sample interval using a rate table (mirroring ICM42605) and documents omission of the ICM42605 ASYNC_RESET step because INT_CONFIG1 is absent on this chip.

src/main/drivers/accgyro/accgyro_icm40609d.c

accgyro_icm40609d.hExpose ICM40609D detect entrypoints +21/-0

Expose ICM40609D detect entrypoints

• Declares icm40609dAccDetect() and icm40609dGyroDetect() for use by the sensor autodetect dispatchers.

src/main/drivers/accgyro/accgyro_icm40609d.h

accgyro_mpu.hAdd centralized ICM40609D WHO_AM_I constant +1/-0

Add centralized ICM40609D WHO_AM_I constant

• Defines ICM40609D_WHO_AM_I_CONST (0x3B) alongside other IMU identity constants for consistent detect logic.

src/main/drivers/accgyro/accgyro_mpu.h

bus.hAdd DEVHW_ICM40609D bus hardware id +1/-0

Add DEVHW_ICM40609D bus hardware id

• Introduces DEVHW_ICM40609D to allow bus device registration and opening of ICM40609D instances distinctly from other IMUs.

src/main/drivers/bus.h

acceleration.cWire ICM40609D accel detect into autodetect path +13/-0

Wire ICM40609D accel detect into autodetect path

• Includes the new driver header and adds an ACC_ICM40609D case to accDetect(), supporting both explicit selection and autodetect fallthrough behavior.

src/main/sensors/acceleration.c

acceleration.hAdd ACC_ICM40609D enum value +1/-0

Add ACC_ICM40609D enum value

• Introduces ACC_ICM40609D to the accelerationSensor_e enumeration for configuration and dispatch integration.

src/main/sensors/acceleration.h

gyro.cWire ICM40609D gyro detect into autodetect path +10/-0

Wire ICM40609D gyro detect into autodetect path

• Includes the new driver header and adds a GYRO_ICM40609D case to gyroDetect(), allowing the new driver to be selected via autodetect when enabled.

src/main/sensors/gyro.c

gyro.hAdd GYRO_ICM40609D enum value +1/-0

Add GYRO_ICM40609D enum value

• Introduces GYRO_ICM40609D to the gyroSensor_e enumeration to support CLI/config selection and autodetect dispatch.

src/main/sensors/gyro.h

Other (3) +4 / -2
CMakeLists.txtAdd ICM40609D driver sources to build +2/-0

Add ICM40609D driver sources to build

• Registers the new accgyro_icm40609d.c/.h files in the common source list so they are compiled when enabled by target configuration.

src/main/CMakeLists.txt

cli.cExpose ICM40609D in CLI gyro name table +1/-1

Expose ICM40609D in CLI gyro name table

• Adds the ICM40609D string to gyroNames[] to keep CLI-visible names synchronized with the gyroSensor_e enum order.

src/main/fc/cli.c

settings.yamlAdd ICM40609D to acc_hardware settings table +1/-1

Add ICM40609D to acc_hardware settings table

• Extends the acc_hardware values list with ICM40609D, maintaining ordering alignment with accelerationSensor_e.

src/main/fc/settings.yaml

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Enum value compatibility break 🐞 Bug ≡ Correctness
Description
Adding ACC_ICM40609D before ACC_FAKE shifts the numeric value of ACC_FAKE, so configs saved with
acc_hardware=FAKE will deserialize as ACC_ICM40609D after upgrade and attempt to initialize the
wrong sensor driver.
Code

src/main/sensors/acceleration.h[R47-50]

    ACC_ICM45686,
+    ACC_ICM40609D,
    ACC_FAKE,
    ACC_MAX = ACC_FAKE
Evidence
The PR changes the enum ordering and YAML table ordering, and the accelerometer hardware selection
is read from persisted configuration and used directly during init; PG records store raw bytes, so
changing enum numeric values changes how old configs are interpreted.

src/main/sensors/acceleration.h[34-51]
src/main/fc/settings.yaml[1-6]
src/main/sensors/acceleration.c[304-317]
src/main/config/config_eeprom.c[56-72]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ACC_ICM40609D` was inserted before `ACC_FAKE`, which changes the implicit numeric value of `ACC_FAKE`. Because `accelerometerConfig()->acc_hardware` is persisted as a raw PG value, existing saved configs using `ACC_FAKE` will be misinterpreted as `ACC_ICM40609D` after flashing this firmware, causing incorrect sensor init.

### Issue Context
- `accelerometerConfig` is a persisted PG and `acc_hardware` is used directly for detection.
- The YAML table is order-synced with the enum, so the numeric index change is real.

### Fix Focus Areas
- src/main/sensors/acceleration.h[34-51]
- src/main/sensors/acceleration.c[90-101]
- src/main/fc/settings.yaml[1-6]
- src/main/config/config_eeprom.c[56-72]

### Suggested fix approach
- Implement a backward-compatibility migration:
 - Bump the `accelerometerConfig` PG version.
 - On load of older versions, remap the old numeric value that previously meant `ACC_FAKE` to the new `ACC_FAKE` value.
- Alternatively (less preferred if many enums rely on ordering), assign explicit stable numeric values for the enum members and update any loops/range checks accordingly, ensuring `ACC_MAX` reflects the true maximum.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Retry loop off-by-one 🐞 Bug ☼ Reliability
Description
icm40609dDeviceDetect() uses a uint8_t post-decrement do/while which performs 6 WHO_AM_I reads for
attemptsRemaining=5, adding an extra 150ms delay and making retry behavior misleading.
Code

src/main/drivers/accgyro/accgyro_icm40609d.c[R192-195]

+        if (tmp == ICM40609D_WHO_AM_I_CONST) {
+            return true;
+        }
+    } while (attemptsRemaining--);
Evidence
The loop condition uses post-decrement on an unsigned counter in a do/while, which evaluates the old
value and therefore executes once when the counter reaches 0 (6 total iterations for an initial
value of 5).

src/main/drivers/accgyro/accgyro_icm40609d.c[174-198]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The retry loop in `icm40609dDeviceDetect()` runs one extra time due to `do { ... } while (attemptsRemaining--);` with an unsigned counter. This adds an extra delay on failed detection and obscures intent.

### Issue Context
This does not cause an infinite loop, but it *does* perform 6 attempts when initialized to 5.

### Fix Focus Areas
- src/main/drivers/accgyro/accgyro_icm40609d.c[174-198]

### Suggested fix approach
Replace with an explicit bounded loop, e.g.:
```c
for (uint8_t i = 0; i < 5; i++) {
   delay(150);
   if (busRead(dev, MPU_RA_WHO_AM_I, &tmp) && tmp == ICM40609D_WHO_AM_I_CONST) {
       return true;
   }
}
return false;
```
(or keep the existing structure but use `while (attemptsRemaining-- > 0)` and ensure the intended attempt count).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines 47 to 50
ACC_ICM45686,
ACC_ICM40609D,
ACC_FAKE,
ACC_MAX = ACC_FAKE

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Enum value compatibility break 🐞 Bug ≡ Correctness

Adding ACC_ICM40609D before ACC_FAKE shifts the numeric value of ACC_FAKE, so configs saved with
acc_hardware=FAKE will deserialize as ACC_ICM40609D after upgrade and attempt to initialize the
wrong sensor driver.
Agent Prompt
### Issue description
`ACC_ICM40609D` was inserted before `ACC_FAKE`, which changes the implicit numeric value of `ACC_FAKE`. Because `accelerometerConfig()->acc_hardware` is persisted as a raw PG value, existing saved configs using `ACC_FAKE` will be misinterpreted as `ACC_ICM40609D` after flashing this firmware, causing incorrect sensor init.

### Issue Context
- `accelerometerConfig` is a persisted PG and `acc_hardware` is used directly for detection.
- The YAML table is order-synced with the enum, so the numeric index change is real.

### Fix Focus Areas
- src/main/sensors/acceleration.h[34-51]
- src/main/sensors/acceleration.c[90-101]
- src/main/fc/settings.yaml[1-6]
- src/main/config/config_eeprom.c[56-72]

### Suggested fix approach
- Implement a backward-compatibility migration:
  - Bump the `accelerometerConfig` PG version.
  - On load of older versions, remap the old numeric value that previously meant `ACC_FAKE` to the new `ACC_FAKE` value.
- Alternatively (less preferred if many enums rely on ordering), assign explicit stable numeric values for the enum members and update any loops/range checks accordingly, ensuring `ACC_MAX` reflects the true maximum.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +192 to +195
if (tmp == ICM40609D_WHO_AM_I_CONST) {
return true;
}
} while (attemptsRemaining--);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

2. Retry loop off-by-one 🐞 Bug ☼ Reliability

icm40609dDeviceDetect() uses a uint8_t post-decrement do/while which performs 6 WHO_AM_I reads for
attemptsRemaining=5, adding an extra 150ms delay and making retry behavior misleading.
Agent Prompt
### Issue description
The retry loop in `icm40609dDeviceDetect()` runs one extra time due to `do { ... } while (attemptsRemaining--);` with an unsigned counter. This adds an extra delay on failed detection and obscures intent.

### Issue Context
This does not cause an infinite loop, but it *does* perform 6 attempts when initialized to 5.

### Fix Focus Areas
- src/main/drivers/accgyro/accgyro_icm40609d.c[174-198]

### Suggested fix approach
Replace with an explicit bounded loop, e.g.:
```c
for (uint8_t i = 0; i < 5; i++) {
    delay(150);
    if (busRead(dev, MPU_RA_WHO_AM_I, &tmp) && tmp == ICM40609D_WHO_AM_I_CONST) {
        return true;
    }
}
return false;
```
(or keep the existing structure but use `while (attemptsRemaining-- > 0)` and ensure the intended attempt count).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant