]> www.fi.muni.cz Git - tinyboard.git/blob - projects/step-up/adc.c
battery gauge
[tinyboard.git] / projects / step-up / adc.c
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3 #include <avr/power.h>
4 #include <avr/sleep.h>
5
6 #include "lights.h"
7
8 #define ZERO_ADC    2
9
10 //#define NUM_ADCS      ZERO_ADC
11 #define NUM_ADCS        2
12
13 volatile static unsigned char current_adc;;
14 static unsigned char need_battery_adc;
15 static uint16_t adc_sum, read_zero, drop_count, read_count, n_reads_log;
16 volatile uint16_t jiffies;
17
18 static void setup_mux(unsigned char n)
19 {
20         /* ADC numbering: PWM LEDs first, then others, zero at the end */
21         switch (n) {
22         case 0: // pwmled 1: 1.1V, ADC3 (PB3), single-ended
23                 ADMUX = _BV(REFS1) | _BV(MUX1) | _BV(MUX0);
24                 break;
25         case 1: // battery voltage: 1.1V, ADC1 (PB2), single-ended
26                 ADMUX = _BV(REFS1) | _BV(MUX0);
27                 break;
28         case ZERO_ADC: // zero: 1.1V, GND, single-ended
29                 ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX0);
30                 break;
31         }
32 }
33
34 void start_next_adc()
35 {
36         if (need_battery_adc) {
37                 need_battery_adc = 0;
38                 current_adc = 1;
39                 read_zero = 1;
40                 drop_count = 1;
41                 read_count = 1;
42                 n_reads_log = 0;
43         } else {
44                 current_adc = 0;
45                 read_zero = 0;
46                 drop_count = 1;
47                 read_count = 1 << PWMLED_ADC_SHIFT;
48                 n_reads_log = PWMLED_ADC_SHIFT;
49
50         }
51
52         adc_sum = 0;
53
54         // set up mux, start one-shot conversion
55         if (read_zero)
56                 setup_mux(ZERO_ADC);
57         else
58                 setup_mux(current_adc);
59
60         ADCSRA |= _BV(ADSC);
61 }
62
63 #if 0
64 void timer_start_slow_adcs()
65 {
66         if (current_slow_adc > N_PWMLEDS) { // Don't start if in progress
67                 log_byte(0x80 + current_slow_adc);
68         } else {
69                 current_slow_adc = NUM_ADCS;
70                 // TODO: kick the watchdog here
71         }
72 }
73 #endif
74
75 /*
76  * Single synchronous ADC conversion.
77  * Has to be called with IRQs disabled (or with the ADC IRQ disabled).
78  */
79 static uint16_t read_adc_sync()
80 {
81         uint16_t rv;
82
83         ADCSRA |= _BV(ADSC); // start the conversion
84
85         // wait for the conversion to finish
86         while((ADCSRA & _BV(ADIF)) == 0)
87                 ;
88
89         rv = ADCW;
90         ADCSRA |= _BV(ADIF); // clear the IRQ flag
91
92         return rv;
93 }
94
95 void init_adc()
96 {
97         need_battery_adc = 0;
98         current_adc = 0;
99
100         power_adc_enable();
101         ACSR |= _BV(ACD);       // but disable the analog comparator
102
103         ADCSRA = _BV(ADEN)                      // enable
104                 | _BV(ADPS1) | _BV(ADPS0)       // CLK/8 = 125 kHz
105                 // | _BV(ADPS2)                 // CLK/16 = 62.5 kHz
106                 ;
107         // ADCSRB |= _BV(GSEL); // gain 8 or 32
108
109         // Disable digital input on all bits used by ADC
110         DIDR0 = _BV(ADC3D) | _BV(ADC2D);
111         
112         // 1.1V, GND
113         ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX0);
114
115         /* Do first conversion and drop the result */
116         read_adc_sync();
117
118         ADCSRA |= _BV(ADIE); // enable IRQ
119
120         start_next_adc();
121 }
122
123 #if 0
124 void susp_adc()
125 {
126         ADCSRA = 0;
127         DIDR0 = 0;
128 }
129
130 static void adc1_gain20_adc(uint16_t adcsum)
131 {
132         // running average
133         adc1_gain20_offset += adcsum
134                         - (adc1_gain20_offset >> ADC1_GAIN20_OFFSET_SHIFT);
135 }
136 #endif
137
138 static void inline adc_based_timer()
139 {
140         static unsigned char count;
141
142         if (++count < 40) // about 100 Hz jiffies
143                 return;
144
145         count = 0;
146         ++jiffies;
147
148         if ((jiffies & 0x007F) == 1) { // about every 1s
149                 need_battery_adc = 1;
150         }
151         if ((jiffies & 0x0007) == 0) {
152                 patterns_next_tick();
153         }
154         timer_check_buttons();
155 }
156
157 ISR(ADC_vect) { // IRQ handler
158         uint16_t adcval = ADCW;
159
160         adc_based_timer();
161
162         if (read_zero) {
163                 setup_mux(current_adc);
164                 read_zero = 0;
165                 ADCSRA |= _BV(ADSC); // drop this one, start the next
166                 return;
167         }
168
169         if (drop_count) {
170                 ADCSRA |= _BV(ADSC); // drop this one, start the next
171                 drop_count--;
172                 return;
173         }
174
175         if (read_count) {
176                 ADCSRA |= _BV(ADSC); // immediately start the next conversion
177                 adc_sum += adcval;
178                 read_count--;
179                 return;
180         }
181
182         /*
183          * Now we have performed read_count measurements and have them
184          * in adc_sum.
185          */
186         switch (current_adc) {
187         case 0:
188                 // pwmled_adc(current_adc, adc_sum);
189                 pwmled_adc(adc_sum);
190                 break;
191         case 1:
192                 battery_adc(adc_sum);
193                 break;
194         }
195
196         start_next_adc();
197 }