altos: get ao_max6691 driver working
authorKeith Packard <keithp@keithp.com>
Sun, 21 Apr 2019 23:49:19 +0000 (16:49 -0700)
committerKeith Packard <keithp@keithp.com>
Sun, 21 Apr 2019 23:54:54 +0000 (16:54 -0700)
The driver uses a timer connected to a DMA engine to measure pulse
widths from the chip. We get 11 pulses for 4 channels; the first pulse
is caused by the timer starting up, the next two are the marker pulse
and then 8 more indicating the end of the high and low periods for
each channel.

The driver API returns the 8 pulse widths; the caller is expected to
know what to do with those values as using them requires knowing the
value of the configuration resistor and the characteristics of the
thermistors.

The test code assumes a 1k configuration resistor, using that it computes
the resistance of the four thermistors.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/drivers/ao_max6691.c
src/drivers/ao_max6691.h
src/stm/stm32l.h

index 89e8618..c61d2c2 100644 (file)
 
 #include "ao.h"
 #include "ao_max6691.h"
+#include "ao_exti.h"
 
-#define cat(a,b)       a ## b
+#define cat2(a,b)      a ## b
+#define cat(a,b)       cat2(a,b)
+
+#if AO_MAX6691_CH != 2
+#error ao_max6691 driver currently only works for timer channel 2
+#endif
 
 #define AO_MAX6691_CCR         (AO_MAX6691_TIMER->cat(ccr, AO_MAX6691_CH))
 
-#if 0
-static uint16_t        ao_max6691_data[8];
+/* Two samples per channel, plus time start value and two for Tready pulse */
+
+#define AO_MAX6691_SAMPLES     (AO_MAX6691_CHANNELS * 2 + 3)
+
+static uint16_t        ao_max6691_raw[AO_MAX6691_SAMPLES];
+
+static inline uint16_t
+ao_max6691_t_high(int channel)
+{
+       return ao_max6691_raw[channel * 2 + 3] - ao_max6691_raw[channel * 2 + 2];
+}
+
+static inline uint16_t
+ao_max6691_t_low(int channel)
+{
+       return ao_max6691_raw[channel * 2 + 4] - ao_max6691_raw[channel * 2 + 3];
+}
 
 void
