#include #include "lights.h" #define BATTERY_ADC_SHIFT 6 #define RESISTOR_HI 1500 // kOhm #define RESISTOR_LO 100 // kOhm /* * The internal 1.1V reference has tolerance from 1.0 to 1.2V * (datasheet, section 19.6). We have to measure the actual value * of our part. */ #define AREF_1100MV 1060 // mV static volatile uint16_t battery_adcval; volatile unsigned char battery_critical; void init_battery() { 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 * multiply adcval by as big number as possible to fit uint16_t, * then divide to get the final value, * and finally type-cast it to unsigned char. * We don't do running average, as the required precision * is coarse (0.1 V). */ 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) AREF_1100MV \ * ((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; }