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