From a92a939b24cedf6f6bc6b93cab11f110487f3306 Mon Sep 17 00:00:00 2001 From: helloradiosky <839034809@qq.com> Date: Tue, 23 Jun 2026 11:22:09 +0800 Subject: [PATCH 1/5] The gyroscope of V16 has been enhanced with multi-chip dynamic loading functionality --- radio/src/drivers/icm42627.h | 2 ++ radio/src/targets/horus/CMakeLists.txt | 6 +++++ radio/src/targets/horus/board.cpp | 32 +++++++++++++++++++++----- radio/src/targets/horus/hal.h | 1 - 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/radio/src/drivers/icm42627.h b/radio/src/drivers/icm42627.h index 70c9395a6aa..1b9df0f26b7 100644 --- a/radio/src/drivers/icm42627.h +++ b/radio/src/drivers/icm42627.h @@ -23,6 +23,8 @@ #include "hal/imu.h" +#define ICM42627_I2C_BASE_ADDR 0x68 + #define ICM42627_WHO_AM_I_REG 0x75 #define ICM42627_WHO_AM_I 0x20 diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 80c04f93a8f..16a0e29ffa2 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -116,6 +116,8 @@ if (PCB STREQUAL X10) set(AUX2_SERIAL OFF) set(CSD203_SENSOR ON) set(FLYSKY_GIMBAL ON) + add_definitions(-DIMU_ICM42627) + add_definitions(-DIMU_SC7U22) if (BLUETOOTH) set(AUX_SERIAL OFF) endif() @@ -296,6 +298,10 @@ add_library(board OBJECT EXCLUDE_FROM_ALL ) set(FIRMWARE_SRC ${FIRMWARE_SRC} $) +if(FLAVOUR STREQUAL v16) + target_sources(board PRIVATE drivers/sc7u22.cpp) +endif() + if(BLUETOOTH) target_sources(board PRIVATE targets/common/arm/stm32/bluetooth_driver.cpp) endif() diff --git a/radio/src/targets/horus/board.cpp b/radio/src/targets/horus/board.cpp index bbe71bb0a48..7d9673ffabc 100644 --- a/radio/src/targets/horus/board.cpp +++ b/radio/src/targets/horus/board.cpp @@ -52,7 +52,8 @@ #include "csd203_sensor.h" #endif -#if defined(IMU) && defined(IMU_I2C_BUS) && defined(IMU_I2C_ADDRESS) +#if defined(IMU) && defined(IMU_I2C_BUS) && \ + (defined(RADIO_V16) || defined(IMU_I2C_ADDRESS)) #define HAS_IMU #endif @@ -137,21 +138,40 @@ void audioInit() #include "stm32_i2c_driver.h" #include "drivers/icm42627.h" #include "drivers/lsm6ds.h" +#if defined(IMU_SC7U22) +#include "drivers/sc7u22.h" +#endif + +#if defined(RADIO_V16) + +static const etx_imu_t _imu_candidates[] = { +#if defined(IMU_ICM42627) + { &imu_icm42627_driver, IMU_I2C_BUS, ICM42627_I2C_BASE_ADDR }, + { &imu_icm42627_driver, IMU_I2C_BUS, ICM42627_I2C_BASE_ADDR + 1 }, +#endif +#if defined(IMU_SC7U22) + { &imu_sc7u22_driver, IMU_I2C_BUS, SC7U22_I2C_BASE_ADDR }, + { &imu_sc7u22_driver, IMU_I2C_BUS, SC7U22_I2C_BASE_ADDR + 1 }, +#endif +}; static void gyroInit() { -#if defined(RADIO_V16) - const etx_imu_driver_t *driver = &imu_icm42627_driver; + gyroStart(imuDetect(_imu_candidates, DIM(_imu_candidates))); +} + #else - const etx_imu_driver_t *driver = &imu_lsm6ds_driver; -#endif +static void gyroInit() +{ const etx_imu_t candidates[] = { - { driver, IMU_I2C_BUS, IMU_I2C_ADDRESS }, + { &imu_lsm6ds_driver, IMU_I2C_BUS, IMU_I2C_ADDRESS }, }; gyroStart(imuDetect(candidates, DIM(candidates))); } + +#endif #endif void boardInit() diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index ac93332d46e..868c9d621d7 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -466,7 +466,6 @@ #define IMU_I2C_ADDRESS 0x6A #elif defined(RADIO_V16) #define IMU_I2C_BUS I2C_Bus_1 - #define IMU_I2C_ADDRESS 0x69 #elif !defined(AUX_SERIAL) && defined(IMU_LSM6DS33) #define IMU_I2C_BUS I2C_Bus_2 #define IMU_I2C_ADDRESS 0x6A From c64e010c3e2375b7bde7d382d773f9d9fa489d1c Mon Sep 17 00:00:00 2001 From: Peter Feerick <5500713+pfeerick@users.noreply.github.com> Date: Tue, 23 Jun 2026 20:27:07 +1000 Subject: [PATCH 2/5] refactor(horus): make IMU detection chip-define driven, not handset-specific Replace the RADIO_V16-gated multi-chip candidate table and HAS_IMU check with a single table driven by IMU_xxx chip defines (IMU_ICM42627, IMU_SC7U22, IMU_LSM6DS33), with a fallback for boards that only set the legacy IMU_I2C_ADDRESS (PCBX12S). Lays groundwork for unifying rm-h750/taranis/pa01/st16 onto the same pattern. Co-Authored-By: Claude Sonnet 4.6 --- radio/src/targets/horus/board.cpp | 41 ++++++++++++++----------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/radio/src/targets/horus/board.cpp b/radio/src/targets/horus/board.cpp index 7d9673ffabc..49fa1b5d8c9 100644 --- a/radio/src/targets/horus/board.cpp +++ b/radio/src/targets/horus/board.cpp @@ -53,8 +53,21 @@ #endif #if defined(IMU) && defined(IMU_I2C_BUS) && \ - (defined(RADIO_V16) || defined(IMU_I2C_ADDRESS)) + (defined(IMU_I2C_ADDRESS) || defined(IMU_ICM42627) || \ + defined(IMU_SC7U22) || defined(IMU_LSM6DS33)) #define HAS_IMU + #include "gyro.h" + #include "stm32_i2c_driver.h" + #if defined(IMU_ICM42627) + #include "drivers/icm42627.h" + #endif + #if defined(IMU_SC7U22) + #include "drivers/sc7u22.h" + #endif + #if defined(IMU_LSM6DS33) || \ + (!defined(IMU_ICM42627) && !defined(IMU_SC7U22) && defined(IMU_I2C_ADDRESS)) + #include "drivers/lsm6ds.h" + #endif #endif HardwareOptions hardwareOptions; @@ -134,16 +147,6 @@ void audioInit() #endif #if defined(HAS_IMU) -#include "gyro.h" -#include "stm32_i2c_driver.h" -#include "drivers/icm42627.h" -#include "drivers/lsm6ds.h" -#if defined(IMU_SC7U22) -#include "drivers/sc7u22.h" -#endif - -#if defined(RADIO_V16) - static const etx_imu_t _imu_candidates[] = { #if defined(IMU_ICM42627) { &imu_icm42627_driver, IMU_I2C_BUS, ICM42627_I2C_BASE_ADDR }, @@ -153,6 +156,10 @@ static const etx_imu_t _imu_candidates[] = { { &imu_sc7u22_driver, IMU_I2C_BUS, SC7U22_I2C_BASE_ADDR }, { &imu_sc7u22_driver, IMU_I2C_BUS, SC7U22_I2C_BASE_ADDR + 1 }, #endif +#if defined(IMU_LSM6DS33) || \ + (!defined(IMU_ICM42627) && !defined(IMU_SC7U22) && defined(IMU_I2C_ADDRESS)) + { &imu_lsm6ds_driver, IMU_I2C_BUS, IMU_I2C_ADDRESS }, +#endif }; static void gyroInit() @@ -160,18 +167,6 @@ static void gyroInit() gyroStart(imuDetect(_imu_candidates, DIM(_imu_candidates))); } -#else - -static void gyroInit() -{ - const etx_imu_t candidates[] = { - { &imu_lsm6ds_driver, IMU_I2C_BUS, IMU_I2C_ADDRESS }, - }; - - gyroStart(imuDetect(candidates, DIM(candidates))); -} - -#endif #endif void boardInit() From 9cb5a9ff9be47612aeb42a79e056f15193f7a136 Mon Sep 17 00:00:00 2001 From: Peter Feerick <5500713+pfeerick@users.noreply.github.com> Date: Wed, 24 Jun 2026 22:50:33 +0000 Subject: [PATCH 3/5] refactor(horus): unconditionally build sc7u22 IMU driver like other IMU drivers drivers/sc7u22.cpp had no internal guard, so gating it on FLAVOUR STREQUAL v16 was redundant and inconsistent with lsm6ds.cpp and icm42627.cpp, which are always compiled and rely on --gc-sections to drop unused code when their IMU_xxx define isn't set. Co-Authored-By: Claude Sonnet 4.6 --- radio/src/targets/horus/CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 16a0e29ffa2..6f974fec098 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -287,6 +287,7 @@ add_library(board OBJECT EXCLUDE_FROM_ALL targets/common/arm/stm32/heartbeat_driver.cpp drivers/lsm6ds.cpp drivers/icm42627.cpp + drivers/sc7u22.cpp targets/common/arm/stm32/mixer_scheduler_driver.cpp targets/common/arm/stm32/module_timer_driver.cpp targets/common/arm/stm32/sticks_pwm_driver.cpp @@ -298,10 +299,6 @@ add_library(board OBJECT EXCLUDE_FROM_ALL ) set(FIRMWARE_SRC ${FIRMWARE_SRC} $) -if(FLAVOUR STREQUAL v16) - target_sources(board PRIVATE drivers/sc7u22.cpp) -endif() - if(BLUETOOTH) target_sources(board PRIVATE targets/common/arm/stm32/bluetooth_driver.cpp) endif() From f4a517d459c7e48ffb57f25b08ccd4a91fade399 Mon Sep 17 00:00:00 2001 From: Peter Feerick <5500713+pfeerick@users.noreply.github.com> Date: Thu, 25 Jun 2026 04:15:03 +0000 Subject: [PATCH 4/5] fix(horus): correct ICM42627 register sequencing and I2C probe retries Configure the gyro/accel signal path while powered off per datasheet section 12.6, then power on last with the required 200us/45ms timing windows. Also retry the initial I2C device-ready probe instead of a single quick check, since the chip can take longer than that to start acknowledging after power-up. Co-Authored-By: Claude Sonnet 4.6 --- radio/src/drivers/icm42627.cpp | 56 +++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/radio/src/drivers/icm42627.cpp b/radio/src/drivers/icm42627.cpp index ad796fd8a82..dc27e69d7d6 100644 --- a/radio/src/drivers/icm42627.cpp +++ b/radio/src/drivers/icm42627.cpp @@ -31,8 +31,9 @@ static etx_i2c_bus_t s_i2c_bus; static uint16_t s_i2c_addr; static constexpr uint32_t RESET_DELAY_MS = 150; -static constexpr uint32_t READY_TIMEOUT_MS = 200; -static constexpr uint32_t POWER_DELAY_MS = 20; +static constexpr uint32_t READY_TIMEOUT_MS = 500; +static constexpr uint32_t POWER_ON_SETTLE_US = 200; +static constexpr uint32_t GYRO_STARTUP_DELAY_MS = 45; static constexpr uint32_t CONFIG_DELAY_MS = 1; static constexpr uint8_t BURST_READ_LEN = 14; @@ -75,7 +76,10 @@ static int gyro42627Init(etx_i2c_bus_t bus, uint16_t addr) return -1; } - if (i2c_dev_ready(s_i2c_bus, s_i2c_addr) < 0) { + // The chip can take longer than a couple of quick retries to start + // acknowledging on I2C after power-up, so give the initial probe the + // same generous retry window used after a soft reset below. + if (wait_for_device_ready(READY_TIMEOUT_MS) < 0) { TRACE("ICM42627: device did not acknowledge on I2C"); return -1; } @@ -103,17 +107,27 @@ static int gyro42627Init(etx_i2c_bus_t bus, uint16_t addr) return -1; } - if (write_cmd(ICM42627_PWR_MGMT0_REG, ICM42627_PWR_STAGE1) < 0) { - TRACE("ICM42627: failed to write PWR_MGMT0 stage 1"); + // Per datasheet section 12.6 ("Register Values Modification"), registers + // other than ODR/FSR/mode selects must only be changed while the + // accel/gyro are off. Configure the signal path here, while PWR_MGMT0 + // still has gyro and accel powered down, then power them on last. + if (write_cmd(ICM42627_BANK_SEL_REG, ICM42627_BANK1) < 0) { + TRACE("ICM42627: failed to select register bank 1"); return -1; } - delay_ms(POWER_DELAY_MS); + delay_ms(CONFIG_DELAY_MS); - if (write_cmd(ICM42627_PWR_MGMT0_REG, ICM42627_PWR_STAGE2) < 0) { - TRACE("ICM42627: failed to write PWR_MGMT0 stage 2"); + if (write_cmd(ICM42627_GYRO_STATIC2_REG, ICM42627_GYRO_STATIC2) < 0) { + TRACE("ICM42627: failed to write GYRO_STATIC2"); + return -1; + } + delay_ms(CONFIG_DELAY_MS); + + if (write_cmd(ICM42627_BANK_SEL_REG, ICM42627_BANK0) < 0) { + TRACE("ICM42627: failed to restore register bank 0"); return -1; } - delay_ms(POWER_DELAY_MS); + delay_ms(CONFIG_DELAY_MS); if (write_cmd(ICM42627_GYRO_CONFIG0_REG, ICM42627_GYRO_CONFIG) < 0) { TRACE("ICM42627: failed to write GYRO_CONFIG0"); @@ -145,25 +159,23 @@ static int gyro42627Init(etx_i2c_bus_t bus, uint16_t addr) } delay_ms(CONFIG_DELAY_MS); - if (write_cmd(ICM42627_BANK_SEL_REG, ICM42627_BANK1) < 0) { - TRACE("ICM42627: failed to select register bank 1"); - return -1; - } - delay_ms(CONFIG_DELAY_MS); - - if (write_cmd(ICM42627_GYRO_STATIC2_REG, ICM42627_GYRO_STATIC2) < 0) { - TRACE("ICM42627: failed to write GYRO_STATIC2"); + // Power on gyro and accel in Low Noise mode last, now that the signal + // path is fully configured. The datasheet requires no register writes + // for 200us after an OFF -> mode transition, and the gyro must be kept + // on for at least 45ms before its output is valid. + if (write_cmd(ICM42627_PWR_MGMT0_REG, ICM42627_PWR_STAGE1) < 0) { + TRACE("ICM42627: failed to write PWR_MGMT0 stage 1"); return -1; } - delay_ms(CONFIG_DELAY_MS); + delay_us(POWER_ON_SETTLE_US); - if (write_cmd(ICM42627_BANK_SEL_REG, ICM42627_BANK0) < 0) { - TRACE("ICM42627: failed to restore register bank 0"); + if (write_cmd(ICM42627_PWR_MGMT0_REG, ICM42627_PWR_STAGE2) < 0) { + TRACE("ICM42627: failed to write PWR_MGMT0 stage 2"); return -1; } - delay_ms(CONFIG_DELAY_MS); + delay_ms(GYRO_STARTUP_DELAY_MS); - // Re-read WHO_AM_I after reset and banked configuration to catch a device + // Re-read WHO_AM_I after reset and configuration to catch a device // that stopped responding even though the initial probe succeeded. if (read_cmd(ICM42627_WHO_AM_I_REG, &who_am_i) < 0 || who_am_i != ICM42627_WHO_AM_I) { From 68e1bb51c43004e100a6e47c6529ddb82a4e5ca0 Mon Sep 17 00:00:00 2001 From: Peter Feerick <5500713+pfeerick@users.noreply.github.com> Date: Thu, 25 Jun 2026 07:48:38 +0000 Subject: [PATCH 5/5] fix(horus): restore register bank 0 on GYRO_STATIC2 write failure If the GYRO_STATIC2 write fails during ICM42627 init, the function returned early while still in bank 1, leaving WHO_AM_I/DEVICE_CONFIG (bank 0 registers) unreadable on any subsequent init retry or candidate probe. Co-Authored-By: Claude Sonnet 4.6 --- radio/src/drivers/icm42627.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/drivers/icm42627.cpp b/radio/src/drivers/icm42627.cpp index dc27e69d7d6..b040c34e346 100644 --- a/radio/src/drivers/icm42627.cpp +++ b/radio/src/drivers/icm42627.cpp @@ -119,6 +119,7 @@ static int gyro42627Init(etx_i2c_bus_t bus, uint16_t addr) if (write_cmd(ICM42627_GYRO_STATIC2_REG, ICM42627_GYRO_STATIC2) < 0) { TRACE("ICM42627: failed to write GYRO_STATIC2"); + write_cmd(ICM42627_BANK_SEL_REG, ICM42627_BANK0); return -1; } delay_ms(CONFIG_DELAY_MS);