altos: Add support for STM ADC
authorKeith Packard <keithp@keithp.com>
Sun, 8 Apr 2012 01:53:12 +0000 (18:53 -0700)
committerKeith Packard <keithp@keithp.com>
Sun, 8 Apr 2012 01:53:12 +0000 (18:53 -0700)
DMA-based driver for the STM analog to digital converter.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/stm/ao_adc_stm.c [new file with mode: 0644]
src/stm/ao_arch.h
src/stm/ao_arch_funcs.h
src/stm/ao_dma_stm.c
src/stm/stm32l.h

diff --git a/src/stm/ao_adc_stm.c b/src/stm/ao_adc_stm.c
new file mode 100644 (file)
index 0000000..1722a9e
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+
+volatile __xdata struct ao_adc ao_adc_ring[AO_ADC_RING];
+volatile __data uint8_t                ao_adc_head;
+static uint8_t                 ao_adc_ready;
+
+#define AO_ADC_CR2_VAL         ((0 << STM_ADC_CR2_SWSTART) |           \
+                                (STM_ADC_CR2_EXTEN_DISABLE << STM_ADC_CR2_EXTEN) | \
+                                (0 << STM_ADC_CR2_EXTSEL) |            \
+                                (0 << STM_ADC_CR2_JWSTART) |           \
+                                (STM_ADC_CR2_JEXTEN_DISABLE << STM_ADC_CR2_JEXTEN) | \
+                                (0 << STM_ADC_CR2_JEXTSEL) |           \
+                                (1 << STM_ADC_CR2_ALIGN) |             \
+                                (0 << STM_ADC_CR2_EOCS) |              \
+                                (1 << STM_ADC_CR2_DDS) |               \
+                                (1 << STM_ADC_CR2_DMA) |               \
+                                (STM_ADC_CR2_DELS_NONE << STM_ADC_CR2_DELS) | \
+                                (0 << STM_ADC_CR2_CONT) |              \
+                                (1 << STM_ADC_CR2_ADON))
+
+/*
+ * Callback from DMA ISR
+ *
+ * Mark time in ring, shut down DMA engine
+ */
+static void ao_adc_done(void)
+{
+       ao_adc_ring[ao_adc_head].tick = ao_time();
+       ao_adc_head = ao_adc_ring_next(ao_adc_head);
+       ao_wakeup((void *) &ao_adc_head);
+       ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+       ao_adc_ready = 1;
+}
+
+/*
+ * Start the ADC sequence using the DMA engine
+ */
+void
+ao_adc_poll(void)
+{
+       if (!ao_adc_ready)
+               return;
+       ao_adc_ready = 0;
+       stm_adc.sr = 0;
+       ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1),
+                           &stm_adc.dr,
+                           (void *) (&ao_adc_ring[ao_adc_head].tick + 1),
+                           AO_NUM_ADC,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+       ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1), ao_adc_done);
+       ao_dma_start(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+       stm_adc.cr2 = AO_ADC_CR2_VAL | (1 << STM_ADC_CR2_SWSTART);
+}
+
+/*
+ * Fetch a copy of the most recent ADC data
+ */
+void
+ao_adc_get(__xdata struct ao_adc *packet)
+{
+       uint8_t i = ao_adc_ring_prev(ao_adc_head);
+       memcpy(packet, (void *) &ao_adc_ring[i], sizeof (struct ao_adc));
+}
+
+static void
+ao_adc_dump(void) __reentrant
+{
+       struct ao_adc   packet;
+       int16_t *d;
+       uint8_t i;
+
+       ao_adc_get(&packet);
+       printf("tick: %5u",  packet.tick);
+       d = (int16_t *) (&packet.tick + 1);
+       for (i = 0; i < AO_NUM_ADC; i++)
+               printf (" %2d: %5d", i, d[i]);
+       printf("\n");
+}
+
+__code struct ao_cmds ao_adc_cmds[] = {
+       { ao_adc_dump,  "a\0Display current ADC values" },
+       { 0, NULL },
+};
+
+void
+ao_adc_init(void)
+{
+#ifdef AO_ADC_PIN0_PORT
+       stm_rcc.ahbenr |= AO_ADC_RCC_AHBENR;
+#endif
+
+#ifdef AO_ADC_PIN0_PORT
+       stm_moder_set(&AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN1_PORT
+       stm_moder_set(&AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN2_PORT
+       stm_moder_set(&AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN3_PORT
+       stm_moder_set(&AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN4_PORT
+       stm_moder_set(&AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN5_PORT
+       stm_moder_set(&AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN6_PORT
+       stm_moder_set(&AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN7_PORT
+       stm_moder_set(&AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN8_PORT
+       stm_moder_set(&AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN9_PORT
+       stm_moder_set(&AO_ADC_PIN9_PORT, AO_ADC_PIN9_PIN, STM_MODER_ANALOG);
+#endif
+
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_ADC1EN);
+
+       /* Turn off ADC during configuration */
+       stm_adc.cr2 = 0;
+
+       stm_adc.cr1 = ((0 << STM_ADC_CR1_OVRIE ) |
+                      (STM_ADC_CR1_RES_12 << STM_ADC_CR1_RES ) |
+                      (0 << STM_ADC_CR1_AWDEN ) |
+                      (0 << STM_ADC_CR1_JAWDEN ) |
+                      (0 << STM_ADC_CR1_PDI ) |
+                      (0 << STM_ADC_CR1_PDD ) |
+                      (0 << STM_ADC_CR1_DISCNUM ) |
+                      (0 << STM_ADC_CR1_JDISCEN ) |
+                      (0 << STM_ADC_CR1_DISCEN ) |
+                      (0 << STM_ADC_CR1_JAUTO ) |
+                      (0 << STM_ADC_CR1_AWDSGL ) |
+                      (1 << STM_ADC_CR1_SCAN ) |
+                      (0 << STM_ADC_CR1_JEOCIE ) |
+                      (0 << STM_ADC_CR1_AWDIE ) |
+                      (0 << STM_ADC_CR1_EOCIE ) |
+                      (0 << STM_ADC_CR1_AWDCH ));
+
+       /* 4 cycle sample time for everyone */
+       stm_adc.smpr1 = 0;
+       stm_adc.smpr2 = 0;
+       stm_adc.smpr3 = 0;
+
+       stm_adc.sqr1 = ((AO_NUM_ADC - 1) << 20);
+       stm_adc.sqr2 = 0;
+       stm_adc.sqr3 = 0;
+       stm_adc.sqr4 = 0;
+       stm_adc.sqr5 = 0;
+#if AO_NUM_ADC > 0
+       stm_adc.sqr5 |= (AO_ADC_SQ1 << 0);
+#endif
+#if AO_NUM_ADC > 1
+       stm_adc.sqr5 |= (AO_ADC_SQ2 << 5);
+#endif
+#if AO_NUM_ADC > 2
+       stm_adc.sqr5 |= (AO_ADC_SQ3 << 10);
+#endif
+#if AO_NUM_ADC > 3
+       stm_adc.sqr5 |= (AO_ADC_SQ4 << 15);
+#endif
+#if AO_NUM_ADC > 4
+       stm_adc.sqr5 |= (AO_ADC_SQ5 << 20);
+#endif
+#if AO_NUM_ADC > 5
+       stm_adc.sqr5 |= (AO_ADC_SQ6 << 25);
+#endif
+#if AO_NUM_ADC > 6
+       stm_adc.sqr4 |= (AO_ADC_SQ7 << 0);
+#endif
+#if AO_NUM_ADC > 7
+       stm_adc.sqr4 |= (AO_ADC_SQ8 << 5);
+#endif
+#if AO_NUM_ADC > 8
+       stm_adc.sqr4 |= (AO_ADC_SQ9 << 10);
+#endif
+
+       /* Turn ADC on */
+       stm_adc.cr2 = AO_ADC_CR2_VAL;
+
+       /* Wait for ADC to be ready */
+       while (!(stm_adc.sr & (1 << STM_ADC_SR_ADONS)))
+               ;
+
+#if HAS_ADC_TEMP
+       stm_adc.ccr = ((1 << STM_ADC_CCR_TSVREFE));
+#else
+       stm_adc.ccr = 0;
+#endif
+       /* Clear any stale status bits */
+       stm_adc.sr = 0;
+       ao_adc_ready = 1;
+       ao_cmd_register(&ao_adc_cmds[0]);
+}
index 188b89962b3532b5aa6634a8d47004f50d382495..ce3a22e262e6ce8c7a7c330c3aa66b732b9b4620 100644 (file)
@@ -145,13 +145,6 @@ extern const uint16_t ao_serial_number;
 
 #define ao_arch_critical(b) do { cli(); do { b } while (0); sei(); } while (0)
 
-#define AO_ARM_NUM_ADC 12
-
-struct ao_adc {
-       uint16_t        tick;                   /* tick when the sample was read */
-       uint16_t        adc[AO_ARM_NUM_ADC];    /* samples */
-};
-
 /*
  * For now, we're running at a weird frequency
  */
@@ -203,5 +196,8 @@ ao_serial3_set_speed(uint8_t speed);
 
 extern uint32_t        ao_radio_cal;
 
+void
+ao_adc_init();
+
 #endif /* _AO_ARCH_H_ */
 
index e0ecb0a56ee4e7b6f91a8b89ca44b5800ec0af62..a42fe10f3cf9b3d5356b77407b6df46ee9099eae 100644 (file)
@@ -49,6 +49,9 @@ ao_dma_set_transfer(uint8_t           index,
                    uint16_t            count,
                    uint32_t            ccr);
 
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(void));
+
 void
 ao_dma_start(uint8_t index);
 
index f996e7cd603ca7666fa273bd39594c2e9267277d..785235a8023cfe94512ce35c5a34ef46c7201182 100644 (file)
@@ -20,7 +20,7 @@
 #define NUM_DMA        7
 
 struct ao_dma_config {
-       uint32_t        isr;
+       void            (*isr)(void);
 };
 
 uint8_t ao_dma_done[NUM_DMA];
@@ -37,10 +37,12 @@ ao_dma_isr(uint8_t index) {
 
        /* Ack them */
        stm_dma.ifcr = isr;
-       isr >>= STM_DMA_ISR(index);
-       ao_dma_config[index].isr |= isr;
-       ao_dma_done[index] = 1;
-       ao_wakeup(&ao_dma_done[index]);
+       if (ao_dma_config[index].isr)
+               (*ao_dma_config[index].isr)();
+       else {
+               ao_dma_done[index] = 1;
+               ao_wakeup(&ao_dma_done[index]);
+       }
 }
 
 void stm_dma1_channel1_isr(void) { ao_dma_isr(STM_DMA_INDEX(1)); }
@@ -59,12 +61,21 @@ ao_dma_set_transfer(uint8_t                 index,
                    uint32_t            ccr)
 {
        ao_mutex_get(&ao_dma_mutex[index]);
-       if (ao_dma_active++ == 0)
-               stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+       ao_arch_critical(
+               if (ao_dma_active++ == 0)
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+               );
        stm_dma.channel[index].ccr = ccr | (1 << STM_DMA_CCR_TCIE);
        stm_dma.channel[index].cndtr = count;
        stm_dma.channel[index].cpar = peripheral;
        stm_dma.channel[index].cmar = memory;
+       ao_dma_config[index].isr = NULL;
+}
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(void))
+{
+       ao_dma_config[index].isr = isr;
 }
 
 void
@@ -78,8 +89,10 @@ void
 ao_dma_done_transfer(uint8_t index)
 {
        stm_dma.channel[index].ccr &= ~(1 << STM_DMA_CCR_EN);
-       if (--ao_dma_active == 0)
-               stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMA1EN);
+       ao_arch_critical(
+               if (--ao_dma_active == 0)
+                       stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMA1EN);
+               );
        ao_mutex_put(&ao_dma_mutex[index]);
 }
 
index e03556b0ea7f5faad6e35ad8e5a9c179f11e7c7a..b9f455138897e92f3efa46f93b2365cc304f6a0f 100644 (file)
@@ -1013,4 +1013,143 @@ extern struct stm_spi stm_spi1, stm_spi2, stm_spi3;
 #define STM_SPI_SR_TXE         1
 #define STM_SPI_SR_RXNE                0
 
+struct stm_adc {
+       vuint32_t       sr;
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       smpr1;
+       vuint32_t       smpr2;
+       vuint32_t       smpr3;
+       vuint32_t       jofr1;
+       vuint32_t       jofr2;
+       vuint32_t       jofr3;
+       vuint32_t       jofr4;
+       vuint32_t       htr;
+       vuint32_t       ltr;
+       vuint32_t       sqr1;
+       vuint32_t       sqr2;
+       vuint32_t       sqr3;
+       vuint32_t       sqr4;
+       vuint32_t       sqr5;
+       vuint32_t       jsqr;
+       vuint32_t       jdr1;
+       vuint32_t       jdr2;
+       vuint32_t       jdr3;
+       vuint32_t       jdr4;
+       vuint32_t       dr;
+       uint8_t         reserved[0x300 - 0x5c];
+       vuint32_t       csr;
+       vuint32_t       ccr;
+};
+
+extern struct stm_adc stm_adc;
+
+#define STM_ADC_SR_JCNR                9
+#define STM_ADC_SR_RCNR                8
+#define STM_ADC_SR_ADONS       6
+#define STM_ADC_SR_OVR         5
+#define STM_ADC_SR_STRT                4
+#define STM_ADC_SR_JSTRT       3
+#define STM_ADC_SR_JEOC                2
+#define STM_ADC_SR_EOC         1
+#define STM_ADC_SR_AWD         0
+
+#define STM_ADC_CR1_OVRIE      26
+#define STM_ADC_CR1_RES                24
+#define  STM_ADC_CR1_RES_12            0
+#define  STM_ADC_CR1_RES_10            1
+#define  STM_ADC_CR1_RES_8             2
+#define  STM_ADC_CR1_RES_6             3
+#define  STM_ADC_CR1_RES_MASK          3
+#define STM_ADC_CR1_AWDEN       23
+#define STM_ADC_CR1_JAWDEN     22
+#define STM_ADC_CR1_PDI                17
+#define STM_ADC_CR1_PDD                16
+#define STM_ADC_CR1_DISCNUM    13
+#define  STM_ADC_CR1_DISCNUM_1         0
+#define  STM_ADC_CR1_DISCNUM_2         1
+#define  STM_ADC_CR1_DISCNUM_3         2
+#define  STM_ADC_CR1_DISCNUM_4         3
+#define  STM_ADC_CR1_DISCNUM_5         4
+#define  STM_ADC_CR1_DISCNUM_6         5
+#define  STM_ADC_CR1_DISCNUM_7         6
+#define  STM_ADC_CR1_DISCNUM_8         7
+#define  STM_ADC_CR1_DISCNUM_MASK      7
+#define STM_ADC_CR1_JDISCEN    12
+#define STM_ADC_CR1_DISCEN     11
+#define STM_ADC_CR1_JAUTO      10
+#define STM_ADC_CR1_AWDSGL     9
+#define STM_ADC_CR1_SCAN       8
+#define STM_ADC_CR1_JEOCIE     7
+#define STM_ADC_CR1_AWDIE      6
+#define STM_ADC_CR1_EOCIE      5
+#define STM_ADC_CR1_AWDCH      0
+#define  STM_ADC_CR1_AWDCH_MASK                0x1f
+
+#define STM_ADC_CR2_SWSTART    30
+#define STM_ADC_CR2_EXTEN      28
+#define  STM_ADC_CR2_EXTEN_DISABLE     0
+#define  STM_ADC_CR2_EXTEN_RISING      1
+#define  STM_ADC_CR2_EXTEN_FALLING     2
+#define  STM_ADC_CR2_EXTEN_BOTH                3
+#define  STM_ADC_CR2_EXTEN_MASK                3
+#define STM_ADC_CR2_EXTSEL     24
+#define  STM_ADC_CR2_EXTSEL_TIM9_CC2   0
+#define  STM_ADC_CR2_EXTSEL_TIM9_TRGO  1
+#define  STM_ADC_CR2_EXTSEL_TIM2_CC3   2
+#define  STM_ADC_CR2_EXTSEL_TIM2_CC2   3
+#define  STM_ADC_CR2_EXTSEL_TIM3_TRGO  4
+#define  STM_ADC_CR2_EXTSEL_TIM4_CC4   5
+#define  STM_ADC_CR2_EXTSEL_TIM2_TRGO  6
+#define  STM_ADC_CR2_EXTSEL_TIM3_CC1   7
+#define  STM_ADC_CR2_EXTSEL_TIM3_CC3   8
+#define  STM_ADC_CR2_EXTSEL_TIM4_TRGO  9
+#define  STM_ADC_CR2_EXTSEL_TIM6_TRGO  10
+#define  STM_ADC_CR2_EXTSEL_EXTI_11    15
+#define  STM_ADC_CR2_EXTSEL_MASK       15
+#define STM_ADC_CR2_JWSTART    22
+#define STM_ADC_CR2_JEXTEN     20
+#define  STM_ADC_CR2_JEXTEN_DISABLE    0
+#define  STM_ADC_CR2_JEXTEN_RISING     1
+#define  STM_ADC_CR2_JEXTEN_FALLING    2
+#define  STM_ADC_CR2_JEXTEN_BOTH       3
+#define  STM_ADC_CR2_JEXTEN_MASK       3
+#define STM_ADC_CR2_JEXTSEL    16
+#define  STM_ADC_CR2_JEXTSEL_TIM9_CC1  0
+#define  STM_ADC_CR2_JEXTSEL_TIM9_TRGO 1
+#define  STM_ADC_CR2_JEXTSEL_TIM2_TRGO 2
+#define  STM_ADC_CR2_JEXTSEL_TIM2_CC1  3
+#define  STM_ADC_CR2_JEXTSEL_TIM3_CC4  4
+#define  STM_ADC_CR2_JEXTSEL_TIM4_TRGO 5
+#define  STM_ADC_CR2_JEXTSEL_TIM4_CC1  6
+#define  STM_ADC_CR2_JEXTSEL_TIM4_CC2  7
+#define  STM_ADC_CR2_JEXTSEL_TIM4_CC3  8
+#define  STM_ADC_CR2_JEXTSEL_TIM10_CC1 9
+#define  STM_ADC_CR2_JEXTSEL_TIM7_TRGO 10
+#define  STM_ADC_CR2_JEXTSEL_EXTI_15   15
+#define  STM_ADC_CR2_JEXTSEL_MASK      15
+#define STM_ADC_CR2_ALIGN      11
+#define STM_ADC_CR2_EOCS       10
+#define STM_ADC_CR2_DDS                9
+#define STM_ADC_CR2_DMA                8
+#define STM_ADC_CR2_DELS       4
+#define  STM_ADC_CR2_DELS_NONE         0
+#define  STM_ADC_CR2_DELS_UNTIL_READ   1
+#define  STM_ADC_CR2_DELS_7            2
+#define  STM_ADC_CR2_DELS_15           3
+#define  STM_ADC_CR2_DELS_31           4
+#define  STM_ADC_CR2_DELS_63           5
+#define  STM_ADC_CR2_DELS_127          6
+#define  STM_ADC_CR2_DELS_255          7
+#define  STM_ADC_CR2_DELS_MASK         7
+#define STM_ADC_CR2_CONT       1
+#define STM_ADC_CR2_ADON       0
+
+#define STM_ADC_CCR_TSVREFE    23
+#define STM_ADC_CCR_ADCPRE     16
+#define  STM_ADC_CCR_ADCPRE_HSI_1      0
+#define  STM_ADC_CCR_ADCPRE_HSI_2      1
+#define  STM_ADC_CCR_ADCPRE_HSI_4      2
+#define  STM_ADC_CCR_ADCPRE_MASK       3
+
 #endif /* _STM32L_H_ */