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