]> www.fi.muni.cz Git - tinyboard.git/blob - projects/step-up/adc.c
pwmled.c: only one pwmled
[tinyboard.git] / projects / step-up / adc.c
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3 #include <avr/sleep.h>
4
5 #include "lights.h"
6
7 #define BATTERY_ADC (N_PWMLEDS + 0)
8 #define BUTTON_ADC  (N_PWMLEDS + 1)
9 #define ZERO_ADC    (N_PWMLEDS + 2)
10
11 //#define NUM_ADCS      ZERO_ADC
12 #define NUM_ADCS        1
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 #if 0
21         { 0, 1, PWMLED_ADC_SHIFT },     // pwmled 2
22         { 0, 1, PWMLED_ADC_SHIFT },     // pwmled 3
23         { 0, 1, AMBIENT_ADC_SHIFT },    // ambient
24         { 0, 1, 0 },                    // battery
25         { 0, 1, 0 },                    // gain20
26         { 0, 1, 0 },                    // buttons
27 #endif
28 };
29
30 volatile static unsigned char current_adc, current_slow_adc;
31 static uint16_t adc_sum, zero_count, drop_count, read_count, n_reads_log;
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, ADC3 (PB3), single-ended
39                 ADMUX = _BV(REFS1) | _BV(MUX1) | _BV(MUX0);
40                 break;
41         case ZERO_ADC: // zero: 1.1V, GND, single-ended
42                 ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX0);
43                 break;
44         }
45 }
46
47 void start_next_adc()
48 {
49 #if 0
50         if (current_adc == 0) {
51                 if (current_slow_adc > N_PWMLEDS) {
52                         // read one of the non-PWMLED ADCs
53                         current_adc = --current_slow_adc;
54                 } else {
55                         // no more non-PWMLEDs to do, start with PWMLEDs
56                         current_adc = N_PWMLEDS-1;
57                 }
58         } else if (current_adc >= N_PWMLEDS) {
59                 // one of the non-PWMLED ADCs just finished, skip to PWMLEDs.
60                 current_adc = N_PWMLEDS-1;
61         } else {
62                 // next PWMLED
63                 current_adc--;
64         }
65 #else
66         // single ADC for testing only
67         current_adc = 0;
68 #endif
69
70 #if 0
71         log_byte(0x90 + current_adc); // debug ADC switching
72 #endif
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 void timer_start_slow_adcs()
100 {
101         if (current_slow_adc > N_PWMLEDS) { // Don't start if in progress
102                 log_byte(0x80 + current_slow_adc);
103         } else {
104                 current_slow_adc = NUM_ADCS;
105                 // TODO: kick the watchdog here
106         }
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_slow_adc = NUM_ADCS;
133         current_adc = 0;
134
135         ADCSRA = _BV(ADEN)                      // enable
136                 | _BV(ADPS1) | _BV(ADPS0)       // CLK/8 = 125 kHz
137                 // | _BV(ADPS2)                 // CLK/16 = 62.5 kHz
138                 ;
139         // ADCSRB |= _BV(GSEL); // gain 8 or 32
140
141         // Disable digital input on all bits used by ADC
142         DIDR0 = _BV(ADC3D) | _BV(ADC2D);
143         
144         // 1.1V, GND
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         ADCSRA |= _BV(ADIE); // enable IRQ
151
152         start_next_adc();
153 }
154
155 #if 0
156 void susp_adc()
157 {
158         ADCSRA = 0;
159         DIDR0 = 0;
160 }
161
162 static void adc1_gain20_adc(uint16_t adcsum)
163 {
164         // running average
165         adc1_gain20_offset += adcsum
166                         - (adc1_gain20_offset >> ADC1_GAIN20_OFFSET_SHIFT);
167 }
168 #endif
169
170 ISR(ADC_vect) { // IRQ handler
171         uint16_t adcval = ADCW;
172
173         if (zero_count) {
174                 if (zero_count > 1) {
175                         ADCSRA |= _BV(ADSC);
176                         zero_count--;
177                         return;
178                 } else {
179                         setup_mux(current_adc);
180                         zero_count = 0;
181                         /* fall through */
182                 }
183         }
184
185         if (drop_count) {
186                 ADCSRA |= _BV(ADSC); // drop this one, start the next
187                 drop_count--;
188                 return;
189         }
190
191         if (read_count) {
192                 ADCSRA |= _BV(ADSC);
193                 adc_sum += adcval;
194                 read_count--;
195                 return;
196         }
197
198         /*
199          * Now we have performed read_count measurements and have them
200          * in adc_sum.
201          */
202         switch (current_adc) {
203         case 0:
204                 // pwmled_adc(current_adc, adc_sum);
205                 pwmled_adc(adc_sum);
206                 break;
207         }
208
209         start_next_adc();
210 }