]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/adc.c
adc.c: configurable number of pre-readings
[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-1;
73
74         adc_sum = 0;
75         // we use the last iteration of zero_count to set up the MUX
76         // to its final destination, hence the "1 +" below:
77         if (adc_params[current_adc].read_zero_log)
78                 zero_count = 1 + (1 << (adc_params[current_adc].read_zero_log-1));
79         else
80                 zero_count = 1;
81
82         if (adc_params[current_adc].read_drop_log)
83                 drop_count = 1 << (adc_params[current_adc].read_drop_log - 1);
84         else
85                 drop_count = 0;
86
87         read_count = 1 << adc_params[current_adc].read_keep_log;
88         n_reads_log = adc_params[current_adc].read_keep_log;
89
90         // set up mux, start one-shot conversion
91         if (zero_count > 1)
92                 setup_mux(ZERO_ADC);
93         else
94                 setup_mux(current_adc);
95
96         ADCSRA |= _BV(ADSC);
97 }
98
99 /*
100  * Single synchronous ADC conversion.
101  * Has to be called with IRQs disabled (or with the ADC IRQ disabled).
102  */
103 static uint16_t read_adc_sync()
104 {
105         uint16_t rv;
106
107         ADCSRA |= _BV(ADSC); // start the conversion
108
109         // wait for the conversion to finish
110         while((ADCSRA & _BV(ADIF)) == 0)
111                 ;
112
113         rv = ADCW;
114         ADCSRA |= _BV(ADIF); // clear the IRQ flag
115
116         return rv;
117 }
118
119 void init_adc()
120 {
121         unsigned char i;
122         current_adc = NUM_ADCS;
123
124         ADCSRA = _BV(ADEN)                      // enable
125                 | _BV(ADPS1) | _BV(ADPS0)       // CLK/8 = 125 kHz
126                 // | _BV(ADPS2)                 // CLK/16 = 62.5 kHz
127                 ;
128         // ADCSRB |= _BV(GSEL); // gain 8 or 32
129
130         // Disable digital input on all bits used by ADC
131         DIDR0 = _BV(ADC0D) | _BV(ADC1D) | _BV(ADC2D) | _BV(ADC3D)
132                 | _BV(ADC4D) | _BV(ADC5D) | _BV(ADC6D);
133
134         // 1.1V, ADC1,1, gain 20
135         ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX0);
136
137         /* Do first conversion and drop the result */
138         read_adc_sync();
139
140         adc1_gain20_offset = 0;
141
142         for (i = 0; i < (1 << ADC1_GAIN20_OFFSET_SHIFT); i++) {
143                 adc1_gain20_offset += read_adc_sync()
144                         - (adc1_gain20_offset >> ADC1_GAIN20_OFFSET_SHIFT);
145         }
146
147         ADCSRA |= _BV(ADIE); // enable IRQ
148
149         start_next_adc();
150 }
151
152 void susp_adc()
153 {
154         ADCSRA = 0;
155         DIDR0 = 0;
156 }
157
158 static void adc1_gain20_adc(uint16_t adcsum)
159 {
160         // running average
161         adc1_gain20_offset += adcsum
162                         - (adc1_gain20_offset >> ADC1_GAIN20_OFFSET_SHIFT);
163 }
164
165 ISR(ADC_vect) { // IRQ handler
166         uint16_t adcval = ADCW;
167
168         if (zero_count) {
169                 if (zero_count > 1) {
170                         ADCSRA |= _BV(ADSC);
171                         zero_count--;
172                         return;
173                 } else {
174                         setup_mux(current_adc);
175                         zero_count = 0;
176                         /* fall through */
177                 }
178         }
179
180         if (drop_count) {
181                 ADCSRA |= _BV(ADSC); // drop this one, start the next
182                 drop_count--;
183                 return;
184         }
185
186         if (read_count) {
187                 ADCSRA |= _BV(ADSC);
188                 adc_sum += adcval;
189                 read_count--;
190                 return;
191         }
192
193         /*
194          * Now we have performed read_count measurements and have them
195          * in adc_sum.
196          */
197
198         // For inputs with gain, subtract the measured gain stage offset
199         if (current_adc < 2) {
200                 uint16_t offset = adc1_gain20_offset
201                         >> (ADC1_GAIN20_OFFSET_SHIFT - n_reads_log);
202
203                 if (adc_sum > offset)
204                         adc_sum -= offset;
205                 else
206                         adc_sum = 0;
207         }
208
209         switch (current_adc) {
210         case 0:
211         case 1:
212         case 2:
213                 pwmled_adc(current_adc, adc_sum);
214                 break;
215         case AMBIENT_ADC:
216                 ambient_adc(adc_sum);
217                 break;
218         case BATTERY_ADC:
219                 battery_adc(adc_sum);
220                 break;
221         case BUTTON_ADC:
222                 button_adc(adc_sum);
223                 break;
224         case ADC1_GAIN20:
225                 adc1_gain20_adc(adcval);
226                 break;
227         }
228
229         start_next_adc();
230 }
231