doc: Add 1.9 release notes
[fw/altos] / src / avr / ao_adc_avr.c
1 /*
2  * Copyright © 2011 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include "ao.h"
20 #include "ao_pwmin.h"
21
22 volatile struct ao_data ao_data_ring[AO_DATA_RING];
23 volatile uint8_t                ao_data_head;
24
25 #ifdef TELESCIENCE
26 const uint8_t   adc_channels[AO_LOG_TELESCIENCE_NUM_ADC] = {
27         0x00,
28         0x01,
29         0x04,
30         0x05,
31         0x06,
32         0x07,
33         0x20,
34         0x21,
35         0x22,
36         0x23,
37         0x24,
38         0x25,
39 };
40 #endif
41
42 #ifdef TELEPYRO
43 const uint8_t   adc_channels[AO_TELEPYRO_NUM_ADC] = {
44         0x00,   /* ADC0  v_batt */
45         0x04,   /* ADC4  sense_a */
46         0x05,   /* ADC5  sense_b */
47         0x06,   /* ADC6  sense_c */
48         0x07,   /* ADC7  sense_d */
49         0x23,   /* ADC11 sense_e */
50         0x22,   /* ADC10 sense_f */
51         0x21,   /* ADC9 sense_g */
52         0x20,   /* ADC8 sense_h */
53 };
54 #endif
55
56 #define NUM_ADC (sizeof (adc_channels) / sizeof (adc_channels[0]))
57
58 static uint8_t  ao_adc_channel;
59
60 #define ADC_CHANNEL_LOW(c)      (((c) & 0x1f) << MUX0)
61 #define ADC_CHANNEL_HIGH(c)     ((((c) & 0x20) >> 5) << MUX5)
62
63 #define ADCSRA_INIT     ((1 << ADEN) |          /* Enable ADC */                \
64                          (0 << ADATE) |         /* No auto ADC trigger */       \
65                          (1 << ADIF) |          /* Clear interrupt */           \
66                          (0 << ADIE) |          /* Enable interrupt */          \
67                          (6 << ADPS0))          /* Prescale clock by 64 */
68
69 #define ADCSRB_INIT     ((0 << ADHSM) |         /* No high-speed mode */ \
70                          (0 << ACME) |          /* Some comparitor thing */ \
71                          (0 << ADTS0))          /* Free running mode (don't care) */
72
73 static void
74 ao_adc_start(void)
75 {
76         uint8_t channel = adc_channels[ao_adc_channel];
77         ADMUX = ((0 << REFS1) |                         /* AVcc reference */
78                  (1 << REFS0) |                         /* AVcc reference */
79                  (1 << ADLAR) |                         /* Left-shift results */
80                  (ADC_CHANNEL_LOW(channel)));           /* Select channel */
81
82         ADCSRB = (ADCSRB_INIT |
83                   ADC_CHANNEL_HIGH(channel));           /* High channel bit */
84
85         ADCSRA = (ADCSRA_INIT |
86                   (1 << ADSC) |
87                   (1 << ADIE));                         /* Start conversion */
88 }
89
90 ISR(ADC_vect)
91 {
92         uint16_t        value;
93
94         /* Must read ADCL first or the value there will be lost */
95         value = ADCL;
96         value |= (ADCH << 8);
97         ao_data_ring[ao_data_head].adc.adc[ao_adc_channel] = value;
98         if (++ao_adc_channel < NUM_ADC - HAS_ICP3_COUNT)
99                 ao_adc_start();
100         else {
101 #if HAS_ICP3_COUNT
102                 /* steal last adc channel for pwm input */
103                 ao_data_ring[ao_data_head].adc.adc[ao_adc_channel] = ao_icp3_count;
104 #endif
105                 ADCSRA = ADCSRA_INIT;
106                 ao_data_ring[ao_data_head].tick = ao_time();
107                 ao_data_head = ao_data_ring_next(ao_data_head);
108                 ao_wakeup((void *) &ao_data_head);
109                 ao_cpu_sleep_disable = 0;
110         }
111 }
112
113 void
114 ao_adc_poll(void)
115 {
116         ao_cpu_sleep_disable = 1;
117         ao_adc_channel = 0;
118         ao_adc_start();
119 }
120
121 void
122 ao_data_get(struct ao_data *packet)
123 {
124         uint8_t i = ao_data_ring_prev(ao_data_head);
125         memcpy(packet, (void *) &ao_data_ring[i], sizeof (struct ao_data));
126 }
127
128 static void
129 ao_adc_dump(void) 
130 {
131         static struct ao_data   packet;
132         uint8_t i;
133         ao_data_get(&packet);
134         printf("tick: %5u",  packet.tick);
135         for (i = 0; i < NUM_ADC; i++)
136                 printf (" %2d: %5u", i, packet.adc.adc[i]);
137         printf("\n");
138 }
139
140 const struct ao_cmds ao_adc_cmds[] = {
141         { ao_adc_dump,  "a\0ADC" },
142         { 0, NULL },
143 };
144
145 void
146 ao_adc_init(void)
147 {
148         PRR0 &= ~(1 << PRADC);
149         DIDR0 = 0xf3;
150         DIDR2 = 0x3f;
151         ADCSRB = ADCSRB_INIT;
152         ADCSRA = ADCSRA_INIT;
153         ao_cmd_register(&ao_adc_cmds[0]);
154 }