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