]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/adc.c
adc: average several measurements
[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
9 #define NUM_ADCS 5
10 volatile static unsigned char current_adc;
11 static uint16_t adc_sum;
12 static unsigned char sum_shift;
13 static unsigned char adc_vals;
14
15 static void inline setup_mux(unsigned char n)
16 {
17         ADCSRA |= _BV(ADEN); // enable ADC
18
19         /* ADC numbering: PWM LEDs first, then ambient light sensor, battery sensor */
20         switch (n) {
21         case 0: // pwmled 1: 1.1V, ADC0,1 (PA0,1), gain 20
22                 ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX1) | _BV(MUX0);
23                 sum_shift = 3; // 8 measurements
24                 break;
25         case 1: // pwmled 2: 1.1V, ADC2,1 (PA2,1), gain 20
26                 ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
27                 sum_shift = 3; // 8 measurements
28                 break;
29         case 2: // pwmled 3: 1.1V, ADC4 (PA5), single-ended
30                 ADMUX = _BV(REFS1) | _BV(MUX2);
31                 sum_shift = 3; // 8 measurements
32                 break;
33         case 3: // ambient light: 1.1V, ADC5 (PA6), single-ended
34                 ADMUX = _BV(REFS1) | _BV(MUX2) | _BV(MUX0);
35                 sum_shift = 0; // 1 measurement
36                 break;
37         case 4: // batt voltage: 1.1V, ADC6 (PA7), single-ended
38                 ADMUX = _BV(REFS1) | _BV(MUX2) | _BV(MUX1);
39                 sum_shift = 0; // 1 measurement
40                 break;
41         }
42
43         adc_sum = 0;
44         adc_vals = 1 << sum_shift;
45 }
46
47 static void start_next_adc()
48 {
49         if (current_adc > 0) {
50                 current_adc--;
51                 // set up mux, start one-shot conversion
52                 setup_mux(current_adc);
53                 ADCSRA |= _BV(ADSC);
54         } else {
55                 current_adc = NUM_ADCS;
56                 // TODO: kick the watchdog here.
57         }
58 }
59
60 void init_adc()
61 {
62         current_adc = NUM_ADCS;
63
64         ADCSRA = _BV(ADEN)                      // enable
65                 | _BV(ADPS1) | _BV(ADPS0)       // CLK/8 = 125 kHz
66                 // | _BV(ADPS2)                 // CLK/16 = 62.5 kHz
67                 ;
68         // ADCSRB |= _BV(GSEL); // gain 8 or 32
69
70         // Disable digital input on all bits used by ADC
71         DIDR0 = _BV(ADC0D) | _BV(ADC1D) | _BV(ADC2D)
72                 | _BV(ADC4D) | _BV(ADC5D) | _BV(ADC6D);
73
74         ADCSRA |= _BV(ADSC);
75
76         /* Do first conversion and drop the result */
77         while ((ADCSRA & _BV(ADIF)) == 0)
78                 ;
79         ADCSRA |= _BV(ADIF); // clear the IRQ flag
80         ADCSRA |= _BV(ADIE); // enable IRQ
81
82         ADCSRA &= ~_BV(ADEN); // disable until needed
83 }
84
85 void susp_adc()
86 {
87         ADCSRA = 0;
88         DIDR0 = 0;
89 }
90
91 ISR(ADC_vect) { // IRQ handler
92         uint16_t adcval = ADCW;
93
94         if (adc_vals)
95                 // start the next conversion immediately
96                 ADCSRA |= _BV(ADSC);
97         else
98                 ADCSRA &= ~_BV(ADEN); // the last one, disable ADC
99
100         if (adc_vals < (1 << sum_shift))
101                  // drop the first conversion, use all others
102                  adc_sum += adcval;
103
104         if (adc_vals) {
105                 adc_vals--;
106                 return;
107         }
108
109         // Now handle the (1 << sum_shift) measurements
110
111         adcval = adc_sum >> sum_shift;
112
113         if (current_adc < N_PWMLEDS)
114                 pwmled_adc(current_adc, adcval);
115         if (current_adc == AMBIENT_ADC)
116                 ambient_adc(adcval);
117         if (current_adc == BATTERY_ADC)
118                 battery_adc(adcval);
119         
120         start_next_adc();
121 }
122
123 void timer_start_adcs()
124 {
125         if (current_adc == NUM_ADCS) // Don't start if in progress
126                 start_next_adc();
127 }
128