samd21: Hook up AltOS style ADC
authorKeith Packard <keithp@keithp.com>
Wed, 9 Nov 2022 00:18:57 +0000 (16:18 -0800)
committerKeith Packard <keithp@keithp.com>
Wed, 9 Nov 2022 00:18:57 +0000 (16:18 -0800)
Can't use DMA; samd21 only does ADC in continuous pins. ISR per ADC,
manually step through.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/samd21/ao_adc_samd21.c
src/samd21/ao_adc_samd21.h
src/samd21/samd21.h

index 1457b60314996d6395ac666e40c2b124a7d18be7..6d67eda7c137ecf8c00aa85993548e7cf0991b63 100644 (file)
@@ -22,33 +22,107 @@ ao_adc_sync(void)
                ;
 }
 
-static uint16_t
-ao_adc_do_conversion(void)
-{
-       ao_adc_sync();
-       samd21_adc.swtrig = (1 << SAMD21_ADC_SWTRIG_START);
-       ao_adc_sync();
-       while ((samd21_adc.intflag & (1 << SAMD21_ADC_INTFLAG_RESRDY)) == 0)
-               ao_adc_sync();
-       ao_adc_sync();
-       return samd21_adc.result;
-}
+static uint8_t ao_adc_sequence;
+static uint8_t ao_adc_ready;
+
+static uint8_t ao_adc_mux[AO_NUM_ADC] = {
+#if AO_NUM_ADC > 0
+       AO_ADC_SQ0,
+#endif
+#if AO_NUM_ADC > 1
+       AO_ADC_SQ1,
+#endif
+#if AO_NUM_ADC > 2
+       AO_ADC_SQ2,
+#endif
+#if AO_NUM_ADC > 3
+       AO_ADC_SQ3,
+#endif
+#if AO_NUM_ADC > 4
+       AO_ADC_SQ4,
+#endif
+#if AO_NUM_ADC > 5
+       AO_ADC_SQ5,
+#endif
+#if AO_NUM_ADC > 6
+       AO_ADC_SQ6,
+#endif
+#if AO_NUM_ADC > 7
+       AO_ADC_SQ7,
+#endif
+#if AO_NUM_ADC > 8
+       AO_ADC_SQ8,
+#endif
+#if AO_NUM_ADC > 9
+#error set up more ADC
+#endif
+};
 
-uint16_t
-ao_adc_read(uint8_t channel)
+static void
+ao_adc_start(void)
 {
-       ao_adc_sync();
-       samd21_adc.inputctrl = ((channel << SAMD21_ADC_INPUTCTRL_MUXPOS) |
+       uint8_t mux = ao_adc_mux[ao_adc_sequence];
+       samd21_adc.inputctrl = ((mux << SAMD21_ADC_INPUTCTRL_MUXPOS) |
                                (SAMD21_ADC_INPUTCTRL_MUXNEG_GND << SAMD21_ADC_INPUTCTRL_MUXNEG) |
                                (0 << SAMD21_ADC_INPUTCTRL_INPUTSCAN) |
                                (0 << SAMD21_ADC_INPUTCTRL_INPUTOFFSET) |
                                (SAMD21_ADC_INPUTCTRL_GAIN_DIV2 << SAMD21_ADC_INPUTCTRL_GAIN));
+       samd21_adc.swtrig = (1UL << SAMD21_ADC_SWTRIG_START);
+}
+
+void
+samd21_adc_isr(void)
+{
+       uint16_t        *out;
+
+       /* Store converted value in packet */
+       out = (uint16_t *) &ao_data_ring[ao_data_head].adc;
+       out[ao_adc_sequence] = (uint16_t) samd21_adc.result;
+       if (++ao_adc_sequence < AO_NUM_ADC) {
+               ao_adc_start();
+               return;
+       }
+
+       AO_DATA_PRESENT(AO_DATA_ADC);
+       ao_data_fill(ao_data_head);
+       ao_adc_ready = 1;
+}
 
-       /* Read twice and discard the first value as recommended by app note
-        * http://www.atmel.com/images/Atmel-42645-ADC-Configurations-with-Examples_ApplicationNote_AT11481.pdf
-        */
-       (void) ao_adc_do_conversion();
-       return ao_adc_do_conversion();
+void
+ao_adc_poll(void)
+{
+       if (!ao_adc_ready)
+               return;
+       ao_adc_ready = 0;
+       ao_adc_sequence = 0;
+       ao_adc_start();
+}
+
+static void
+ao_adc_dump(void)
+{
+       struct ao_data  packet;
+
+       ao_data_get(&packet);
+       AO_ADC_DUMP(&packet);
+}
+
+const struct ao_cmds ao_adc_cmds[] = {
+       { ao_adc_dump,  "a\0Display current ADC values" },
+       { 0, NULL },
+};
+
+static inline void
+set_adc(struct samd21_port *port, uint8_t pin)
+{
+       samd21_port_pmux_set(port, pin, SAMD21_PORT_PMUX_FUNC_B);
+       samd21_port_pincfg_set(port, pin,
+                              (1 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (1 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (1 << SAMD21_PORT_PINCFG_INEN),
+                              (0 << SAMD21_PORT_PINCFG_DRVSTR) |
+                              (0 << SAMD21_PORT_PINCFG_PULLEN) |
+                              (1 << SAMD21_PORT_PINCFG_INEN));
 }
 
 void
@@ -96,5 +170,47 @@ ao_adc_init(void)
 
        ao_adc_sync();
 
+       samd21_adc.intenset = (1UL << SAMD21_ADC_INTFLAG_RESRDY);
+
        samd21_adc.ctrla = (1 << SAMD21_ADC_CTRLA_ENABLE);
+
+       /* configure interrupts */
+       samd21_nvic_set_enable(SAMD21_NVIC_ISR_ADC_POS);
+       samd21_nvic_set_priority(SAMD21_NVIC_ISR_ADC_POS, 0);
+
+       ao_cmd_register(&ao_adc_cmds[0]);
+
+       /* configure pins */
+#if AO_NUM_ADC_PIN > 0
+       set_adc(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 1
+       set_adc(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 2
+       set_adc(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 3
+       set_adc(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 4
+       set_adc(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 5
+       set_adc(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 6
+       set_adc(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 7
+       set_adc(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 8
+       set_adc(AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN);
+#endif
+#if AO_NUM_ADC_PIN > 9
+#error set up more ADC bits
+#endif
+
+       ao_adc_ready = 1;
 }
index dd562a05d816b762d4f4b64954b940086f728e55..3d1658be38dcc7e6fb36bfc59bc33ab0a331990f 100644 (file)
@@ -15,8 +15,8 @@
 #ifndef _AO_ADC_SAMD21_H_
 #define _AO_ADC_SAMD21_H_
 
-uint16_t
-ao_adc_read(uint8_t channel);
+void
+ao_adc_poll(void);
 
 void
 ao_adc_init(void);
index 4a18e757d986d73a2ee4c43891f2803448e3cb17..91ad52bf0f24718d25320f8d2d5b0407ce2f9a53 100644 (file)
@@ -860,6 +860,7 @@ struct samd21_adc {
 #define SAMD21_ADC_SWTRIG_START                1
 
 #define SAMD21_ADC_INPUTCTRL_MUXPOS            0
+# define SAMD21_ADC_INPUTCTRL_MUXPOS_TEMP              0x18
 # define SAMD21_ADC_INPUTCTRL_MUXPOS_BANDGAP           0x19
 # define SAMD21_ADC_INPUTCTRL_MUXPOS_SCALEDCOREVCC     0x1a
 # define SAMD21_ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC       0x1b