2 * This file is part of INAV.
4 * INAV is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * INAV is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with INAV. If not, see <http://www.gnu.org/licenses/>.
24 #include "build/debug.h"
26 #include "common/axis.h"
27 #include "common/maths.h"
28 #include "common/utils.h"
29 #include "common/log.h"
31 #include "drivers/system.h"
32 #include "drivers/time.h"
34 #include "drivers/sensor.h"
35 #include "drivers/accgyro/accgyro.h"
36 #include "drivers/accgyro/accgyro_mpu.h"
37 #include "drivers/accgyro/accgyro_icm42605.h"
39 #if defined(USE_IMU_ICM42605)
41 #define ICM42605_RA_PWR_MGMT0 0x4E
43 #define ICM42605_PWR_MGMT0_ACCEL_MODE_LN (3 << 0)
44 #define ICM42605_PWR_MGMT0_GYRO_MODE_LN (3 << 2)
45 #define ICM42605_PWR_MGMT0_TEMP_DISABLE_OFF (0 << 5)
46 #define ICM42605_PWR_MGMT0_TEMP_DISABLE_ON (1 << 5)
48 #define ICM426XX_RA_REG_BANK_SEL 0x76
49 #define ICM426XX_BANK_SELECT0 0x00
50 #define ICM426XX_BANK_SELECT1 0x01
51 #define ICM426XX_BANK_SELECT2 0x02
52 #define ICM426XX_BANK_SELECT3 0x03
53 #define ICM426XX_BANK_SELECT4 0x04
55 #define ICM42605_RA_GYRO_CONFIG0 0x4F
56 #define ICM42605_RA_ACCEL_CONFIG0 0x50
58 #define ICM42605_RA_GYRO_ACCEL_CONFIG0 0x52
60 #define ICM42605_ACCEL_UI_FILT_BW_LOW_LATENCY (15 << 4)
61 #define ICM42605_GYRO_UI_FILT_BW_LOW_LATENCY (15 << 0)
63 #define ICM42605_RA_GYRO_DATA_X1 0x25
64 #define ICM42605_RA_ACCEL_DATA_X1 0x1F
66 #define ICM42605_RA_INT_CONFIG 0x14
67 #define ICM42605_INT1_MODE_PULSED (0 << 2)
68 #define ICM42605_INT1_MODE_LATCHED (1 << 2)
69 #define ICM42605_INT1_DRIVE_CIRCUIT_OD (0 << 1)
70 #define ICM42605_INT1_DRIVE_CIRCUIT_PP (1 << 1)
71 #define ICM42605_INT1_POLARITY_ACTIVE_LOW (0 << 0)
72 #define ICM42605_INT1_POLARITY_ACTIVE_HIGH (1 << 0)
74 #define ICM42605_RA_INT_CONFIG0 0x63
75 #define ICM42605_UI_DRDY_INT_CLEAR_ON_SBR ((0 << 5) || (0 << 4))
76 #define ICM42605_UI_DRDY_INT_CLEAR_ON_SBR_DUPLICATE ((0 << 5) || (0 << 4)) // duplicate settings in datasheet, Rev 1.2.
77 #define ICM42605_UI_DRDY_INT_CLEAR_ON_F1BR ((1 << 5) || (0 << 4))
78 #define ICM42605_UI_DRDY_INT_CLEAR_ON_SBR_AND_F1BR ((1 << 5) || (1 << 4))
80 #define ICM42605_RA_INT_CONFIG1 0x64
81 #define ICM42605_INT_ASYNC_RESET_BIT 4
82 #define ICM42605_INT_TDEASSERT_DISABLE_BIT 5
83 #define ICM42605_INT_TDEASSERT_ENABLED (0 << ICM42605_INT_TDEASSERT_DISABLE_BIT)
84 #define ICM42605_INT_TDEASSERT_DISABLED (1 << ICM42605_INT_TDEASSERT_DISABLE_BIT)
85 #define ICM42605_INT_TPULSE_DURATION_BIT 6
86 #define ICM42605_INT_TPULSE_DURATION_100 (0 << ICM42605_INT_TPULSE_DURATION_BIT)
87 #define ICM42605_INT_TPULSE_DURATION_8 (1 << ICM42605_INT_TPULSE_DURATION_BIT)
89 #define ICM42605_RA_INT_SOURCE0 0x65
90 #define ICM42605_UI_DRDY_INT1_EN_DISABLED (0 << 3)
91 #define ICM42605_UI_DRDY_INT1_EN_ENABLED (1 << 3)
93 #define ICM42605_INTF_CONFIG1 0x4D
94 #define ICM42605_INTF_CONFIG1_AFSR_MASK 0xC0
95 #define ICM42605_INTF_CONFIG1_AFSR_DISABLE 0x40
97 // --- Registers for gyro and acc Anti-Alias Filter ---------
98 #define ICM426XX_RA_GYRO_CONFIG_STATIC3 0x0C // User Bank 1
99 #define ICM426XX_RA_GYRO_CONFIG_STATIC4 0x0D // User Bank 1
100 #define ICM426XX_RA_GYRO_CONFIG_STATIC5 0x0E // User Bank 1
101 #define ICM426XX_RA_ACCEL_CONFIG_STATIC2 0x03 // User Bank 2
102 #define ICM426XX_RA_ACCEL_CONFIG_STATIC3 0x04 // User Bank 2
103 #define ICM426XX_RA_ACCEL_CONFIG_STATIC4 0x05 // User Bank 2
105 static bool is42688P
= false;
107 typedef struct aafConfig_s
{
114 // Possible gyro Anti-Alias Filter (AAF) cutoffs for ICM-42688P
115 static aafConfig_t aafLUT42688
[] = { // see table in section 5.3 https://invensense.tdk.com/wp-content/uploads/2020/04/ds-000347_icm-42688-p-datasheet.pdf
116 // freq, delt, deltSqr, bitshift
126 {1962, 37, 1376, 4 },
127 { 0, 0, 0, 0}, // 42HZ
130 // Possible gyro Anti-Alias Filter (AAF) cutoffs for ICM-42688P
131 static aafConfig_t aafLUT42605
[] = { // see table in section 5.3 https://invensense.tdk.com/wp-content/uploads/2022/09/DS-000292-ICM-42605-v1.7.pdf
132 // freq, delt, deltSqr, bitshift
142 { 524, 39, 1536, 4 },
143 { 995, 63, 3968, 3 },
147 static const aafConfig_t
*getGyroAafConfig(bool is42688
, const uint16_t desiredLpf
);
149 static void setUserBank(const busDevice_t
*dev
, const uint8_t user_bank
)
151 busWrite(dev
, ICM426XX_RA_REG_BANK_SEL
, user_bank
& 7);
154 static void icm42605AccInit(accDev_t
*acc
)
156 acc
->acc_1G
= 512 * 4;
159 static bool icm42605AccRead(accDev_t
*acc
)
163 const bool ack
= busReadBuf(acc
->busDev
, ICM42605_RA_ACCEL_DATA_X1
, data
, 6);
168 acc
->ADCRaw
[X
] = (float) int16_val_big_endian(data
, 0);
169 acc
->ADCRaw
[Y
] = (float) int16_val_big_endian(data
, 1);
170 acc
->ADCRaw
[Z
] = (float) int16_val_big_endian(data
, 2);
175 bool icm42605AccDetect(accDev_t
*acc
)
177 acc
->busDev
= busDeviceOpen(BUSTYPE_ANY
, DEVHW_ICM42605
, acc
->imuSensorToUse
);
178 if (acc
->busDev
== NULL
) {
182 mpuContextData_t
* ctx
= busDeviceGetScratchpadMemory(acc
->busDev
);
183 if (ctx
->chipMagicNumber
!= 0x4265) {
187 acc
->initFn
= icm42605AccInit
;
188 acc
->readFn
= icm42605AccRead
;
189 acc
->accAlign
= acc
->busDev
->param
;
194 static const gyroFilterAndRateConfig_t icm42605GyroConfigs
[] = {
196 { GYRO_LPF_256HZ
, 8000, { 6, 3 } }, /* 400 Hz LPF */
197 { GYRO_LPF_256HZ
, 4000, { 5, 4 } }, /* 250 Hz LPF */
198 { GYRO_LPF_256HZ
, 2000, { 3, 5 } }, /* 250 Hz LPF */
199 { GYRO_LPF_256HZ
, 1000, { 1, 6 } }, /* 250 Hz LPF */
200 { GYRO_LPF_256HZ
, 500, { 0, 15 } }, /* 250 Hz LPF */
203 static void icm42605AccAndGyroInit(gyroDev_t
*gyro
)
205 busDevice_t
* dev
= gyro
->busDev
;
206 const gyroFilterAndRateConfig_t
* config
= chooseGyroConfig(gyro
->lpf
, 1000000 / gyro
->requestedSampleIntervalUs
,
207 &icm42605GyroConfigs
[0], ARRAYLEN(icm42605GyroConfigs
));
208 gyro
->sampleRateIntervalUs
= 1000000 / config
->gyroRateHz
;
210 busSetSpeed(dev
, BUS_SPEED_INITIALIZATION
);
212 setUserBank(dev
, ICM426XX_BANK_SELECT0
);
213 busWrite(dev
, ICM42605_RA_PWR_MGMT0
, ICM42605_PWR_MGMT0_TEMP_DISABLE_OFF
| ICM42605_PWR_MGMT0_ACCEL_MODE_LN
| ICM42605_PWR_MGMT0_GYRO_MODE_LN
);
216 /* ODR and dynamic range */
217 busWrite(dev
, ICM42605_RA_GYRO_CONFIG0
, (0x00) << 5 | (config
->gyroConfigValues
[1] & 0x0F)); /* 2000 deg/s */
220 busWrite(dev
, ICM42605_RA_ACCEL_CONFIG0
, (0x00) << 5 | (config
->gyroConfigValues
[1] & 0x0F)); /* 16 G deg/s */
224 // low latency, same as BF
225 busWrite(dev
, ICM42605_RA_GYRO_ACCEL_CONFIG0
, ICM42605_ACCEL_UI_FILT_BW_LOW_LATENCY
| ICM42605_GYRO_UI_FILT_BW_LOW_LATENCY
);
228 if (gyro
->lpf
!= GYRO_LPF_NONE
) {
229 // Configure gyro Anti-Alias Filter (see section 5.3 "ANTI-ALIAS FILTER")
230 const aafConfig_t
*aafConfig
= getGyroAafConfig(is42688P
, gyro
->lpf
);
232 setUserBank(dev
, ICM426XX_BANK_SELECT1
);
233 busWrite(dev
, ICM426XX_RA_GYRO_CONFIG_STATIC3
, aafConfig
->delt
);
234 busWrite(dev
, ICM426XX_RA_GYRO_CONFIG_STATIC4
, aafConfig
->deltSqr
& 0xFF);
235 busWrite(dev
, ICM426XX_RA_GYRO_CONFIG_STATIC5
, (aafConfig
->deltSqr
>> 8) | (aafConfig
->bitshift
<< 4));
237 aafConfig
= getGyroAafConfig(is42688P
, 256); // This was hard coded on BF
238 setUserBank(dev
, ICM426XX_BANK_SELECT2
);
239 busWrite(dev
, ICM426XX_RA_ACCEL_CONFIG_STATIC2
, aafConfig
->delt
<< 1);
240 busWrite(dev
, ICM426XX_RA_ACCEL_CONFIG_STATIC3
, aafConfig
->deltSqr
& 0xFF);
241 busWrite(dev
, ICM426XX_RA_ACCEL_CONFIG_STATIC4
, (aafConfig
->deltSqr
>> 8) | (aafConfig
->bitshift
<< 4));
244 setUserBank(dev
, ICM426XX_BANK_SELECT0
);
245 busWrite(dev
, ICM42605_RA_INT_CONFIG
, ICM42605_INT1_MODE_PULSED
| ICM42605_INT1_DRIVE_CIRCUIT_PP
| ICM42605_INT1_POLARITY_ACTIVE_HIGH
);
248 busWrite(dev
, ICM42605_RA_INT_CONFIG0
, ICM42605_UI_DRDY_INT_CLEAR_ON_SBR
);
251 uint8_t intConfig1Value
;
253 busWrite(dev
, ICM42605_RA_INT_SOURCE0
, ICM42605_UI_DRDY_INT1_EN_ENABLED
);
255 // Datasheet says: "User should change setting to 0 from default setting of 1, for proper INT1 and INT2 pin operation"
256 busRead(dev
, ICM42605_RA_INT_CONFIG1
, &intConfig1Value
);
258 intConfig1Value
&= ~(1 << ICM42605_INT_ASYNC_RESET_BIT
);
259 intConfig1Value
|= (ICM42605_INT_TPULSE_DURATION_8
| ICM42605_INT_TDEASSERT_DISABLED
);
261 busWrite(dev
, ICM42605_RA_INT_CONFIG1
, intConfig1Value
);
264 //Disable AFSR as in BF and Ardupilot
265 uint8_t intfConfig1Value
;
266 busRead(dev
, ICM42605_INTF_CONFIG1
, &intfConfig1Value
);
267 intfConfig1Value
&= ~ICM42605_INTF_CONFIG1_AFSR_MASK
;
268 intfConfig1Value
|= ICM42605_INTF_CONFIG1_AFSR_DISABLE
;
269 busWrite(dev
, ICM42605_INTF_CONFIG1
, intfConfig1Value
);
273 busSetSpeed(dev
, BUS_SPEED_FAST
);
276 static bool icm42605DeviceDetect(busDevice_t
* dev
)
279 uint8_t attemptsRemaining
= 5;
281 busSetSpeed(dev
, BUS_SPEED_INITIALIZATION
);
283 busWrite(dev
, ICM42605_RA_PWR_MGMT0
, 0x00);
288 busRead(dev
, MPU_RA_WHO_AM_I
, &tmp
);
291 /* ICM42605 and ICM42688P share the register structure*/
292 case ICM42605_WHO_AM_I_CONST
:
295 case ICM42688P_WHO_AM_I_CONST
:
303 } while (attemptsRemaining
--);
308 static bool icm42605GyroRead(gyroDev_t
*gyro
)
312 const bool ack
= busReadBuf(gyro
->busDev
, ICM42605_RA_GYRO_DATA_X1
, data
, 6);
317 gyro
->gyroADCRaw
[X
] = (float) int16_val_big_endian(data
, 0);
318 gyro
->gyroADCRaw
[Y
] = (float) int16_val_big_endian(data
, 1);
319 gyro
->gyroADCRaw
[Z
] = (float) int16_val_big_endian(data
, 2);
324 bool icm42605GyroDetect(gyroDev_t
*gyro
)
326 gyro
->busDev
= busDeviceInit(BUSTYPE_ANY
, DEVHW_ICM42605
, gyro
->imuSensorToUse
, OWNER_MPU
);
327 if (gyro
->busDev
== NULL
) {
331 if (!icm42605DeviceDetect(gyro
->busDev
)) {
332 busDeviceDeInit(gyro
->busDev
);
336 // Magic number for ACC detection to indicate that we have detected icm42605 gyro
337 mpuContextData_t
* ctx
= busDeviceGetScratchpadMemory(gyro
->busDev
);
338 ctx
->chipMagicNumber
= 0x4265;
340 gyro
->initFn
= icm42605AccAndGyroInit
;
341 gyro
->readFn
= icm42605GyroRead
;
342 gyro
->intStatusFn
= gyroCheckDataReady
;
343 gyro
->temperatureFn
= NULL
;
344 gyro
->scale
= 1.0f
/ 16.4f
; // 16.4 dps/lsb scalefactor
345 gyro
->gyroAlign
= gyro
->busDev
->param
;
350 static uint16_t getAafFreq(const uint8_t gyroLpf
)
373 static const aafConfig_t
*getGyroAafConfig(bool is42688
, const uint16_t desiredLpf
)
375 uint16_t desiredFreq
= getAafFreq(desiredLpf
);
376 const aafConfig_t
*aafConfigs
= NULL
;
378 aafConfigs
= aafLUT42688
;
380 aafConfigs
= aafLUT42605
;
383 int8_t selectedFreq
= aafConfigs
[0].freq
;
384 const aafConfig_t
* candidate
= &aafConfigs
[0];
386 // Choose closest supported LPF value
387 for (i
= 1; aafConfigs
[i
].freq
!= 0; i
++) {
388 if (ABS(desiredFreq
- aafConfigs
[i
].freq
) < ABS(desiredFreq
- selectedFreq
)) {
389 selectedFreq
= aafConfigs
[i
].freq
;
390 candidate
= &aafConfigs
[i
];
394 LOG_VERBOSE(GYRO
, "ICM426%s AAF CONFIG { %d, %d } -> { %d }; delt: %d deltSqr: %d, shift: %d",
395 (is42688P
? "88" : "05"),
396 desiredLpf
, desiredFreq
,
398 candidate
->delt
, candidate
->deltSqr
, candidate
->bitshift
);