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