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];
47 ao_max6691_sample(struct ao_max6691 *ao_max6691)
49 struct stm_tim234 *tim = AO_MAX6691_TIMER;
53 memset(&ao_max6691_raw, '\0', sizeof (ao_max6691_raw));
54 /* Get the DMA engine ready */
55 ao_dma_set_transfer(AO_MAX6691_DMA,
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);
69 /* Prod the max6691 */
70 ao_set_output(AO_MAX6691_GPIO, AO_MAX6691_PIN, 0);
72 for (i = 0; i < 100; i++)
74 ao_set_input(AO_MAX6691_GPIO, AO_MAX6691_PIN);
75 for (i = 0; i < 100; i++)
78 /* Reset the timer count */
81 /* Switch the pin to timer input mode */
82 stm_afr_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_AFR_AF1);
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));
97 /* Enable event generation on channel 2 */
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);
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();
113 /* Disable event generation */
116 /* Disable capture */
120 tim->cr1 &= ~(1 << STM_TIM234_CR1_CEN);
122 /* Switch back to GPIO mode */
123 stm_moder_set(AO_MAX6691_GPIO, AO_MAX6691_PIN, STM_MODER_INPUT);
126 ao_dma_done_transfer(AO_MAX6691_DMA);
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);
134 #define R_EXT 1000.0f
137 ao_max6691_test(void)
139 struct ao_max6691 ao_max6691;
141 printf("Testing MAX6691\n"); flush();
142 ao_max6691_sample(&ao_max6691);
145 for (i = 0; i < AO_MAX6691_SAMPLES; i++)
146 printf("%d: %5u\n", i, ao_max6691_raw[i]);
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;
153 * From the MAX6691 data sheet
156 * ----- = ---- - 0.0002 = ---------- - 0.0002
157 * Tlow Vref Rext - Rth
159 * We want to find Rth given Rext and the timing values
162 * ----- + 0.0002 = ----------
165 * V = (Thigh / Tlow + 0.0002)
167 * (Rext + Rth) * V = Rext
169 * Rext * V + Rth * V = Rext
171 * Rth * V = Rext - Rext * V
173 * Rth * V = Rext * (1 - V)
175 * Rth = Rext * (1 - V) / V
178 float V = (float) t_high / (float) t_low + 0.0002f;
180 float Rth = R_EXT * (1 - V) / V;
182 printf("%d: high %5u low %5u ohms: %7g\n", i, t_high, t_low, Rth);
186 static const struct ao_cmds ao_max6691_cmds[] = {
187 { ao_max6691_test, "q\0Thermistor test" },
193 ao_max6691_init(void)
195 ao_cmd_register(&ao_max6691_cmds[0]);
197 struct stm_tim234 *tim = AO_MAX6691_TIMER;
199 stm_rcc.apb1enr |= (1 << AO_MAX6691_TIMER_ENABLE);
202 tim->psc = (AO_TIM23467_CLK / 4000000) - 1; /* run the timer at 4MHz */
206 * XXX This assumes we're using CH2, which is true on TeleFireOne v2.0
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));
217 tim->cr2 = ((0 << STM_TIM234_CR2_TI1S) |
218 (STM_TIM234_CR2_MMS_RESET<< STM_TIM234_CR2_MMS) |
219 (0 << STM_TIM234_CR2_CCDS));
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));
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));
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);