2 * Copyright © 2019 Keith Packard <keithp@keithp.com>
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.
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.
16 #include "ao_max6691.h"
19 #define cat2(a,b) a ## b
20 #define cat(a,b) cat2(a,b)
22 #if AO_MAX6691_CH != 2
23 #error ao_max6691 driver currently only works for timer channel 2
26 #define AO_MAX6691_CCR (AO_MAX6691_TIMER->cat(ccr, AO_MAX6691_CH))
28 /* Two samples per channel, plus time start value and two for Tready pulse */
30 #define AO_MAX6691_SAMPLES (AO_MAX6691_CHANNELS * 2 + 3)
32 static uint16_t ao_max6691_raw[AO_MAX6691_SAMPLES];
34 static inline uint16_t
35 ao_max6691_t_high(int channel)
37 return ao_max6691_raw[channel * 2 + 3] - ao_max6691_raw[channel * 2 + 2];
40 static inline uint16_t
41 ao_max6691_t_low(int channel)
43 return ao_max6691_raw[channel * 2 + 4] - ao_max6691_raw[channel * 2 + 3];
46 struct ao_max6691_sample ao_max6691_current;
49 ao_max6691_sample(void)
51 struct stm_tim234 *tim = AO_MAX6691_TIMER;
55 memset(&ao_max6691_raw, '\0', sizeof (ao_max6691_raw));
56 /* Get the DMA engine ready */
57 ao_dma_set_transfer(AO_MAX6691_DMA,
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);
71 /* Prod the max6691 */
72 ao_set_output(AO_MAX6691_GPIO, AO_MAX6691_PIN, 0);
74 for (i = 0; i < 100; i++)
76 ao_set_input(AO_MAX6691_GPIO, AO_MAX6691_PIN);
77 for (i = 0; i < 100; i++)
80 /* Reset the timer count */
83 /* Switch the pin to timer input mode */
84 stm_afr_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_AFR_AF1);
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));
99 /* Enable event generation on channel 2 */
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);
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();
115 /* Disable event generation */
118 /* Disable capture */
122 tim->cr1 &= ~(1 << STM_TIM234_CR1_CEN);
124 /* Switch back to GPIO mode */
125 stm_moder_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_MODER_INPUT);
128 ao_dma_done_transfer(AO_MAX6691_DMA);
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);
141 ao_arch_critical(AO_DATA_PRESENT(AO_DATA_MAX6691););
145 static struct ao_task ao_max6691_task;
147 #define R_EXT 1000.0f
150 ao_max6691_dump(void)
152 struct ao_max6691_sample ao_max6691;
154 ao_max6691 = ao_max6691_current;
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;
162 * From the MAX6691 data sheet
165 * ----- = ---- - 0.0002 = ---------- - 0.0002
166 * Tlow Vref Rext - Rth
168 * We want to find Rth given Rext and the timing values
171 * ----- + 0.0002 = ----------
174 * V = (Thigh / Tlow + 0.0002)
176 * (Rext + Rth) * V = Rext
178 * Rext * V + Rth * V = Rext
180 * Rth * V = Rext - Rext * V
182 * Rth * V = Rext * (1 - V)
184 * Rth = Rext * (1 - V) / V
187 float V = (float) t_high / (float) t_low + 0.0002f;
189 float Rth = R_EXT * (1 - V) / V;
191 printf("max6691 channel %d: high %5u low %5u ohms: %7g\n", i, t_high, t_low, Rth);
195 static const struct ao_cmds ao_max6691_cmds[] = {
196 { ao_max6691_dump, "q\0Thermistor test" },
202 ao_max6691_init(void)
204 ao_cmd_register(&ao_max6691_cmds[0]);
206 struct stm_tim234 *tim = AO_MAX6691_TIMER;
208 stm_rcc.apb1enr |= (1 << AO_MAX6691_TIMER_ENABLE);
211 tim->psc = (AO_TIM23467_CLK / 4000000) - 1; /* run the timer at 4MHz */
215 * XXX This assumes we're using CH2, which is true on TeleFireOne v2.0
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));
226 tim->cr2 = ((0 << STM_TIM234_CR2_TI1S) |
227 (STM_TIM234_CR2_MMS_RESET<< STM_TIM234_CR2_MMS) |
228 (0 << STM_TIM234_CR2_CCDS));
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));
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));
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);
256 ao_add_task(&ao_max6691_task, ao_max6691, "max6691");