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