]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/battery.c
192acf04e40502fd3097ed2425ee9c8288bc0d31
[bike-lights.git] / firmware / battery.c
1 #include <avr/io.h>
2
3 #include "lights.h"
4
5 #define BATTERY_ADC_SHIFT       6
6 #define RESISTOR_HI     1500    // kOhm
7 #define RESISTOR_LO      100    // kOhm
8 /*
9  * The internal 1.1V reference has tolerance from 1.0 to 1.2V
10  * (datasheet, section 19.6). We have to measure the actual value
11  * of our part.
12  */
13 #define AREF_1100MV     1060    // mV
14
15 static volatile uint16_t battery_adcval;
16 volatile unsigned char battery_critical;
17
18 void init_battery()
19 {
20         battery_adcval = 0;
21         battery_critical = 0;
22 }
23
24 void battery_adc(uint16_t adcval)
25 {
26         if (battery_adcval == 0) {
27                 battery_adcval = adcval << BATTERY_ADC_SHIFT;
28         } else { // running average
29                 battery_adcval += (adcval
30                         - (battery_adcval >> BATTERY_ADC_SHIFT));
31         }
32 }
33
34 unsigned char battery_100mv()
35 {
36         /*
37          * This is tricky: we need to maintain precision, so we first
38          * multiply adcval by as big number as possible to fit uint16_t,
39          * then divide to get the final value,
40          * and finally type-cast it to unsigned char.
41          * We don't do running average, as the required precision
42          * is coarse (0.1 V).
43          */
44         return (unsigned char)
45                 ((uint16_t)(
46                 (battery_adcval >> BATTERY_ADC_SHIFT)
47                 * (11                                      // 1.1V
48                 * (RESISTOR_HI+RESISTOR_LO)/RESISTOR_LO    // resistor ratio
49                 / 4)) >> 8);                               // divide by 1024
50 }
51
52 /* convert value in mV to value of ADC shifted by BATTERY_ADC_SHIFT */
53 #define MV_TO_ADC(x) \
54         ((uint16_t)(((uint32_t)(x) * 1024 * (1 << BATTERY_ADC_SHIFT)) \
55                 / ((uint32_t) AREF_1100MV \
56                         * ((RESISTOR_HI+RESISTOR_LO)/RESISTOR_LO))))
57
58 /* convert value in mV to upper 8 bits of ADC value << BATTERY_ADC_SHIFT */
59 #define MV_TO_ADC8(x)   ((unsigned char)(MV_TO_ADC(x) >> 8))
60
61 /*
62  * Returns number from 1 to 10 (1 = battery almost empty, 10 = full)
63  * Lithium cells have voltage from about 2.9V to 4.1V. We consider battery
64  * above 4V full, and under 3.2V critically low. We guess whether
65  * the battery has two or three cells - voltages above 8.6V are considered
66  * from three-cell battery.
67  */
68 unsigned char battery_gauge()
69 {
70         unsigned char b8 = battery_adcval >> 8;
71         unsigned char rv;
72
73         if        (b8 < MV_TO_ADC8(2 * 3200)) {
74                 rv = 1;
75         } else if (b8 < MV_TO_ADC8(2 * 3475)) {
76                 rv = 2;
77         } else if (b8 < MV_TO_ADC8(2 * 3577)) {
78                 rv = 3;
79         } else if (b8 < MV_TO_ADC8(2 * 3620)) {
80                 rv = 4;
81         } else if (b8 < MV_TO_ADC8(2 * 3741)) {
82                 rv = 5;
83         } else if (b8 < MV_TO_ADC8(2 * 3775)) {
84                 rv = 6;
85         } else if (b8 < MV_TO_ADC8(2 * 3844)) {
86                 rv = 7;
87         } else if (b8 < MV_TO_ADC8(2 * 3930)) {
88                 rv = 8;
89         } else if (b8 < MV_TO_ADC8(2 * 4000)) {
90                 rv = 9;
91         } else if (b8 < MV_TO_ADC8(2 * 4300)) {
92                 rv = 10;
93         } else if (b8 < MV_TO_ADC8(3 * 3200)) { // three-cell battery
94                 rv = 1;
95         } else if (b8 < MV_TO_ADC8(3 * 3475)) {
96                 rv = 2;
97         } else if (b8 < MV_TO_ADC8(3 * 3577)) {
98                 rv = 3;
99         } else if (b8 < MV_TO_ADC8(3 * 3620)) {
100                 rv = 4;
101         } else if (b8 < MV_TO_ADC8(3 * 3741)) {
102                 rv = 5;
103         } else if (b8 < MV_TO_ADC8(3 * 3775)) {
104                 rv = 6;
105         } else if (b8 < MV_TO_ADC8(3 * 3844)) {
106                 rv = 7;
107         } else if (b8 < MV_TO_ADC8(3 * 3930)) {
108                 rv = 8;
109         } else if (b8 < MV_TO_ADC8(3 * 4000)) {
110                 rv = 9;
111         } else {
112                 rv = 10;
113         }
114
115         if (rv == 1)
116                 battery_critical = 1;
117
118 #if 0
119         log_byte(0xbb);
120         log_byte(rv);
121         log_flush();
122 #endif
123
124         return rv;
125 }