2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
30 #include "build/debug.h"
32 #include "common/axis.h"
33 #include "common/filter.h"
34 #include "common/utils.h"
36 #include "config/config_reset.h"
37 #include "config/feature.h"
39 #include "drivers/accgyro/accgyro.h"
40 #include "drivers/accgyro/accgyro_fake.h"
41 #include "drivers/accgyro/accgyro_mpu.h"
42 #include "drivers/accgyro/accgyro_mpu3050.h"
43 #include "drivers/accgyro/accgyro_mpu6050.h"
44 #include "drivers/accgyro/accgyro_mpu6500.h"
45 #include "drivers/accgyro/accgyro_spi_bmi160.h"
46 #include "drivers/accgyro/accgyro_spi_bmi270.h"
47 #include "drivers/accgyro/accgyro_spi_icm20649.h"
48 #include "drivers/accgyro/accgyro_spi_icm20689.h"
49 #include "drivers/accgyro/accgyro_spi_icm426xx.h"
50 #include "drivers/accgyro/accgyro_spi_lsm6dso.h"
51 #include "drivers/accgyro/accgyro_spi_mpu6000.h"
52 #include "drivers/accgyro/accgyro_spi_mpu6500.h"
53 #include "drivers/accgyro/accgyro_spi_mpu9250.h"
55 #ifdef USE_ACC_ADXL345
56 #include "drivers/accgyro_legacy/accgyro_adxl345.h"
60 #include "drivers/accgyro_legacy/accgyro_bma280.h"
63 #ifdef USE_ACC_LSM303DLHC
64 #include "drivers/accgyro_legacy/accgyro_lsm303dlhc.h"
67 #ifdef USE_ACC_MMA8452
68 #include "drivers/accgyro_legacy/accgyro_mma845x.h"
71 #include "drivers/bus_spi.h"
73 #include "config/config.h"
74 #include "fc/runtime_config.h"
76 #include "io/beeper.h"
78 #include "pg/gyrodev.h"
80 #include "pg/pg_ids.h"
82 #include "sensors/boardalignment.h"
83 #include "sensors/gyro.h"
84 #include "sensors/gyro_init.h"
85 #include "sensors/sensors.h"
87 #include "acceleration_init.h"
89 #define CALIBRATING_ACC_CYCLES 400
91 FAST_DATA_ZERO_INIT accelerationRuntime_t accelerationRuntime
;
93 void resetRollAndPitchTrims(rollAndPitchTrims_t
*rollAndPitchTrims
)
95 RESET_CONFIG_2(rollAndPitchTrims_t
, rollAndPitchTrims
,
101 static void setConfigCalibrationCompleted(void)
103 accelerometerConfigMutable()->accZero
.values
.calibrationCompleted
= 1;
106 bool accHasBeenCalibrated(void)
108 return accelerometerConfig()->accZero
.values
.calibrationCompleted
;
111 void accResetRollAndPitchTrims(void)
113 resetRollAndPitchTrims(&accelerometerConfigMutable()->accelerometerTrims
);
116 static void resetFlightDynamicsTrims(flightDynamicsTrims_t
*accZero
)
118 accZero
->values
.roll
= 0;
119 accZero
->values
.pitch
= 0;
120 accZero
->values
.yaw
= 0;
121 accZero
->values
.calibrationCompleted
= 0;
124 void pgResetFn_accelerometerConfig(accelerometerConfig_t
*instance
)
126 RESET_CONFIG_2(accelerometerConfig_t
, instance
,
127 .acc_lpf_hz
= 25, // ATTITUDE/IMU runs at 100Hz (acro) or 500Hz (level modes) so we need to set 50 Hz (or lower) to avoid aliasing
128 .acc_hardware
= ACC_DEFAULT
,
129 .acc_high_fsr
= false,
131 resetRollAndPitchTrims(&instance
->accelerometerTrims
);
132 resetFlightDynamicsTrims(&instance
->accZero
);
135 PG_REGISTER_WITH_RESET_FN(accelerometerConfig_t
, accelerometerConfig
, PG_ACCELEROMETER_CONFIG
, 2);
137 extern uint16_t InflightcalibratingA
;
138 extern bool AccInflightCalibrationMeasurementDone
;
139 extern bool AccInflightCalibrationSavetoEEProm
;
140 extern bool AccInflightCalibrationActive
;
143 bool accDetect(accDev_t
*dev
, accelerationSensor_e accHardwareToUse
)
145 accelerationSensor_e accHardware
= ACC_NONE
;
147 #ifdef USE_ACC_ADXL345
148 drv_adxl345_config_t acc_params
;
153 switch (accHardwareToUse
) {
157 #ifdef USE_ACC_ADXL345
158 case ACC_ADXL345
: // ADXL345
159 acc_params
.useFifo
= false;
160 acc_params
.dataRate
= 800; // unused currently
161 if (adxl345Detect(&acc_params
, dev
)) {
162 accHardware
= ACC_ADXL345
;
168 #ifdef USE_ACC_LSM303DLHC
170 if (lsm303dlhcAccDetect(dev
)) {
171 accHardware
= ACC_LSM303DLHC
;
177 #ifdef USE_ACC_MPU6050
178 case ACC_MPU6050
: // MPU6050
179 if (mpu6050AccDetect(dev
)) {
180 accHardware
= ACC_MPU6050
;
186 #ifdef USE_ACC_MMA8452
187 case ACC_MMA8452
: // MMA8452
188 if (mma8452Detect(dev
)) {
189 accHardware
= ACC_MMA8452
;
195 #ifdef USE_ACC_BMA280
196 case ACC_BMA280
: // BMA280
197 if (bma280Detect(dev
)) {
198 accHardware
= ACC_BMA280
;
204 #ifdef USE_ACC_SPI_MPU6000
206 if (mpu6000SpiAccDetect(dev
)) {
207 accHardware
= ACC_MPU6000
;
213 #ifdef USE_ACC_SPI_MPU9250
215 if (mpu9250SpiAccDetect(dev
)) {
216 accHardware
= ACC_MPU9250
;
226 #if defined(USE_ACC_MPU6500) || defined(USE_ACC_SPI_MPU6500)
227 #ifdef USE_ACC_SPI_MPU6500
228 if (mpu6500AccDetect(dev
) || mpu6500SpiAccDetect(dev
)) {
230 if (mpu6500AccDetect(dev
)) {
232 switch (dev
->mpuDetectionResult
.sensor
) {
234 accHardware
= ACC_MPU9250
;
237 accHardware
= ACC_ICM20601
;
240 accHardware
= ACC_ICM20602
;
243 accHardware
= ACC_ICM20608G
;
246 accHardware
= ACC_MPU6500
;
253 #ifdef USE_ACC_SPI_ICM20649
255 if (icm20649SpiAccDetect(dev
)) {
256 accHardware
= ACC_ICM20649
;
262 #ifdef USE_ACC_SPI_ICM20689
264 if (icm20689SpiAccDetect(dev
)) {
265 accHardware
= ACC_ICM20689
;
271 #if defined(USE_ACC_SPI_ICM42605) || defined(USE_ACC_SPI_ICM42688P)
274 if (icm426xxSpiAccDetect(dev
)) {
275 switch (dev
->mpuDetectionResult
.sensor
) {
277 accHardware
= ACC_ICM42605
;
280 accHardware
= ACC_ICM42688P
;
283 accHardware
= ACC_NONE
;
291 #ifdef USE_ACCGYRO_BMI160
293 if (bmi160SpiAccDetect(dev
)) {
294 accHardware
= ACC_BMI160
;
300 #ifdef USE_ACCGYRO_BMI270
302 if (bmi270SpiAccDetect(dev
)) {
303 accHardware
= ACC_BMI270
;
309 #ifdef USE_ACCGYRO_LSM6DSO
311 if (lsm6dsoSpiAccDetect(dev
)) {
312 accHardware
= ACC_LSM6DSO
;
320 if (fakeAccDetect(dev
)) {
321 accHardware
= ACC_FAKE
;
328 case ACC_NONE
: // disable ACC
329 accHardware
= ACC_NONE
;
333 // Found anything? Check if error or ACC is really missing.
334 if (accHardware
== ACC_NONE
&& accHardwareToUse
!= ACC_DEFAULT
&& accHardwareToUse
!= ACC_NONE
) {
335 // Nothing was found and we have a forced sensor that isn't present.
336 accHardwareToUse
= ACC_DEFAULT
;
340 if (accHardware
== ACC_NONE
) {
344 detectedSensors
[SENSOR_INDEX_ACC
] = accHardware
;
345 sensorsSet(SENSOR_ACC
);
349 void accInitFilters(void)
351 // Only set the lowpass cutoff if the ACC sample rate is detected otherwise
352 // the filter initialization is not defined (sample rate = 0)
353 accelerationRuntime
.accLpfCutHz
= (acc
.sampleRateHz
) ? accelerometerConfig()->acc_lpf_hz
: 0;
354 if (accelerationRuntime
.accLpfCutHz
) {
355 const float k
= pt2FilterGain(accelerationRuntime
.accLpfCutHz
, 1.0f
/ acc
.sampleRateHz
);
356 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
357 pt2FilterInit(&accelerationRuntime
.accFilter
[axis
], k
);
362 bool accInit(uint16_t accSampleRateHz
)
364 memset(&acc
, 0, sizeof(acc
));
365 // copy over the common gyro mpu settings
366 acc
.dev
.gyro
= gyroActiveDev();
367 acc
.dev
.mpuDetectionResult
= *gyroMpuDetectionResult();
368 acc
.dev
.acc_high_fsr
= accelerometerConfig()->acc_high_fsr
;
370 // Copy alignment from active gyro, as all production boards use acc-gyro-combi chip.
371 // Exception is STM32F411DISCOVERY, and (may be) handled in future enhancement.
373 sensor_align_e alignment
= gyroDeviceConfig(0)->alignment
;
374 const sensorAlignment_t
* customAlignment
= &gyroDeviceConfig(0)->customAlignment
;
376 #ifdef USE_MULTI_GYRO
377 if (gyroConfig()->gyro_to_use
== GYRO_CONFIG_USE_GYRO_2
) {
378 alignment
= gyroDeviceConfig(1)->alignment
;
380 customAlignment
= &gyroDeviceConfig(1)->customAlignment
;
383 acc
.dev
.accAlign
= alignment
;
384 buildRotationMatrixFromAlignment(customAlignment
, &acc
.dev
.rotationMatrix
);
386 if (!accDetect(&acc
.dev
, accelerometerConfig()->acc_hardware
)) {
389 acc
.dev
.acc_1G
= 256; // set default
390 acc
.dev
.initFn(&acc
.dev
); // driver initialisation
391 acc
.dev
.acc_1G_rec
= 1.0f
/ acc
.dev
.acc_1G
;
393 acc
.sampleRateHz
= accSampleRateHz
;
398 void accStartCalibration(void)
400 accelerationRuntime
.calibratingA
= CALIBRATING_ACC_CYCLES
;
403 bool accIsCalibrationComplete(void)
405 return accelerationRuntime
.calibratingA
== 0;
408 static bool isOnFinalAccelerationCalibrationCycle(void)
410 return accelerationRuntime
.calibratingA
== 1;
413 static bool isOnFirstAccelerationCalibrationCycle(void)
415 return accelerationRuntime
.calibratingA
== CALIBRATING_ACC_CYCLES
;
418 void performAcclerationCalibration(rollAndPitchTrims_t
*rollAndPitchTrims
)
422 for (int axis
= 0; axis
< 3; axis
++) {
424 // Reset a[axis] at start of calibration
425 if (isOnFirstAccelerationCalibrationCycle()) {
429 // Sum up CALIBRATING_ACC_CYCLES readings
430 a
[axis
] += acc
.accADC
[axis
];
432 // Reset global variables to prevent other code from using un-calibrated data
433 acc
.accADC
[axis
] = 0;
434 accelerationRuntime
.accelerationTrims
->raw
[axis
] = 0;
437 if (isOnFinalAccelerationCalibrationCycle()) {
438 // Calculate average, shift Z down by acc_1G and store values in EEPROM at end of calibration
439 accelerationRuntime
.accelerationTrims
->raw
[X
] = (a
[X
] + (CALIBRATING_ACC_CYCLES
/ 2)) / CALIBRATING_ACC_CYCLES
;
440 accelerationRuntime
.accelerationTrims
->raw
[Y
] = (a
[Y
] + (CALIBRATING_ACC_CYCLES
/ 2)) / CALIBRATING_ACC_CYCLES
;
441 accelerationRuntime
.accelerationTrims
->raw
[Z
] = (a
[Z
] + (CALIBRATING_ACC_CYCLES
/ 2)) / CALIBRATING_ACC_CYCLES
- acc
.dev
.acc_1G
;
443 resetRollAndPitchTrims(rollAndPitchTrims
);
444 setConfigCalibrationCompleted();
446 saveConfigAndNotify();
449 accelerationRuntime
.calibratingA
--;
452 void performInflightAccelerationCalibration(rollAndPitchTrims_t
*rollAndPitchTrims
)
455 static int16_t accZero_saved
[3] = { 0, 0, 0 };
456 static rollAndPitchTrims_t angleTrim_saved
= { { 0, 0 } };
458 // Saving old zeropoints before measurement
459 if (InflightcalibratingA
== 50) {
460 accZero_saved
[X
] = accelerationRuntime
.accelerationTrims
->raw
[X
];
461 accZero_saved
[Y
] = accelerationRuntime
.accelerationTrims
->raw
[Y
];
462 accZero_saved
[Z
] = accelerationRuntime
.accelerationTrims
->raw
[Z
];
463 angleTrim_saved
.values
.roll
= rollAndPitchTrims
->values
.roll
;
464 angleTrim_saved
.values
.pitch
= rollAndPitchTrims
->values
.pitch
;
466 if (InflightcalibratingA
> 0) {
467 for (int axis
= 0; axis
< 3; axis
++) {
468 // Reset a[axis] at start of calibration
469 if (InflightcalibratingA
== 50)
471 // Sum up 50 readings
472 b
[axis
] += acc
.accADC
[axis
];
473 // Clear global variables for next reading
474 acc
.accADC
[axis
] = 0;
475 accelerationRuntime
.accelerationTrims
->raw
[axis
] = 0;
477 // all values are measured
478 if (InflightcalibratingA
== 1) {
479 AccInflightCalibrationActive
= false;
480 AccInflightCalibrationMeasurementDone
= true;
481 beeper(BEEPER_ACC_CALIBRATION
); // indicate end of calibration
482 // recover saved values to maintain current flight behaviour until new values are transferred
483 accelerationRuntime
.accelerationTrims
->raw
[X
] = accZero_saved
[X
];
484 accelerationRuntime
.accelerationTrims
->raw
[Y
] = accZero_saved
[Y
];
485 accelerationRuntime
.accelerationTrims
->raw
[Z
] = accZero_saved
[Z
];
486 rollAndPitchTrims
->values
.roll
= angleTrim_saved
.values
.roll
;
487 rollAndPitchTrims
->values
.pitch
= angleTrim_saved
.values
.pitch
;
489 InflightcalibratingA
--;
491 // Calculate average, shift Z down by acc_1G and store values in EEPROM at end of calibration
492 if (AccInflightCalibrationSavetoEEProm
) { // the aircraft is landed, disarmed and the combo has been done again
493 AccInflightCalibrationSavetoEEProm
= false;
494 accelerationRuntime
.accelerationTrims
->raw
[X
] = b
[X
] / 50;
495 accelerationRuntime
.accelerationTrims
->raw
[Y
] = b
[Y
] / 50;
496 accelerationRuntime
.accelerationTrims
->raw
[Z
] = b
[Z
] / 50 - acc
.dev
.acc_1G
; // for nunchuck 200=1G
498 resetRollAndPitchTrims(rollAndPitchTrims
);
499 setConfigCalibrationCompleted();
501 saveConfigAndNotify();
505 void setAccelerationTrims(flightDynamicsTrims_t
*accelerationTrimsToUse
)
507 accelerationRuntime
.accelerationTrims
= accelerationTrimsToUse
;
510 void applyAccelerometerTrimsDelta(rollAndPitchTrims_t
*rollAndPitchTrimsDelta
)
512 accelerometerConfigMutable()->accelerometerTrims
.values
.roll
+= rollAndPitchTrimsDelta
->values
.roll
;
513 accelerometerConfigMutable()->accelerometerTrims
.values
.pitch
+= rollAndPitchTrimsDelta
->values
.pitch
;