-ao_max6691_sample(void)
+ao_max6691_sample(struct ao_max6691 *ao_max6691)
 {
+       struct stm_tim234       *tim = AO_MAX6691_TIMER;
+
+       tim->sr = 0;
+
+       memset(&ao_max6691_raw, '\0', sizeof (ao_max6691_raw));
        /* Get the DMA engine ready */
        ao_dma_set_transfer(AO_MAX6691_DMA,
                            &AO_MAX6691_CCR,
-                           &ao_max6691_data,
-                           8,
+                           &ao_max6691_raw,
+                           AO_MAX6691_SAMPLES,
                            (0 << STM_DMA_CCR_MEM2MEM) |
                            (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
                            (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
@@ -41,66 +67,120 @@ ao_max6691_sample(void)
        ao_dma_start(AO_MAX6691_DMA);
 
        /* Prod the max6691 */
-       ao_gpio_set(AO_MAX6691_PORT, AO_MAX6691_PIN, 0);
-       ao_arch_nop();
-       ao_gpio_set(AO_MAX6691_PORT, AO_MAX6691_PIN, 1);
+       ao_set_output(AO_MAX6691_GPIO, AO_MAX6691_PIN, 0);
+       int i;
+       for (i = 0; i < 100; i++)
+               ao_arch_nop();
+       ao_set_input(AO_MAX6691_GPIO, AO_MAX6691_PIN);
+       for (i = 0; i < 100; i++)
+               ao_arch_nop();
+
+       /* Reset the timer count */
+       tim->cnt = 0;
 
        /* Switch the pin to timer input mode */
        stm_afr_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_AFR_AF1);
 
+       tim->ccer = ((0 << STM_TIM234_CCER_CC1E) |
+                    (0 << STM_TIM234_CCER_CC1P) |
+                    (0 << STM_TIM234_CCER_CC1NP) |
+                    (1 << STM_TIM234_CCER_CC2E) |
+                    (1 << STM_TIM234_CCER_CC2P) |
+                    (1 << STM_TIM234_CCER_CC2NP) |
+                    (0 << STM_TIM234_CCER_CC3E) |
+                    (0 << STM_TIM234_CCER_CC3P) |
+                    (0 << STM_TIM234_CCER_CC3NP) |
+                    (0 << STM_TIM234_CCER_CC4E) |
+                    (0 << STM_TIM234_CCER_CC4P) |
+                    (0 << STM_TIM234_CCER_CC4NP));
+
+       /* Enable event generation on channel 2 */
+
+       tim->egr = ((0 << STM_TIM234_EGR_TG) |
+                   (0 << STM_TIM234_EGR_CC4G) |
+                   (0 << STM_TIM234_EGR_CC3G) |
+                   (1 << STM_TIM234_EGR_CC2G) |
+                   (0 << STM_TIM234_EGR_CC1G) |
+                   (0 << STM_TIM234_EGR_UG));
+       /* Start the timer */
+       tim->cr1 |= (1 << STM_TIM234_CR1_CEN);
+
        ao_arch_block_interrupts();
-       while (!ao_dma_done(AO_MAX6691_DMA))
-               ao_sleep(&ao_dma_done(AO_MAX6691_DMA));
+       while (!ao_dma_done[AO_MAX6691_DMA])
+               ao_sleep(&ao_dma_done[AO_MAX6691_DMA]);
        ao_arch_release_interrupts();
 
-       /* Switch pin back to output mode */
-       stm_moder_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_MODER_OUTPUT);
+       /* Disable event generation */
+       tim->egr = 0;
+
+       /* Disable capture */
+       tim->ccer = 0;
+
+       /* Stop the timer */
+       tim->cr1 &= ~(1 << STM_TIM234_CR1_CEN);
+
+       /* Switch back to GPIO mode */
+       stm_moder_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_MODER_INPUT);
 
        /* Mark DMA done */
        ao_dma_done_transfer(AO_MAX6691_DMA);
+
+       for (i = 0; i < AO_MAX6691_CHANNELS; i++) {
+               ao_max6691->sensor[i].t_high = ao_max6691_t_high(i);
+               ao_max6691->sensor[i].t_low = ao_max6691_t_low(i);
+       }
 }
-#endif
+
+#define R_EXT  1000.0f
 
 static void
 ao_max6691_test(void)
 {
-       int i;
+       struct ao_max6691 ao_max6691;
 
-       printf("Testing MAX6691\n");
-       /* Prod the max6691 */
-       ao_set_output(AO_MAX6691_GPIO, AO_MAX6691_PIN, 0);
-       for (i = 0; i < 1000; i++) {
-               ao_led_on(AO_LED_ARMED);
-               ao_gpio_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, 0);
-               ao_delay(AO_MS_TO_TICKS(250));
-               ao_led_off(AO_LED_ARMED);
-               ao_gpio_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, 1);
-               ao_delay(AO_MS_TO_TICKS(250));
-       }
-#if 0
-       uint16_t        tick;
-       uint16_t        i, j;
-       uint8_t         p, v;
+       printf("Testing MAX6691\n"); flush();
+       ao_max6691_sample(&ao_max6691);
 
