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/>.
24 #include "common/axis.h"
25 #include "common/maths.h"
26 #include "common/filter.h"
28 #include "drivers/sensor.h"
30 #include "sensors/battery.h"
31 #include "sensors/sensors.h"
32 #include "io/beeper.h"
33 #include "sensors/boardalignment.h"
34 #include "fc/runtime_config.h"
36 #include "config/config.h"
38 #include "sensors/acceleration.h"
40 acc_t acc
; // acc access functions
41 int32_t accADC
[XYZ_AXIS_COUNT
];
42 sensor_align_e accAlign
= 0;
43 uint32_t accTargetLooptime
;
45 static uint16_t calibratingA
= 0; // the calibration is done is the main loop. Calibrating decreases at each cycle down to 0, then we enter in a normal mode.
46 static int16_t accADCRaw
[XYZ_AXIS_COUNT
];
48 static flightDynamicsTrims_t
* accZero
;
49 static flightDynamicsTrims_t
* accGain
;
51 static uint8_t accLpfCutHz
= 0;
52 static biquadFilter_t accFilter
[XYZ_AXIS_COUNT
];
54 void accInit(uint32_t targetLooptime
)
56 accTargetLooptime
= targetLooptime
;
58 for (int axis
= 0; axis
< 3; axis
++) {
59 biquadFilterInitLPF(&accFilter
[axis
], accLpfCutHz
, accTargetLooptime
);
64 void accSetCalibrationCycles(uint16_t calibrationCyclesRequired
)
66 calibratingA
= calibrationCyclesRequired
;
69 bool isAccelerationCalibrationComplete(void)
71 return calibratingA
== 0;
74 bool isOnFinalAccelerationCalibrationCycle(void)
76 return calibratingA
== 1;
79 bool isOnFirstAccelerationCalibrationCycle(void)
81 return calibratingA
== CALIBRATING_ACC_CYCLES
;
84 static sensorCalibrationState_t calState
;
85 static bool calibratedAxis
[6];
86 static int32_t accSamples
[6][3];
87 static int calibratedAxisCount
= 0;
89 int getPrimaryAxisIndex(int32_t sample
[3])
91 if (ABS(sample
[Z
]) > ABS(sample
[X
]) && ABS(sample
[Z
]) > ABS(sample
[Y
])) {
93 return (sample
[Z
] > 0) ? 0 : 1;
95 else if (ABS(sample
[X
]) > ABS(sample
[Y
]) && ABS(sample
[X
]) > ABS(sample
[Z
])) {
97 return (sample
[X
] > 0) ? 2 : 3;
99 else if (ABS(sample
[Y
]) > ABS(sample
[X
]) && ABS(sample
[Y
]) > ABS(sample
[Z
])) {
101 return (sample
[Y
] > 0) ? 4 : 5;
107 void performAcclerationCalibration(void)
109 int axisIndex
= getPrimaryAxisIndex(accADC
);
111 // Check if sample is usable
116 // Top-up and first calibration cycle, reset everything
117 if (axisIndex
== 0 && isOnFirstAccelerationCalibrationCycle()) {
118 for (int axis
= 0; axis
< 6; axis
++) {
119 calibratedAxis
[axis
] = false;
120 accSamples
[axis
][X
] = 0;
121 accSamples
[axis
][Y
] = 0;
122 accSamples
[axis
][Z
] = 0;
125 calibratedAxisCount
= 0;
126 sensorCalibrationResetState(&calState
);
129 if (!calibratedAxis
[axisIndex
]) {
130 sensorCalibrationPushSampleForOffsetCalculation(&calState
, accADC
);
131 accSamples
[axisIndex
][X
] += accADC
[X
];
132 accSamples
[axisIndex
][Y
] += accADC
[Y
];
133 accSamples
[axisIndex
][Z
] += accADC
[Z
];
135 if (isOnFinalAccelerationCalibrationCycle()) {
136 calibratedAxis
[axisIndex
] = true;
137 calibratedAxisCount
++;
139 beeperConfirmationBeeps(2);
143 if (calibratedAxisCount
== 6) {
145 int32_t accSample
[3];
147 /* Calculate offset */
148 sensorCalibrationSolveForOffset(&calState
, accTmp
);
150 for (int axis
= 0; axis
< 3; axis
++) {
151 accZero
->raw
[axis
] = lrintf(accTmp
[axis
]);
154 /* Not we can offset our accumulated averages samples and calculate scale factors and calculate gains */
155 sensorCalibrationResetState(&calState
);
157 for (int axis
= 0; axis
< 6; axis
++) {
158 accSample
[X
] = accSamples
[axis
][X
] / CALIBRATING_ACC_CYCLES
- accZero
->raw
[X
];
159 accSample
[Y
] = accSamples
[axis
][Y
] / CALIBRATING_ACC_CYCLES
- accZero
->raw
[Y
];
160 accSample
[Z
] = accSamples
[axis
][Z
] / CALIBRATING_ACC_CYCLES
- accZero
->raw
[Z
];
162 sensorCalibrationPushSampleForScaleCalculation(&calState
, axis
/ 2, accSample
, acc
.acc_1G
);
165 sensorCalibrationSolveForScale(&calState
, accTmp
);
167 for (int axis
= 0; axis
< 3; axis
++) {
168 accGain
->raw
[axis
] = lrintf(accTmp
[axis
] * 4096);
171 saveConfigAndNotify();
177 static void applyAccelerationZero(const flightDynamicsTrims_t
* accZero
, const flightDynamicsTrims_t
* accGain
)
179 accADC
[X
] = (accADC
[X
] - accZero
->raw
[X
]) * accGain
->raw
[X
] / 4096;
180 accADC
[Y
] = (accADC
[Y
] - accZero
->raw
[Y
]) * accGain
->raw
[Y
] / 4096;
181 accADC
[Z
] = (accADC
[Z
] - accZero
->raw
[Z
]) * accGain
->raw
[Z
] / 4096;
184 void updateAccelerationReadings(void)
186 if (!acc
.read(accADCRaw
)) {
190 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
191 accADC
[axis
] = accADCRaw
[axis
];
195 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
196 accADC
[axis
] = lrintf(biquadFilterApply(&accFilter
[axis
], (float) accADC
[axis
]));
200 if (!isAccelerationCalibrationComplete()) {
201 performAcclerationCalibration();
204 applyAccelerationZero(accZero
, accGain
);
206 alignSensors(accADC
, accAlign
);
209 void setAccelerationZero(flightDynamicsTrims_t
* accZeroToUse
)
211 accZero
= accZeroToUse
;
214 void setAccelerationGain(flightDynamicsTrims_t
* accGainToUse
)
216 accGain
= accGainToUse
;
219 void setAccelerationFilter(uint8_t initialAccLpfCutHz
)
221 accLpfCutHz
= initialAccLpfCutHz
;
222 if (accTargetLooptime
) {
223 for (int axis
= 0; axis
< XYZ_AXIS_COUNT
; axis
++) {
224 biquadFilterInitLPF(&accFilter
[axis
], accLpfCutHz
, accTargetLooptime
);