#include "lights.h"
+#define BATTERY_ADC_SHIFT 6
#define RESISTOR_HI 1500 // kOhm
#define RESISTOR_LO 100 // kOhm
-volatile unsigned char battery_100mv = 0;
+static volatile uint16_t battery_adcval;
+volatile unsigned char battery_critical;
void init_battery()
{
- battery_100mv = 0;
+ battery_adcval = 0;
+ battery_critical = 0;
}
void battery_adc(uint16_t adcval)
+{
+ if (battery_adcval == 0) {
+ battery_adcval = adcval << BATTERY_ADC_SHIFT;
+ } else { // running average
+ battery_adcval += (adcval
+ - (battery_adcval >> BATTERY_ADC_SHIFT));
+ }
+}
+
+unsigned char battery_100mv()
{
/*
* This is tricky: we need to maintain precision, so we first
* We don't do running average, as the required precision
* is coarse (0.1 V).
*/
- battery_100mv = (unsigned char)
- ((uint16_t)(adcval * (11 // 1.1V
+ return (unsigned char)
+ ((uint16_t)(
+ (battery_adcval >> BATTERY_ADC_SHIFT)
+ * (11 // 1.1V
* (RESISTOR_HI+RESISTOR_LO)/RESISTOR_LO // resistor ratio
/ 4)) >> 8); // divide by 1024
}
+/* convert value in mV to value of ADC shifted by BATTERY_ADC_SHIFT */
+#define MV_TO_ADC(x) \
+ ((uint16_t)(((uint32_t)(x) * 1024 * (1 << BATTERY_ADC_SHIFT)) \
+ / ((uint32_t) 1100 \
+ * ((RESISTOR_HI+RESISTOR_LO)/RESISTOR_LO))))
+
+/* convert value in mV to upper 8 bits of ADC value << BATTERY_ADC_SHIFT */
+#define MV_TO_ADC8(x) ((unsigned char)(MV_TO_ADC(x) >> 8))
+
+/*
+ * Returns number from 1 to 10 (1 = battery almost empty, 10 = full)
+ * Lithium cells have voltage from about 2.9V to 4.1V. We consider battery
+ * above 4V full, and under 3.2V critically low. We guess whether
+ * the battery has two or three cells - voltages above 8.6V are considered
+ * from three-cell battery.
+ */
+unsigned char battery_gauge()
+{
+ unsigned char b8 = battery_adcval >> 8;
+ unsigned char rv;
+
+ if (b8 < MV_TO_ADC8(2 * 3200)) {
+ rv = 1;
+ } else if (b8 < MV_TO_ADC8(2 * 3475)) {
+ rv = 2;
+ } else if (b8 < MV_TO_ADC8(2 * 3577)) {
+ rv = 3;
+ } else if (b8 < MV_TO_ADC8(2 * 3620)) {
+ rv = 4;
+ } else if (b8 < MV_TO_ADC8(2 * 3741)) {
+ rv = 5;
+ } else if (b8 < MV_TO_ADC8(2 * 3775)) {
+ rv = 6;
+ } else if (b8 < MV_TO_ADC8(2 * 3844)) {
+ rv = 7;
+ } else if (b8 < MV_TO_ADC8(2 * 3930)) {
+ rv = 8;
+ } else if (b8 < MV_TO_ADC8(2 * 4000)) {
+ rv = 9;
+ } else if (b8 < MV_TO_ADC8(2 * 4300)) {
+ rv = 10;
+ } else if (b8 < MV_TO_ADC8(3 * 3200)) { // three-cell battery
+ rv = 1;
+ } else if (b8 < MV_TO_ADC8(3 * 3475)) {
+ rv = 2;
+ } else if (b8 < MV_TO_ADC8(3 * 3577)) {
+ rv = 3;
+ } else if (b8 < MV_TO_ADC8(3 * 3620)) {
+ rv = 4;
+ } else if (b8 < MV_TO_ADC8(3 * 3741)) {
+ rv = 5;
+ } else if (b8 < MV_TO_ADC8(3 * 3775)) {
+ rv = 6;
+ } else if (b8 < MV_TO_ADC8(3 * 3844)) {
+ rv = 7;
+ } else if (b8 < MV_TO_ADC8(3 * 3930)) {
+ rv = 8;
+ } else if (b8 < MV_TO_ADC8(3 * 4000)) {
+ rv = 9;
+ } else {
+ rv = 10;
+ }
+
+ if (rv == 1)
+ battery_critical = 1;
+
+#if 0
+ log_byte(0xbb);
+ log_byte(rv);
+ log_flush();
+#endif
+
+ return rv;
+}