samd21: Hook up AltOS style ADC
[fw/altos] / src / samd21 / ao_adc_samd21.c
1 /*
2  * Copyright © 2019 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
15 #include <ao.h>
16 #include <ao_adc_samd21.h>
17
18 static void
19 ao_adc_sync(void)
20 {
21         while (samd21_adc.status & (1 << SAMD21_ADC_STATUS_SYNCBUSY))
22                 ;
23 }
24
25 static uint8_t ao_adc_sequence;
26 static uint8_t ao_adc_ready;
27
28 static uint8_t ao_adc_mux[AO_NUM_ADC] = {
29 #if AO_NUM_ADC > 0
30         AO_ADC_SQ0,
31 #endif
32 #if AO_NUM_ADC > 1
33         AO_ADC_SQ1,
34 #endif
35 #if AO_NUM_ADC > 2
36         AO_ADC_SQ2,
37 #endif
38 #if AO_NUM_ADC > 3
39         AO_ADC_SQ3,
40 #endif
41 #if AO_NUM_ADC > 4
42         AO_ADC_SQ4,
43 #endif
44 #if AO_NUM_ADC > 5
45         AO_ADC_SQ5,
46 #endif
47 #if AO_NUM_ADC > 6
48         AO_ADC_SQ6,
49 #endif
50 #if AO_NUM_ADC > 7
51         AO_ADC_SQ7,
52 #endif
53 #if AO_NUM_ADC > 8
54         AO_ADC_SQ8,
55 #endif
56 #if AO_NUM_ADC > 9
57 #error set up more ADC
58 #endif
59 };
60
61 static void
62 ao_adc_start(void)
63 {
64         uint8_t mux = ao_adc_mux[ao_adc_sequence];
65         samd21_adc.inputctrl = ((mux << SAMD21_ADC_INPUTCTRL_MUXPOS) |
66                                 (SAMD21_ADC_INPUTCTRL_MUXNEG_GND << SAMD21_ADC_INPUTCTRL_MUXNEG) |
67                                 (0 << SAMD21_ADC_INPUTCTRL_INPUTSCAN) |
68                                 (0 << SAMD21_ADC_INPUTCTRL_INPUTOFFSET) |
69                                 (SAMD21_ADC_INPUTCTRL_GAIN_DIV2 << SAMD21_ADC_INPUTCTRL_GAIN));
70         samd21_adc.swtrig = (1UL << SAMD21_ADC_SWTRIG_START);
71 }
72
73 void
74 samd21_adc_isr(void)
75 {
76         uint16_t        *out;
77
78         /* Store converted value in packet */
79         out = (uint16_t *) &ao_data_ring[ao_data_head].adc;
80         out[ao_adc_sequence] = (uint16_t) samd21_adc.result;
81         if (++ao_adc_sequence < AO_NUM_ADC) {
82                 ao_adc_start();
83                 return;
84         }
85
86         AO_DATA_PRESENT(AO_DATA_ADC);
87         ao_data_fill(ao_data_head);
88         ao_adc_ready = 1;
89 }
90
91 void
92 ao_adc_poll(void)
93 {
94         if (!ao_adc_ready)
95                 return;
96         ao_adc_ready = 0;
97         ao_adc_sequence = 0;
98         ao_adc_start();
99 }
100
101 static void
102 ao_adc_dump(void)
103 {
104         struct ao_data  packet;
105
106         ao_data_get(&packet);
107         AO_ADC_DUMP(&packet);
108 }
109
110 const struct ao_cmds ao_adc_cmds[] = {
111         { ao_adc_dump,  "a\0Display current ADC values" },
112         { 0, NULL },
113 };
114
115 static inline void
116 set_adc(struct samd21_port *port, uint8_t pin)
117 {
118         samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_B);
119         samd21_port_pincfg_set(port, pin,
120                                (1 << SAMD21_PORT_PINCFG_DRVSTR) |
121                                (1 << SAMD21_PORT_PINCFG_PULLEN) |
122                                (1 << SAMD21_PORT_PINCFG_INEN),
123                                (0 << SAMD21_PORT_PINCFG_DRVSTR) |
124                                (0 << SAMD21_PORT_PINCFG_PULLEN) |
125                                (1 << SAMD21_PORT_PINCFG_INEN));
126 }
127
128 void
129 ao_adc_init(void)
130 {
131         /* supply a clock */
132         samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_ADC);
133
134         /* enable the device */
135         samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_ADC);
136
137         /* Reset */
138         samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_SWRST);
139
140         ao_adc_sync();
141
142         while ((samd21_adc.ctrla & (1 << SAMD21_ADC_CTRLA_SWRST)) != 0 ||
143                (samd21_adc.status & (1 << SAMD21_ADC_STATUS_SYNCBUSY)) != 0)
144                 ao_adc_sync();
145
146         /* Load ADC calibration values */
147         uint32_t b = (samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_ADC_BIASCAL) & SAMD21_AUX1_CALIBRATION_ADC_BIASCAL_MASK;
148         uint32_t l = (samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_ADC_LINEARITY) & SAMD21_AUX1_CALIBRATION_ADC_LINEARITY_MASK;
149
150         samd21_adc.calib = (uint16_t) ((b << SAMD21_ADC_CALIB_BIAS_CAL) |
151                                        (l << SAMD21_ADC_CALIB_LINEARITY_CAL));
152
153
154         ao_adc_sync();
155
156         samd21_adc.ctrlb = ((0 << SAMD21_ADC_CTRLB_DIFFMODE) |
157                             (0 << SAMD21_ADC_CTRLB_LEFTADJ) |
158                             (0 << SAMD21_ADC_CTRLB_FREERUN) |
159                             (0 << SAMD21_ADC_CTRLB_CORREN) |
160                             (SAMD21_ADC_CTRLB_RESSEL_12BIT << SAMD21_ADC_CTRLB_RESSEL) |
161                             (SAMD21_ADC_CTRLB_PRESCALER_DIV512 << SAMD21_ADC_CTRLB_PRESCALER));
162
163         ao_adc_sync();
164
165         samd21_adc.sampctrl = 0x1f;
166
167         ao_adc_sync();
168
169         samd21_adc.refctrl = (SAMD21_ADC_REFCTRL_REFSEL_INTVCC1 << SAMD21_ADC_REFCTRL_REFSEL);
170
171         ao_adc_sync();
172
173         samd21_adc.intenset = (1UL << SAMD21_ADC_INTFLAG_RESRDY);
174
175         samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_ENABLE);
176
177         /* configure interrupts */
178         samd21_nvic_set_enable(SAMD21_NVIC_ISR_ADC_POS);
179         samd21_nvic_set_priority(SAMD21_NVIC_ISR_ADC_POS, 0);
180
181         ao_cmd_register(&ao_adc_cmds[0]);
182
183         /* configure pins */
184 #if AO_NUM_ADC_PIN > 0
185         set_adc(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN);
186 #endif
187 #if AO_NUM_ADC_PIN > 1
188         set_adc(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN);
189 #endif
190 #if AO_NUM_ADC_PIN > 2
191         set_adc(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN);
192 #endif
193 #if AO_NUM_ADC_PIN > 3
194         set_adc(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN);
195 #endif
196 #if AO_NUM_ADC_PIN > 4
197         set_adc(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN);
198 #endif
199 #if AO_NUM_ADC_PIN > 5
200         set_adc(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN);
201 #endif
202 #if AO_NUM_ADC_PIN > 6
203         set_adc(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN);
204 #endif
205 #if AO_NUM_ADC_PIN > 7
206         set_adc(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN);
207 #endif
208 #if AO_NUM_ADC_PIN > 8
209         set_adc(AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN);
210 #endif
211 #if AO_NUM_ADC_PIN > 9
212 #error set up more ADC bits
213 #endif
214
215         ao_adc_ready = 1;
216 }