-       for (i = 0; i < 100; i++)
-               ao_arch_nop();
-       ao_gpio_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, 1);
-       ao_arch_nop();
-       ao_set_input(AO_MAX6691_GPIO, AO_MAX6691_PIN);
-       i = 0;
-       p = 1;
-       for (tick = 0; i < 8 && tick < 10000; tick++)
-       {
-               v = ao_gpio_get(AO_MAX6691_GPIO, AO_MAX6691_PIN);
-               if (v != p) {
-                       ao_max6691_data[i++] = tick;
-                       p = v;
-               }
+       int i;
+       for (i = 0; i < AO_MAX6691_SAMPLES; i++)
+               printf("%d: %5u\n", i, ao_max6691_raw[i]);
+
+       for (i = 0; i < AO_MAX6691_CHANNELS; i++) {
+               uint16_t        t_high = ao_max6691.sensor[i].t_high;
+               uint16_t        t_low = ao_max6691.sensor[i].t_low;
+
+               /*
+                * From the MAX6691 data sheet
+                *
+                *      Thigh   Vext               Rext
+                *      ----- = ---- - 0.0002 = ---------- - 0.0002
+                *      Tlow    Vref            Rext - Rth
+                *
+                *      We want to find Rth given Rext and the timing values
+                *
+                *      Thigh              Rext
+                *      ----- + 0.0002 = ----------
+                *      Tlow             Rext + Rth
+                *
+                *      V = (Thigh / Tlow + 0.0002)
+                *
+                *      (Rext + Rth) * V = Rext
+                *
+                *      Rext * V + Rth * V = Rext
+                *
+                *      Rth * V = Rext - Rext * V
+                *
+                *      Rth * V = Rext * (1 - V)
+                *
+                *      Rth = Rext * (1 - V) / V
+                */
+
+               float V = (float) t_high / (float) t_low + 0.0002f;
+
+               float Rth = R_EXT * (1 - V) / V;
+
+               printf("%d: high %5u low %5u ohms: %7g\n", i, t_high, t_low, Rth);
        }
-       for (j = 0; j < i; i++)
-               printf("%d: %5u\n", j, ao_max6691_data[j]);
-#endif
-       printf("Done\n");
 }
 
 static const struct ao_cmds ao_max6691_cmds[] = {
@@ -114,40 +194,22 @@ ao_max6691_init(void)
 {
        ao_cmd_register(&ao_max6691_cmds[0]);
 
-#if 0
-       struct stm_tim234       *tim = &AO_MAX6691_TIMER;
+       struct stm_tim234       *tim = AO_MAX6691_TIMER;
 
        stm_rcc.apb1enr |= (1 << AO_MAX6691_TIMER_ENABLE);
 
        tim->cr1 = 0;
-       tim->psc = 0;
+       tim->psc = (AO_TIM23467_CLK / 4000000) - 1;     /* run the timer at 4MHz */
        tim->cnt = 0;
 
-       tim->ccmr1 = ((0 << STM_TIM234_CCMR1_IC2F) |
-                     (
+       /*
+        * XXX This assumes we're using CH2, which is true on TeleFireOne v2.0
+        */
+       tim->ccmr1 = ((STM_TIM234_CCMR1_IC2F_NONE << STM_TIM234_CCMR1_IC2F) |
+                     (STM_TIM234_CCMR1_IC2PSC_NONE << STM_TIM234_CCMR1_IC2PSC) |
+                     (STM_TIM234_CCMR1_CC2S_INPUT_TI2 << STM_TIM234_CCMR1_CC2S));
 
-
-       tim->ccmr2 = ((0 << STM_TIM234_CCMR2_OC4CE) |
-                     (STM_TIM234_CCMR2_OC4M_PWM_MODE_1 << STM_TIM234_CCMR2_OC4M) |
-                     (0 << STM_TIM234_CCMR2_OC4PE) |
-                     (0 << STM_TIM234_CCMR2_OC4FE) |
-                     (STM_TIM234_CCMR2_CC4S_OUTPUT << STM_TIM234_CCMR2_CC4S) |
-
-                     (0 << STM_TIM234_CCMR2_OC3CE) |
-                     (STM_TIM234_CCMR2_OC3M_PWM_MODE_1 << STM_TIM234_CCMR2_OC3M) |
-                     (0 << STM_TIM234_CCMR2_OC3PE) |
-                     (0 << STM_TIM234_CCMR2_OC3FE) |
-                     (STM_TIM234_CCMR2_CC3S_OUTPUT << STM_TIM234_CCMR2_CC3S));
-       tim->ccer = ((1 << STM_TIM234_CCER_CC1E) |
-                    (0 << STM_TIM234_CCER_CC1P) |
-                    (1 << STM_TIM234_CCER_CC2E) |
-                    (0 << STM_TIM234_CCER_CC2P) |
-                    (1 << STM_TIM234_CCER_CC3E) |
-                    (0 << STM_TIM234_CCER_CC3P) |
-                    (1 << STM_TIM234_CCER_CC4E) |
-                    (0 << STM_TIM234_CCER_CC4P));
-
-       tim->egr = 0;
+       tim->ccer = 0;
 
        tim->sr = 0;
        tim->dier = 0;
@@ -156,19 +218,6 @@ ao_max6691_init(void)
                    (STM_TIM234_CR2_MMS_RESET<< STM_TIM234_CR2_MMS) |
                    (0 << STM_TIM234_CR2_CCDS));
 
-       tim->cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
-                   (0 << STM_TIM234_CR1_ARPE) |
-                   (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
-                   (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
-                   (0 << STM_TIM234_CR1_OPM) |
-                   (0 << STM_TIM234_CR1_URS) |
-                   (0 << STM_TIM234_CR1_UDIS) |
-                   (1 << STM_TIM234_CR1_CEN));
-
-       tim->cr2 = ((0 << STM_TIM234_CR2_TI1S) |
-                   (STM_TIM234_CR2_MMS_RESET << STM_TIM234_CR2_MMS) |
-                   (0 << STM_TIM234_CR2_CCDS));
-
        tim->dier = ((0 << STM_TIM234_DIER_TDE) |
                     (0 << STM_TIM234_DIER_CC4DE) |
                     (0 << STM_TIM234_DIER_CC3DE) |
@@ -181,19 +230,17 @@ ao_max6691_init(void)
                     (0 << STM_TIM234_DIER_CC1IE) |
                     (0 << STM_TIM234_DIER_UIE));
 
-       tim->egr = ((0 << STM_TIM234_EGR_TG) |
-                   (0 << STM_TIM234_EGR_CC4G) |
-                   (0 << STM_TIM234_EGR_CC3G) |
-                   (1 << STM_TIM234_EGR_CC2G) |
-                   (0 << STM_TIM234_EGR_CC1G) |
-                   (0 << STM_TIM234_EGR_UG));
+       tim->egr = 0;
 
-       tim->ccmr1 = ((0 << STM_TIM234_CCMR_IC2F) |
-                     (0 << STM_TIM234_CCMR_IC2PSC) |
-                     (2 << STM_TIM234_CCMR_CC2S) |
-                     (
+       tim->cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+                   (0 << STM_TIM234_CR1_ARPE) |
+                   (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+                   (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+                   (0 << STM_TIM234_CR1_OPM) |
+                   (0 << STM_TIM234_CR1_URS) |
+                   (0 << STM_TIM234_CR1_UDIS) |
+                   (0 << STM_TIM234_CR1_CEN));
 
        stm_ospeedr_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_OSPEEDR_40MHz);
-#endif
-       ao_enable_output(AO_MAX6691_GPIO, AO_MAX6691_PIN, 1);
+       ao_enable_input(AO_MAX6691_GPIO, AO_MAX6691_PIN, AO_EXTI_MODE_PULL_UP);
 }
index 0c422d9..ab8207f 100644 (file)
 #ifndef _AO_MAX6691_H_
 #define _AO_MAX6691_H_
 
+#define AO_MAX6691_CHANNELS    4
+
+struct ao_max6691 {
+       struct {
+               uint16_t        t_high;
+               uint16_t        t_low;
+       } sensor[AO_MAX6691_CHANNELS];
+};
+
+void
+ao_max6691_sample(struct ao_max6691 *ao_max6691);
+
 void
 ao_max6691_init(void);
 
index 5741a84..310b334 100644 (file)
@@ -1871,6 +1871,51 @@ extern struct stm_tim234 stm_tim2, stm_tim3, stm_tim4;
 #define  STM_TIM234_CCMR1_CC1S_INPUT_TRC               3
 #define  STM_TIM234_CCMR1_CC1S_MASK                    3
 
+#define STM_TIM234_CCMR1_IC2F  12
+#define  STM_TIM234_CCMR1_IC2F_NONE                    0
+#define  STM_TIM234_CCMR1_IC2F_CK_INT_N_2              1
+#define  STM_TIM234_CCMR1_IC2F_CK_INT_N_4              2
+#define  STM_TIM234_CCMR1_IC2F_CK_INT_N_8              3
+#define  STM_TIM234_CCMR1_IC2F_DTS_2_N_6               4
+#define  STM_TIM234_CCMR1_IC2F_DTS_2_N_8               5
+#define  STM_TIM234_CCMR1_IC2F_DTS_4_N_6               6
+#define  STM_TIM234_CCMR1_IC2F_DTS_4_N_8               7
+#define  STM_TIM234_CCMR1_IC2F_DTS_8_N_6               8
+#define  STM_TIM234_CCMR1_IC2F_DTS_8_N_8               9
+#define  STM_TIM234_CCMR1_IC2F_DTS_16_N_5              10
+#define  STM_TIM234_CCMR1_IC2F_DTS_16_N_6              11
+#define  STM_TIM234_CCMR1_IC2F_DTS_16_N_8              12
+#define  STM_TIM234_CCMR1_IC2F_DTS_32_N_5              13
+#define  STM_TIM234_CCMR1_IC2F_DTS_32_N_6              14
+#define  STM_TIM234_CCMR1_IC2F_DTS_32_N_8              15
+#define STM_TIM234_CCMR1_IC2PSC        10
+#define  STM_TIM234_CCMR1_IC2PSC_NONE                  0
+#define  STM_TIM234_CCMR1_IC2PSC_2                     1
+#define  STM_TIM234_CCMR1_IC2PSC_4                     2
+#define  STM_TIM234_CCMR1_IC2PSC_8                     3
+#define STM_TIM234_CCMR1_IC1F  4
+#define  STM_TIM234_CCMR1_IC1F_NONE                    0
+#define  STM_TIM234_CCMR1_IC1F_CK_INT_N_2              1
+#define  STM_TIM234_CCMR1_IC1F_CK_INT_N_4              2
+#define  STM_TIM234_CCMR1_IC1F_CK_INT_N_8              3
+#define  STM_TIM234_CCMR1_IC1F_DTS_2_N_6               4
+#define  STM_TIM234_CCMR1_IC1F_DTS_2_N_8               5
+#define  STM_TIM234_CCMR1_IC1F_DTS_4_N_6               6
+#define  STM_TIM234_CCMR1_IC1F_DTS_4_N_8               7
+#define  STM_TIM234_CCMR1_IC1F_DTS_8_N_6               8
+#define  STM_TIM234_CCMR1_IC1F_DTS_8_N_8               9
+#define  STM_TIM234_CCMR1_IC1F_DTS_16_N_5              10
+#define  STM_TIM234_CCMR1_IC1F_DTS_16_N_6              11
+#define  STM_TIM234_CCMR1_IC1F_DTS_16_N_8              12
+#define  STM_TIM234_CCMR1_IC1F_DTS_32_N_5              13
+#define  STM_TIM234_CCMR1_IC1F_DTS_32_N_6              14
+#define  STM_TIM234_CCMR1_IC1F_DTS_32_N_8              15
+#define STM_TIM234_CCMR1_IC1PSC        2
+#define  STM_TIM234_CCMR1_IC1PSC_NONE                  0
+#define  STM_TIM234_CCMR1_IC1PSC_2                     1
+#define  STM_TIM234_CCMR1_IC1PSC_4                     2
+#define  STM_TIM234_CCMR1_IC1PSC_8                     3
+
 #define STM_TIM234_CCMR2_OC4CE 15
 #define STM_TIM234_CCMR2_OC4M  12
 #define  STM_TIM234_CCMR2_OC4M_FROZEN                  0