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/>.
23 #include "common/maths.h"
25 #include "drivers/adc.h"
26 #include "drivers/system.h"
28 #include "config/runtime_config.h"
29 #include "config/config.h"
31 #include "sensors/battery.h"
35 #include "io/rc_controls.h"
36 #include "flight/lowpass.h"
37 #include "io/beeper.h"
39 #define VBATT_PRESENT_THRESHOLD_MV 10
40 #define VBATT_LPF_FREQ 10
42 // Battery monitoring stuff
43 uint8_t batteryCellCount
= 3; // cell count
44 uint16_t batteryWarningVoltage
;
45 uint16_t batteryCriticalVoltage
;
47 uint16_t vbat
= 0; // battery voltage in 0.1V steps (filtered)
48 uint16_t vbatLatestADC
= 0; // most recent unsmoothed raw reading from vbat ADC
49 uint16_t amperageLatestADC
= 0; // most recent raw reading from current ADC
51 int32_t amperage
= 0; // amperage read by current sensor in centiampere (1/100th A)
52 int32_t mAhDrawn
= 0; // milliampere hours drawn from the battery since start
54 batteryConfig_t
*batteryConfig
;
56 static batteryState_e batteryState
;
57 static lowpass_t lowpassFilter
;
59 uint16_t batteryAdcToVoltage(uint16_t src
)
61 // calculate battery voltage based on ADC reading
62 // result is Vbatt in 0.1V steps. 3.3V = ADC Vref, 0xFFF = 12bit adc, 110 = 11:1 voltage divider (10k:1k) * 10 for 0.1V
63 return ((((uint32_t)src
* batteryConfig
->vbatscale
* 33 + (0xFFF * 5)) / (0xFFF * batteryConfig
->vbatresdivval
))/batteryConfig
->vbatresdivmultiplier
);
66 static void updateBatteryVoltage(void)
69 uint16_t vbatFiltered
;
71 // store the battery voltage with some other recent battery voltage readings
72 vbatSample
= vbatLatestADC
= adcGetChannel(ADC_BATTERY
);
73 vbatFiltered
= (uint16_t)lowpassFixed(&lowpassFilter
, vbatSample
, VBATT_LPF_FREQ
);
74 vbat
= batteryAdcToVoltage(vbatFiltered
);
77 #define VBATTERY_STABLE_DELAY 40
78 /* Batt Hysteresis of +/-100mV */
79 #define VBATT_HYSTERESIS 1
81 void updateBattery(void)
83 updateBatteryVoltage();
85 /* battery has just been connected*/
86 if (batteryState
== BATTERY_NOT_PRESENT
&& vbat
> VBATT_PRESENT_THRESHOLD_MV
)
88 /* Actual battery state is calculated below, this is really BATTERY_PRESENT */
89 batteryState
= BATTERY_OK
;
90 /* wait for VBatt to stabilise then we can calc number of cells
91 (using the filtered value takes a long time to ramp up)
92 We only do this on the ground so don't care if we do block, not
93 worse than original code anyway*/
94 delay(VBATTERY_STABLE_DELAY
);
95 updateBatteryVoltage();
97 unsigned cells
= (batteryAdcToVoltage(vbatLatestADC
) / batteryConfig
->vbatmaxcellvoltage
) + 1;
99 // something is wrong, we expect 8 cells maximum (and autodetection will be problematic at 6+ cells)
102 batteryCellCount
= cells
;
103 batteryWarningVoltage
= batteryCellCount
* batteryConfig
->vbatwarningcellvoltage
;
104 batteryCriticalVoltage
= batteryCellCount
* batteryConfig
->vbatmincellvoltage
;
106 /* battery has been disconnected - can take a while for filter cap to disharge so we use a threshold of VBATT_PRESENT_THRESHOLD_MV */
107 else if (batteryState
!= BATTERY_NOT_PRESENT
&& vbat
<= VBATT_PRESENT_THRESHOLD_MV
)
109 batteryState
= BATTERY_NOT_PRESENT
;
110 batteryCellCount
= 0;
111 batteryWarningVoltage
= 0;
112 batteryCriticalVoltage
= 0;
118 if (vbat
<= (batteryWarningVoltage
- VBATT_HYSTERESIS
)) {
119 batteryState
= BATTERY_WARNING
;
120 beeper(BEEPER_BAT_LOW
);
123 case BATTERY_WARNING
:
124 if (vbat
<= (batteryCriticalVoltage
- VBATT_HYSTERESIS
)) {
125 batteryState
= BATTERY_CRITICAL
;
126 beeper(BEEPER_BAT_CRIT_LOW
);
127 } else if (vbat
> (batteryWarningVoltage
+ VBATT_HYSTERESIS
)){
128 batteryState
= BATTERY_OK
;
130 beeper(BEEPER_BAT_LOW
);
133 case BATTERY_CRITICAL
:
134 if (vbat
> (batteryCriticalVoltage
+ VBATT_HYSTERESIS
)){
135 batteryState
= BATTERY_WARNING
;
136 beeper(BEEPER_BAT_LOW
);
138 beeper(BEEPER_BAT_CRIT_LOW
);
141 case BATTERY_NOT_PRESENT
:
146 batteryState_e
getBatteryState(void)
151 const char * const batteryStateStrings
[] = {"OK", "WARNING", "CRITICAL", "NOT PRESENT"};
153 const char * getBatteryStateString(void)
155 return batteryStateStrings
[batteryState
];
158 void batteryInit(batteryConfig_t
*initialBatteryConfig
)
160 batteryConfig
= initialBatteryConfig
;
161 batteryState
= BATTERY_NOT_PRESENT
;
162 batteryCellCount
= 1;
163 batteryWarningVoltage
= 0;
164 batteryCriticalVoltage
= 0;
167 #define ADCVREF 3300 // in mV
168 int32_t currentSensorToCentiamps(uint16_t src
)
172 millivolts
= ((uint32_t)src
* ADCVREF
) / 4096;
173 millivolts
-= batteryConfig
->currentMeterOffset
;
175 return (millivolts
* 1000) / (int32_t)batteryConfig
->currentMeterScale
; // current in 0.01A steps
178 void updateCurrentMeter(int32_t lastUpdateAt
, rxConfig_t
*rxConfig
, uint16_t deadband3d_throttle
)
180 static int32_t amperageRaw
= 0;
181 static int64_t mAhdrawnRaw
= 0;
182 int32_t throttleFactor
= 0;
183 int32_t throttleOffset
= (int32_t)rcCommand
[THROTTLE
] - 1000;
185 switch(batteryConfig
->currentMeterType
) {
186 case CURRENT_SENSOR_ADC
:
187 amperageRaw
-= amperageRaw
/ 8;
188 amperageRaw
+= (amperageLatestADC
= adcGetChannel(ADC_CURRENT
));
189 amperage
= currentSensorToCentiamps(amperageRaw
/ 8);
191 case CURRENT_SENSOR_VIRTUAL
:
192 amperage
= (int32_t)batteryConfig
->currentMeterOffset
;
193 if (ARMING_FLAG(ARMED
)) {
194 throttleStatus_e throttleStatus
= calculateThrottleStatus(rxConfig
, deadband3d_throttle
);
195 if (throttleStatus
== THROTTLE_LOW
&& feature(FEATURE_MOTOR_STOP
))
197 throttleFactor
= throttleOffset
+ (throttleOffset
* throttleOffset
/ 50);
198 amperage
+= throttleFactor
* (int32_t)batteryConfig
->currentMeterScale
/ 1000;
201 case CURRENT_SENSOR_NONE
:
206 mAhdrawnRaw
+= (amperage
* lastUpdateAt
) / 1000;
207 mAhDrawn
= mAhdrawnRaw
/ (3600 * 100);
210 uint8_t calculateBatteryPercentage(void)
212 return (((uint32_t)vbat
- (batteryConfig
->vbatmincellvoltage
* batteryCellCount
)) * 100) / ((batteryConfig
->vbatmaxcellvoltage
- batteryConfig
->vbatmincellvoltage
) * batteryCellCount
);
215 uint8_t calculateBatteryCapacityRemainingPercentage(void)
217 uint16_t batteryCapacity
= batteryConfig
->batteryCapacity
;
219 return constrain((batteryCapacity
- constrain(mAhDrawn
, 0, 0xFFFF)) * 100.0f
/ batteryCapacity
, 0, 100);