2 * This file is part of Cleanflight.
4 * Cleanflight 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 * Cleanflight 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 Cleanflight. If not, see <http://www.gnu.org/licenses/>.
25 FILE_COMPILE_FOR_SPEED
27 #include "build/debug.h"
29 #include "common/axis.h"
30 #include "common/filter.h"
31 #include "common/maths.h"
32 #include "common/utils.h"
33 #include "common/calibration.h"
35 #include "config/config_reset.h"
36 #include "config/parameter_group.h"
37 #include "config/parameter_group_ids.h"
39 #include "drivers/accgyro/accgyro.h"
40 #include "drivers/accgyro/accgyro_mpu.h"
41 #include "drivers/accgyro/accgyro_mpu6000.h"
42 #include "drivers/accgyro/accgyro_mpu6500.h"
43 #include "drivers/accgyro/accgyro_mpu9250.h"
45 #include "drivers/accgyro/accgyro_bmi088.h"
46 #include "drivers/accgyro/accgyro_bmi160.h"
47 #include "drivers/accgyro/accgyro_bmi270.h"
48 #include "drivers/accgyro/accgyro_icm20689.h"
49 #include "drivers/accgyro/accgyro_icm42605.h"
50 #include "drivers/accgyro/accgyro_fake.h"
51 #include "drivers/sensor.h"
53 #include "fc/config.h"
54 #include "fc/runtime_config.h"
55 #include "fc/settings.h"
57 #include "io/beeper.h"
59 #include "sensors/acceleration.h"
60 #include "sensors/battery.h"
61 #include "sensors/boardalignment.h"
62 #include "sensors/gyro.h"
63 #include "sensors/sensors.h"
65 #ifdef USE_HARDWARE_REVISION_DETECTION
66 #include "hardware_revision.h"
70 FASTRAM acc_t acc
; // acc access functions
72 STATIC_FASTRAM zeroCalibrationVector_t zeroCalibration
;
74 STATIC_FASTRAM
int32_t accADC
[XYZ_AXIS_COUNT
];
76 STATIC_FASTRAM filter_t accFilter
[XYZ_AXIS_COUNT
];
77 STATIC_FASTRAM filterApplyFnPtr accSoftLpfFilterApplyFn
;
78 STATIC_FASTRAM
void *accSoftLpfFilter
[XYZ_AXIS_COUNT
];
80 static EXTENDED_FASTRAM pt1Filter_t accVibeFloorFilter
[XYZ_AXIS_COUNT
];
81 static EXTENDED_FASTRAM pt1Filter_t accVibeFilter
[XYZ_AXIS_COUNT
];
83 static EXTENDED_FASTRAM filterApplyFnPtr accNotchFilterApplyFn
;
84 static EXTENDED_FASTRAM
void *accNotchFilter
[XYZ_AXIS_COUNT
];
86 PG_REGISTER_WITH_RESET_FN(accelerometerConfig_t
, accelerometerConfig
, PG_ACCELEROMETER_CONFIG
, 5);
88 void pgResetFn_accelerometerConfig(accelerometerConfig_t
*instance
)
90 RESET_CONFIG_2(accelerometerConfig_t
, instance
,
91 .acc_hardware
= SETTING_ACC_HARDWARE_DEFAULT
,
92 .acc_lpf_hz
= SETTING_ACC_LPF_HZ_DEFAULT
,
93 .acc_notch_hz
= SETTING_ACC_NOTCH_HZ_DEFAULT
,
94 .acc_notch_cutoff
= SETTING_ACC_NOTCH_CUTOFF_DEFAULT
,
95 .acc_soft_lpf_type
= SETTING_ACC_LPF_TYPE_DEFAULT
97 RESET_CONFIG_2(flightDynamicsTrims_t
, &instance
->accZero
,
98 .raw
[X
] = SETTING_ACCZERO_X_DEFAULT
,
99 .raw
[Y
] = SETTING_ACCZERO_Y_DEFAULT
,
100 .raw
[Z
] = SETTING_ACCZERO_Z_DEFAULT
102 RESET_CONFIG_2(flightDynamicsTrims_t
, &instance
->accGain
,
103 .raw
[X
] = SETTING_ACCGAIN_X_DEFAULT
,
104 .raw
[Y
] = SETTING_ACCGAIN_Y_DEFAULT
,
105 .raw
[Z
] = SETTING_ACCGAIN_Z_DEFAULT
109 static bool accDetect(accDev_t
*dev
, accelerationSensor_e accHardwareToUse
)
111 accelerationSensor_e accHardware
= ACC_NONE
;
113 dev
->accAlign
= ALIGN_DEFAULT
;
115 requestedSensors
[SENSOR_INDEX_ACC
] = accHardwareToUse
;
117 switch (accHardwareToUse
) {
121 #ifdef USE_IMU_MPU6000
123 if (mpu6000AccDetect(dev
)) {
124 accHardware
= ACC_MPU6000
;
127 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
128 if (accHardwareToUse
!= ACC_AUTODETECT
) {
134 #if defined(USE_IMU_MPU6500)
136 if (mpu6500AccDetect(dev
)) {
137 accHardware
= ACC_MPU6500
;
140 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
141 if (accHardwareToUse
!= ACC_AUTODETECT
) {
147 #if defined(USE_IMU_MPU9250)
149 if (mpu9250AccDetect(dev
)) {
150 accHardware
= ACC_MPU9250
;
153 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
154 if (accHardwareToUse
!= ACC_AUTODETECT
) {
160 #if defined(USE_IMU_BMI160)
162 if (bmi160AccDetect(dev
)) {
163 accHardware
= ACC_BMI160
;
166 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
167 if (accHardwareToUse
!= ACC_AUTODETECT
) {
173 #if defined(USE_IMU_BMI088)
175 if (bmi088AccDetect(dev
)) {
176 accHardware
= ACC_BMI088
;
179 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
180 if (accHardwareToUse
!= ACC_AUTODETECT
) {
186 #ifdef USE_IMU_ICM20689
188 if (icm20689AccDetect(dev
)) {
189 accHardware
= ACC_ICM20689
;
192 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
193 if (accHardwareToUse
!= ACC_AUTODETECT
) {
199 #ifdef USE_IMU_ICM42605
201 if (icm42605AccDetect(dev
)) {
202 accHardware
= ACC_ICM42605
;
205 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
206 if (accHardwareToUse
!= ACC_AUTODETECT
) {
212 #ifdef USE_IMU_BMI270
214 if (bmi270AccDetect(dev
)) {
215 accHardware
= ACC_BMI270
;
218 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
219 if (accHardwareToUse
!= ACC_AUTODETECT
) {
227 if (fakeAccDetect(dev
)) {
228 accHardware
= ACC_FAKE
;
231 /* If we are asked for a specific sensor - break out, otherwise - fall through and continue */
232 if (accHardwareToUse
!= ACC_AUTODETECT
) {
239 case ACC_NONE
: // disable ACC
240 accHardware
= ACC_NONE
;
244 if (accHardware
== ACC_NONE
) {
248 detectedSensors
[SENSOR_INDEX_ACC
] = accHardware
;
249 sensorsSet(SENSOR_ACC
);
253 bool accInit(uint32_t targetLooptime
)
255 memset(&acc
, 0, sizeof(acc
));
257 // Set inertial sensor tag (for dual-gyro selection)
259 acc
.dev
.imuSensorToUse
= gyroConfig()->gyro_to_use
; // Use the same selection from gyroConfig()
261 acc
.dev
.imuSensorToUse
= 0;
264 if (!accDetect(&acc
.dev
, accelerometerConfig()->acc_hardware
)) {
268 acc
.dev
.acc_1G
= 256; // set default
269 acc
.dev
.initFn(&acc
.dev
);
270 acc
.accTargetLooptime
= targetLooptime
;
271 acc
.accClipCount
= 0;
274 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
275 acc
.extremes
[axis
].min
= 100;
276 acc
.extremes
[axis
].max
= -100;
282 static bool calibratedPosition
[6];
283 static int32_t accSamples
[6][3];
285 uint8_t accGetCalibrationAxisFlags(void)
287 if (STATE(ACCELEROMETER_CALIBRATED
)) {
288 return 0x3F; // All 6 bits are set
291 static const uint8_t bitMap
[6] = { 0, 1, 3, 5, 2, 4 }; // A mapping of bits to match position indexes in Configurator
293 for (int i
= 0; i
< 6; i
++) {
294 if (calibratedPosition
[i
]) {
295 flags
|= (1 << bitMap
[i
]);
302 static int getPrimaryAxisIndex(int32_t accADCData
[3])
304 // Work on a copy so we don't mess with accADC data
307 applySensorAlignment(sample
, accADCData
, acc
.dev
.accAlign
);
309 // Tolerate up to atan(1 / 1.5) = 33 deg tilt (in worst case 66 deg separation between points)
310 if ((ABS(sample
[Z
]) / 1.5f
) > ABS(sample
[X
]) && (ABS(sample
[Z
]) / 1.5f
) > ABS(sample
[Y
])) {
312 return (sample
[Z
] > 0) ? 0 : 1;
314 else if ((ABS(sample
[X
]) / 1.5f
) > ABS(sample
[Y
]) && (ABS(sample
[X
]) / 1.5f
) > ABS(sample
[Z
])) {
316 return (sample
[X
] > 0) ? 2 : 3;
318 else if ((ABS(sample
[Y
]) / 1.5f
) > ABS(sample
[X
]) && (ABS(sample
[Y
]) / 1.5f
) > ABS(sample
[Z
])) {
320 return (sample
[Y
] > 0) ? 4 : 5;
326 void accStartCalibration(void)
328 int positionIndex
= getPrimaryAxisIndex(accADC
);
330 // Fail if we can't detect the side
331 if (positionIndex
< 0) {
335 // Fail if we have accelerometer fully calibrated and are NOT starting with TOP-UP position
336 if (STATE(ACCELEROMETER_CALIBRATED
) && positionIndex
!= 0) {
340 // Top+up and first calibration cycle, reset everything
341 if (positionIndex
== 0) {
342 for (int axis
= 0; axis
< 6; axis
++) {
343 calibratedPosition
[axis
] = false;
344 accSamples
[axis
][X
] = 0;
345 accSamples
[axis
][Y
] = 0;
346 accSamples
[axis
][Z
] = 0;
349 DISABLE_STATE(ACCELEROMETER_CALIBRATED
);
352 // Tolerate 5% variance in accelerometer readings
353 zeroCalibrationStartV(&zeroCalibration
, CALIBRATING_ACC_TIME_MS
, acc
.dev
.acc_1G
* 0.05f
, true);
356 static bool allOrientationsHaveCalibrationDataCollected(void)
358 // Return true only if we have calibration data for all 6 positions
359 return calibratedPosition
[0] && calibratedPosition
[1] && calibratedPosition
[2] &&
360 calibratedPosition
[3] && calibratedPosition
[4] && calibratedPosition
[5];
363 bool accIsCalibrationComplete(void)
365 return zeroCalibrationIsCompleteV(&zeroCalibration
);
368 static void performAcclerationCalibration(void)
370 // Shortcut - no need to do any math if acceleromter is marked as calibrated
371 if (STATE(ACCELEROMETER_CALIBRATED
)) {
375 // If zero calibration logic is finished - no need to do anything
376 if (accIsCalibrationComplete()) {
381 int positionIndex
= getPrimaryAxisIndex(accADC
);
383 // Check if sample is usable
384 if (positionIndex
< 0) {
388 if (!calibratedPosition
[positionIndex
]) {
393 zeroCalibrationAddValueV(&zeroCalibration
, &v
);
395 if (zeroCalibrationIsCompleteV(&zeroCalibration
)) {
396 if (zeroCalibrationIsSuccessfulV(&zeroCalibration
)) {
397 zeroCalibrationGetZeroV(&zeroCalibration
, &v
);
399 accSamples
[positionIndex
][X
] = v
.v
[X
];
400 accSamples
[positionIndex
][Y
] = v
.v
[Y
];
401 accSamples
[positionIndex
][Z
] = v
.v
[Z
];
403 calibratedPosition
[positionIndex
] = true;
406 calibratedPosition
[positionIndex
] = false;
409 beeperConfirmationBeeps(2);
413 if (allOrientationsHaveCalibrationDataCollected()) {
414 sensorCalibrationState_t calState
;
416 bool calFailed
= false;
418 /* Calculate offset */
419 sensorCalibrationResetState(&calState
);
421 for (int axis
= 0; axis
< 6; axis
++) {
422 sensorCalibrationPushSampleForOffsetCalculation(&calState
, accSamples
[axis
]);
425 if (!sensorCalibrationSolveForOffset(&calState
, accTmp
)) {
432 accelerometerConfigMutable()->accZero
.raw
[X
] = lrintf(accTmp
[X
]);
433 accelerometerConfigMutable()->accZero
.raw
[Y
] = lrintf(accTmp
[Y
]);
434 accelerometerConfigMutable()->accZero
.raw
[Z
] = lrintf(accTmp
[Z
]);
436 /* Not we can offset our accumulated averages samples and calculate scale factors and calculate gains */
437 sensorCalibrationResetState(&calState
);
439 for (int axis
= 0; axis
< 6; axis
++) {
440 int32_t accSample
[3];
442 accSample
[X
] = accSamples
[axis
][X
] - accelerometerConfig()->accZero
.raw
[X
];
443 accSample
[Y
] = accSamples
[axis
][Y
] - accelerometerConfig()->accZero
.raw
[Y
];
444 accSample
[Z
] = accSamples
[axis
][Z
] - accelerometerConfig()->accZero
.raw
[Z
];
446 sensorCalibrationPushSampleForScaleCalculation(&calState
, axis
/ 2, accSample
, acc
.dev
.acc_1G
);
449 if (!sensorCalibrationSolveForScale(&calState
, accTmp
)) {
456 for (int axis
= 0; axis
< 3; axis
++) {
457 accelerometerConfigMutable()->accGain
.raw
[axis
] = lrintf(accTmp
[axis
] * 4096);
461 // If failed - don't save and also invalidate the calibration data for all positions
462 for (int axis
= 0; axis
< 6; axis
++) {
463 calibratedPosition
[axis
] = false;
467 // saveConfigAndNotify will trigger eepromREAD and in turn call back the accelerometer gain validation
468 // that will set ENABLE_STATE(ACCELEROMETER_CALIBRATED) if all is good
469 saveConfigAndNotify();
474 static void applyAccelerationZero(const flightDynamicsTrims_t
* accZero
, const flightDynamicsTrims_t
* accGain
)
476 accADC
[X
] = (accADC
[X
] - accZero
->raw
[X
]) * accGain
->raw
[X
] / 4096;
477 accADC
[Y
] = (accADC
[Y
] - accZero
->raw
[Y
]) * accGain
->raw
[Y
] / 4096;
478 accADC
[Z
] = (accADC
[Z
] - accZero
->raw
[Z
]) * accGain
->raw
[Z
] / 4096;
482 * Calculate measured acceleration in body frame in m/s^2
484 void accGetMeasuredAcceleration(fpVector3_t
*measuredAcc
)
486 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
487 measuredAcc
->v
[axis
] = acc
.accADCf
[axis
] * GRAVITY_CMSS
;
494 const acc_extremes_t
* accGetMeasuredExtremes(void)
496 return (const acc_extremes_t
*)&acc
.extremes
;
499 float accGetMeasuredMaxG(void)
507 if (ARMING_FLAG(SIMULATOR_MODE
)) {
508 //output: acc.accADCf
509 //unused: acc.dev.ADCRaw[], acc.accClipCount, acc.accVibeSq[]
513 if (!acc
.dev
.readFn(&acc
.dev
)) {
517 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
518 accADC
[axis
] = acc
.dev
.ADCRaw
[axis
];
519 DEBUG_SET(DEBUG_ACC
, axis
, accADC
[axis
]);
522 performAcclerationCalibration();
524 applyAccelerationZero(&accelerometerConfig()->accZero
, &accelerometerConfig()->accGain
);
526 applySensorAlignment(accADC
, accADC
, acc
.dev
.accAlign
);
527 applyBoardAlignment(accADC
);
529 // Calculate acceleration readings in G's
530 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
531 acc
.accADCf
[axis
] = (float)accADC
[axis
] / acc
.dev
.acc_1G
;
534 // Before filtering check for clipping and vibration levels
535 if (fabsf(acc
.accADCf
[X
]) > ACC_CLIPPING_THRESHOLD_G
|| fabsf(acc
.accADCf
[Y
]) > ACC_CLIPPING_THRESHOLD_G
|| fabsf(acc
.accADCf
[Z
]) > ACC_CLIPPING_THRESHOLD_G
) {
536 acc
.isClipped
= true;
540 acc
.isClipped
= false;
543 // Calculate vibration levels
544 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
545 // filter accel at 5hz
546 const float accFloorFilt
= pt1FilterApply(&accVibeFloorFilter
[axis
], acc
.accADCf
[axis
]);
548 // calc difference from this sample and 5hz filtered value, square and filter at 2hz
549 const float accDiff
= acc
.accADCf
[axis
] - accFloorFilt
;
550 acc
.accVibeSq
[axis
] = pt1FilterApply(&accVibeFilter
[axis
], accDiff
* accDiff
);
553 // Filter acceleration
554 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
555 acc
.accADCf
[axis
] = accSoftLpfFilterApplyFn(accSoftLpfFilter
[axis
], acc
.accADCf
[axis
]);
558 if (accelerometerConfig()->acc_notch_hz
) {
559 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
560 acc
.accADCf
[axis
] = accNotchFilterApplyFn(accNotchFilter
[axis
], acc
.accADCf
[axis
]);
566 // Record extremes: min/max for each axis and acceleration vector modulus
567 void updateAccExtremes(void)
569 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
570 if (acc
.accADCf
[axis
] < acc
.extremes
[axis
].min
) acc
.extremes
[axis
].min
= acc
.accADCf
[axis
];
571 if (acc
.accADCf
[axis
] > acc
.extremes
[axis
].max
) acc
.extremes
[axis
].max
= acc
.accADCf
[axis
];
574 float gforce
= calc_length_pythagorean_3D(acc
.accADCf
[X
], acc
.accADCf
[Y
], acc
.accADCf
[Z
]);
575 if (gforce
> acc
.maxG
) acc
.maxG
= gforce
;
578 void accGetVibrationLevels(fpVector3_t
*accVibeLevels
)
580 accVibeLevels
->x
= fast_fsqrtf(acc
.accVibeSq
[X
]);
581 accVibeLevels
->y
= fast_fsqrtf(acc
.accVibeSq
[Y
]);
582 accVibeLevels
->z
= fast_fsqrtf(acc
.accVibeSq
[Z
]);
585 float accGetVibrationLevel(void)
587 return fast_fsqrtf(acc
.accVibeSq
[X
] + acc
.accVibeSq
[Y
] + acc
.accVibeSq
[Z
]);
590 uint32_t accGetClipCount(void)
592 return acc
.accClipCount
;
595 bool accIsClipped(void)
597 return acc
.isClipped
;
600 void accSetCalibrationValues(void)
602 if ((accelerometerConfig()->accZero
.raw
[X
] == 0) && (accelerometerConfig()->accZero
.raw
[Y
] == 0) && (accelerometerConfig()->accZero
.raw
[Z
] == 0) &&
603 (accelerometerConfig()->accGain
.raw
[X
] == 4096) && (accelerometerConfig()->accGain
.raw
[Y
] == 4096) &&(accelerometerConfig()->accGain
.raw
[Z
] == 4096)) {
604 DISABLE_STATE(ACCELEROMETER_CALIBRATED
);
607 ENABLE_STATE(ACCELEROMETER_CALIBRATED
);
611 void accInitFilters(void)
613 accSoftLpfFilterApplyFn
= nullFilterApply
;
615 if (acc
.accTargetLooptime
&& accelerometerConfig()->acc_lpf_hz
) {
617 switch (accelerometerConfig()->acc_soft_lpf_type
)
620 accSoftLpfFilterApplyFn
= (filterApplyFnPtr
)pt1FilterApply
;
621 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
622 accSoftLpfFilter
[axis
] = &accFilter
[axis
].pt1
;
623 pt1FilterInit(accSoftLpfFilter
[axis
], accelerometerConfig()->acc_lpf_hz
, US2S(acc
.accTargetLooptime
));
627 accSoftLpfFilterApplyFn
= (filterApplyFnPtr
)biquadFilterApply
;
628 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
629 accSoftLpfFilter
[axis
] = &accFilter
[axis
].biquad
;
630 biquadFilterInitLPF(accSoftLpfFilter
[axis
], accelerometerConfig()->acc_lpf_hz
, acc
.accTargetLooptime
);
637 const float accDt
= US2S(acc
.accTargetLooptime
);
638 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
639 pt1FilterInit(&accVibeFloorFilter
[axis
], ACC_VIBE_FLOOR_FILT_HZ
, accDt
);
640 pt1FilterInit(&accVibeFilter
[axis
], ACC_VIBE_FILT_HZ
, accDt
);
643 STATIC_FASTRAM biquadFilter_t accFilterNotch
[XYZ_AXIS_COUNT
];
644 accNotchFilterApplyFn
= nullFilterApply
;
646 if (acc
.accTargetLooptime
&& accelerometerConfig()->acc_notch_hz
) {
647 accNotchFilterApplyFn
= (filterApplyFnPtr
)biquadFilterApply
;
648 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
649 accNotchFilter
[axis
] = &accFilterNotch
[axis
];
650 biquadFilterInitNotch(accNotchFilter
[axis
], acc
.accTargetLooptime
, accelerometerConfig()->acc_notch_hz
, accelerometerConfig()->acc_notch_cutoff
);
656 bool accIsHealthy(void)