]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/adc.c
Timer-initiated ADC
[bike-lights.git] / firmware / adc.c
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3
4 #include "lights.h"
5
6 #define AMBIENT_ADC N_PWMLEDS
7 #define BATTERY_ADC (N_PWMLEDS + 1)
8 #define ADC1_GAIN20 (N_PWMLEDS + 2)
9 #define BUTTON_ADC  (N_PWMLEDS + 3)
10 #define ZERO_ADC    (N_PWMLEDS + 4)
11
12 #define NUM_ADCS        ZERO_ADC
13
14 struct {
15         unsigned char read_zero_log : 2;
16         unsigned char read_drop_log : 2;
17         unsigned char read_keep_log : 4;
18 } adc_params[NUM_ADCS] = {
19         { 0, 1, PWMLED_ADC_SHIFT },     // pwmled 1
20         { 0, 1, PWMLED_ADC_SHIFT },     // pwmled 2
21         { 0, 1, PWMLED_ADC_SHIFT },     // pwmled 3
22         { 0, 1, 3 },                    // ambient
23         { 0, 1, 0 },                    // battery
24         { 0, 1, 0 },                    // gain20
25         { 0, 1, 0 },                    // buttons
26 };
27
28 volatile static unsigned char current_adc;
29 static uint16_t adc_sum, zero_count, drop_count, read_count, n_reads_log;
30 #define ADC1_GAIN20_OFFSET_SHIFT        6
31 static uint16_t adc1_gain20_offset;
32
33
34 static void setup_mux(unsigned char n)
35 {
36         /* ADC numbering: PWM LEDs first, then others, zero at the end */
37         switch (n) {
38         case 0: // pwmled 1: 1.1V, ADC0,1 (PA0,1), gain 20
39                 ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX1) | _BV(MUX0);
40                 break;
41         case 1: // pwmled 2: 1.1V, ADC2,1 (PA2,1), gain 20
42                 ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
43                 break;
44         case 2: // pwmled 3: 1.1V, ADC4 (PA5), single-ended
45                 ADMUX = _BV(REFS1) | _BV(MUX2);
46                 break;
47         case AMBIENT_ADC: // ambient light: 1.1V, ADC5 (PA6), single-ended
48                 ADMUX = _BV(REFS1) | _BV(MUX2) | _BV(MUX0);
49                 break;
50         case BATTERY_ADC: // batt voltage: 1.1V, ADC6 (PA7), single-ended
51                 ADMUX = _BV(REFS1) | _BV(MUX2) | _BV(MUX1);
52                 break;
53         case ADC1_GAIN20: // gain stage offset: 1.1V, ADC1,1, gain 20
54                 ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX0);
55                 break;
56         case BUTTON_ADC: // buttons: 1.1V, ADC3, single-ended
57                 PORTA |= _BV(PA3); // +5V to the voltage splitter
58                 ADMUX = _BV(REFS1) | _BV(MUX1) | _BV(MUX0);
59                 break;
60         case ZERO_ADC: // zero: 1.1V, ADC1 (PA1), single-ended
61                 ADMUX = _BV(REFS1) | _BV(MUX0);
62                 break;
63         }
64 }
65
66 static void start_next_adc()
67 {
68         if (current_adc > 0) {
69                 current_adc--;
70         } else {
71                 // TODO: kick the watchdog here.
72                 current_adc = NUM_ADCS;
73                 return;
74         }
75
76         adc_sum = 0;
77         // we use the last iteration of zero_count to set up the MUX
78         // to its final destination, hence the "1 +" below:
79         if (adc_params[current_adc].read_zero_log)
80                 zero_count = 1 + (1 << (adc_params[current_adc].read_zero_log-1));
81         else
82                 zero_count = 1;
83
84         if (adc_params[current_adc].read_drop_log)
85                 drop_count = 1 << (adc_params[current_adc].read_drop_log - 1);
86         else
87                 drop_count = 0;
88
89         read_count = 1 << adc_params[current_adc].read_keep_log;
90         n_reads_log = adc_params[current_adc].read_keep_log;
91
92         // set up mux, start one-shot conversion
93         if (zero_count > 1)
94                 setup_mux(ZERO_ADC);
95         else
96                 setup_mux(current_adc);
97
98         ADCSRA |= _BV(ADSC);
99 }
100
101 void timer_start_adcs()
102 {
103         if (current_adc == NUM_ADCS) // Don't start if in progress
104                 start_next_adc();
105         else
106                 log_byte(0x99); // overrun
107 }
108
109 /*
110  * Single synchronous ADC conversion.
111  * Has to be called with IRQs disabled (or with the ADC IRQ disabled).
112  */
113 static uint16_t read_adc_sync()
114 {
115         uint16_t rv;
116
117         ADCSRA |= _BV(ADSC); // start the conversion
118
119         // wait for the conversion to finish
120         while((ADCSRA & _BV(ADIF)) == 0)
121                 ;
122
123         rv = ADCW;
124         ADCSRA |= _BV(ADIF); // clear the IRQ flag
125
126         return rv;
127 }
128
129 void init_adc()
130 {
131         unsigned char i;
132         current_adc = NUM_ADCS;
133
134         ADCSRA = _BV(ADEN)                      // enable
135                 | _BV(ADPS1) | _BV(ADPS0)       // CLK/8 = 125 kHz
136                 // | _BV(ADPS2)                 // CLK/16 = 62.5 kHz
137                 ;
138         // ADCSRB |= _BV(GSEL); // gain 8 or 32
139
140         // Disable digital input on all bits used by ADC
141         DIDR0 = _BV(ADC0D) | _BV(ADC1D) | _BV(ADC2D) | _BV(ADC3D)
142                 | _BV(ADC4D) | _BV(ADC5D) | _BV(ADC6D);
143
144         // 1.1V, ADC1,1, gain 20
145         ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX0);
146
147         /* Do first conversion and drop the result */
148         read_adc_sync();
149
150         adc1_gain20_offset = 0;
151
152         for (i = 0; i < (1 << ADC1_GAIN20_OFFSET_SHIFT); i++) {
153                 adc1_gain20_offset += read_adc_sync()
154                         - (adc1_gain20_offset >> ADC1_GAIN20_OFFSET_SHIFT);
155         }
156
157         ADCSRA |= _BV(ADIE); // enable IRQ
158
159         start_next_adc();
160 }
161
162 void susp_adc()
163 {
164         ADCSRA = 0;
165         DIDR0 = 0;
166 }
167
168 static void adc1_gain20_adc(uint16_t adcsum)
169 {
170         // running average
171         adc1_gain20_offset += adcsum
172                         - (adc1_gain20_offset >> ADC1_GAIN20_OFFSET_SHIFT);
173 }
174
175 ISR(ADC_vect) { // IRQ handler
176         uint16_t adcval = ADCW;
177
178         if (zero_count) {
179                 if (zero_count > 1) {
180                         ADCSRA |= _BV(ADSC);
181                         zero_count--;
182                         return;
183                 } else {
184                         setup_mux(current_adc);
185                         zero_count = 0;
186                         /* fall through */
187                 }
188         }
189
190         if (drop_count) {
191                 ADCSRA |= _BV(ADSC); // drop this one, start the next
192                 drop_count--;
193                 return;
194         }
195
196         if (read_count) {
197                 ADCSRA |= _BV(ADSC);
198                 adc_sum += adcval;
199                 read_count--;
200                 return;
201         }
202
203         /*
204          * Now we have performed read_count measurements and have them
205          * in adc_sum.
206          */
207
208         // For inputs with gain, subtract the measured gain stage offset
209         if (current_adc < 2) {
210                 uint16_t offset = adc1_gain20_offset
211                         >> (ADC1_GAIN20_OFFSET_SHIFT - n_reads_log);
212
213                 if (adc_sum > offset)
214                         adc_sum -= offset;
215                 else
216                         adc_sum = 0;
217         }
218
219         switch (current_adc) {
220         case 0:
221         case 1:
222         case 2:
223                 pwmled_adc(current_adc, adc_sum);
224                 break;
225         case AMBIENT_ADC:
226                 ambient_adc(adc_sum);
227                 break;
228         case BATTERY_ADC:
229                 battery_adc(adc_sum);
230                 break;
231         case BUTTON_ADC:
232                 button_adc(adc_sum);
233                 break;
234         case ADC1_GAIN20:
235                 adc1_gain20_adc(adcval);
236                 break;
237         }
238
239         start_next_adc();
240 }
241