altos: Change MAX6691 driver to run its own thread
[fw/altos] / src / drivers / ao_max6691.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_max6691.h"
17 #include "ao_exti.h"
18
19 #define cat2(a,b)       a ## b
20 #define cat(a,b)        cat2(a,b)
21
22 #if AO_MAX6691_CH != 2
23 #error ao_max6691 driver currently only works for timer channel 2
24 #endif
25
26 #define AO_MAX6691_CCR          (AO_MAX6691_TIMER->cat(ccr, AO_MAX6691_CH))
27
28 /* Two samples per channel, plus time start value and two for Tready pulse */
29
30 #define AO_MAX6691_SAMPLES      (AO_MAX6691_CHANNELS * 2 + 3)
31
32 static uint16_t ao_max6691_raw[AO_MAX6691_SAMPLES];
33
34 static inline uint16_t
35 ao_max6691_t_high(int channel)
36 {
37         return ao_max6691_raw[channel * 2 + 3] - ao_max6691_raw[channel * 2 + 2];
38 }
39
40 static inline uint16_t
41 ao_max6691_t_low(int channel)
42 {
43         return ao_max6691_raw[channel * 2 + 4] - ao_max6691_raw[channel * 2 + 3];
44 }
45
46 struct ao_max6691_sample ao_max6691_current;
47
48 static void
49 ao_max6691_sample(void)
50 {
51         struct stm_tim234       *tim = AO_MAX6691_TIMER;
52
53         tim->sr = 0;
54
55         memset(&ao_max6691_raw, '\0', sizeof (ao_max6691_raw));
56         /* Get the DMA engine ready */
57         ao_dma_set_transfer(AO_MAX6691_DMA,
58                             &AO_MAX6691_CCR,
59                             &ao_max6691_raw,
60                             AO_MAX6691_SAMPLES,
61                             (0 << STM_DMA_CCR_MEM2MEM) |
62                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
63                             (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
64                             (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) |
65                             (1 << STM_DMA_CCR_MINC) |
66                             (0 << STM_DMA_CCR_PINC) |
67                             (0 << STM_DMA_CCR_CIRC) |
68                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
69         ao_dma_start(AO_MAX6691_DMA);
70
71         /* Prod the max6691 */
72         ao_set_output(AO_MAX6691_GPIO, AO_MAX6691_PIN, 0);
73         int i;
74         for (i = 0; i < 100; i++)
75                 ao_arch_nop();
76         ao_set_input(AO_MAX6691_GPIO, AO_MAX6691_PIN);
77         for (i = 0; i < 100; i++)
78                 ao_arch_nop();
79
80         /* Reset the timer count */
81         tim->cnt = 0;
82
83         /* Switch the pin to timer input mode */
84         stm_afr_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_AFR_AF1);
85
86         tim->ccer = ((0 << STM_TIM234_CCER_CC1E) |
87                      (0 << STM_TIM234_CCER_CC1P) |
88                      (0 << STM_TIM234_CCER_CC1NP) |
89                      (1 << STM_TIM234_CCER_CC2E) |
90                      (1 << STM_TIM234_CCER_CC2P) |
91                      (1 << STM_TIM234_CCER_CC2NP) |
92                      (0 << STM_TIM234_CCER_CC3E) |
93                      (0 << STM_TIM234_CCER_CC3P) |
94                      (0 << STM_TIM234_CCER_CC3NP) |
95                      (0 << STM_TIM234_CCER_CC4E) |
96                      (0 << STM_TIM234_CCER_CC4P) |
97                      (0 << STM_TIM234_CCER_CC4NP));
98
99         /* Enable event generation on channel 2 */
100
101         tim->egr = ((0 << STM_TIM234_EGR_TG) |
102                     (0 << STM_TIM234_EGR_CC4G) |
103                     (0 << STM_TIM234_EGR_CC3G) |
104                     (1 << STM_TIM234_EGR_CC2G) |
105                     (0 << STM_TIM234_EGR_CC1G) |
106                     (0 << STM_TIM234_EGR_UG));
107         /* Start the timer */
108         tim->cr1 |= (1 << STM_TIM234_CR1_CEN);
109
110         ao_arch_block_interrupts();
111         while (!ao_dma_done[AO_MAX6691_DMA])
112                 ao_sleep(&ao_dma_done[AO_MAX6691_DMA]);
113         ao_arch_release_interrupts();
114
115         /* Disable event generation */
116         tim->egr = 0;
117
118         /* Disable capture */
119         tim->ccer = 0;
120
121         /* Stop the timer */
122         tim->cr1 &= ~(1 << STM_TIM234_CR1_CEN);
123
124         /* Switch back to GPIO mode */
125         stm_moder_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_MODER_INPUT);
126
127         /* Mark DMA done */
128         ao_dma_done_transfer(AO_MAX6691_DMA);
129
130         for (i = 0; i < AO_MAX6691_CHANNELS; i++) {
131                 ao_max6691_current.sensor[i].t_high = ao_max6691_t_high(i);
132                 ao_max6691_current.sensor[i].t_low = ao_max6691_t_low(i);
133         }
134 }
135
136 static void
137 ao_max6691(void)
138 {
139         for (;;) {
140                 ao_max6691_sample();
141                 ao_arch_critical(AO_DATA_PRESENT(AO_DATA_MAX6691););
142         }
143 }
144
145 static struct ao_task ao_max6691_task;
146
147 #define R_EXT   1000.0f
148
149 static void
150 ao_max6691_dump(void)
151 {
152         struct ao_max6691_sample ao_max6691;
153
154         ao_max6691 = ao_max6691_current;
155
156         int i;
157         for (i = 0; i < AO_MAX6691_CHANNELS; i++) {
158                 uint16_t        t_high = ao_max6691.sensor[i].t_high;
159                 uint16_t        t_low = ao_max6691.sensor[i].t_low;
160
161                 /*
162                  * From the MAX6691 data sheet
163                  *
164                  *      Thigh   Vext               Rext
165                  *      ----- = ---- - 0.0002 = ---------- - 0.0002
166                  *      Tlow    Vref            Rext - Rth
167                  *
168                  *      We want to find Rth given Rext and the timing values
169                  *
170                  *      Thigh              Rext
171                  *      ----- + 0.0002 = ----------
172                  *      Tlow             Rext + Rth
173                  *
174                  *      V = (Thigh / Tlow + 0.0002)
175                  *
176                  *      (Rext + Rth) * V = Rext
177                  *
178                  *      Rext * V + Rth * V = Rext
179                  *
180                  *      Rth * V = Rext - Rext * V
181                  *
182                  *      Rth * V = Rext * (1 - V)
183                  *
184                  *      Rth = Rext * (1 - V) / V
185                  */
186
187                 float V = (float) t_high / (float) t_low + 0.0002f;
188
189                 float Rth = R_EXT * (1 - V) / V;
190
191                 printf("max6691 channel %d: high %5u low %5u ohms: %7g\n", i, t_high, t_low, Rth);
192         }
193 }
194
195 static const struct ao_cmds ao_max6691_cmds[] = {
196         { ao_max6691_dump,      "q\0Thermistor test" },
197         { 0, NULL },
198 };
199
200
201 void
202 ao_max6691_init(void)
203 {
204         ao_cmd_register(&ao_max6691_cmds[0]);
205
206         struct stm_tim234       *tim = AO_MAX6691_TIMER;
207
208         stm_rcc.apb1enr |= (1 << AO_MAX6691_TIMER_ENABLE);
209
210         tim->cr1 = 0;
211         tim->psc = (AO_TIM23467_CLK / 4000000) - 1;     /* run the timer at 4MHz */
212         tim->cnt = 0;
213
214         /*
215          * XXX This assumes we're using CH2, which is true on TeleFireOne v2.0
216          */
217         tim->ccmr1 = ((STM_TIM234_CCMR1_IC2F_NONE << STM_TIM234_CCMR1_IC2F) |
218                       (STM_TIM234_CCMR1_IC2PSC_NONE << STM_TIM234_CCMR1_IC2PSC) |
219                       (STM_TIM234_CCMR1_CC2S_INPUT_TI2 << STM_TIM234_CCMR1_CC2S));
220
221         tim->ccer = 0;
222
223         tim->sr = 0;
224         tim->dier = 0;
225         tim->smcr = 0;
226         tim->cr2 = ((0 << STM_TIM234_CR2_TI1S) |
227                     (STM_TIM234_CR2_MMS_RESET<< STM_TIM234_CR2_MMS) |
228                     (0 << STM_TIM234_CR2_CCDS));
229
230         tim->dier = ((0 << STM_TIM234_DIER_TDE) |
231                      (0 << STM_TIM234_DIER_CC4DE) |
232                      (0 << STM_TIM234_DIER_CC3DE) |
233                      (1 << STM_TIM234_DIER_CC2DE) |
234                      (0 << STM_TIM234_DIER_CC1DE) |
235                      (0 << STM_TIM234_DIER_TIE) |
236                      (0 << STM_TIM234_DIER_CC4IE) |
237                      (0 << STM_TIM234_DIER_CC3IE) |
238                      (0 << STM_TIM234_DIER_CC2IE) |
239                      (0 << STM_TIM234_DIER_CC1IE) |
240                      (0 << STM_TIM234_DIER_UIE));
241
242         tim->egr = 0;
243
244         tim->cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
245                     (0 << STM_TIM234_CR1_ARPE) |
246                     (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
247                     (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
248                     (0 << STM_TIM234_CR1_OPM) |
249                     (0 << STM_TIM234_CR1_URS) |
250                     (0 << STM_TIM234_CR1_UDIS) |
251                     (0 << STM_TIM234_CR1_CEN));
252
253         stm_ospeedr_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_OSPEEDR_40MHz);
254         ao_enable_input(AO_MAX6691_GPIO, AO_MAX6691_PIN, AO_EXTI_MODE_PULL_UP);
255
256         ao_add_task(&ao_max6691_task, ao_max6691, "max6691");
257 }