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