Skip to content

Commit e97c7b6

Browse files
Marti Bolivarrsalveti
authored andcommitted
[LTD noup] drivers: sensor: add stm32f401x temperature driver
There is an issue with this driver. At present, there's no good way for board-level configuration (specifically, the voltages on the V_REF- and V_REF+ external pins, which are needed to convert ADC values to volts) to be made available portably to drivers. Hack around this here by hard-coding reasonable defaults of 0V for V_REF- and 3.3V for V_REF+. Signed-off-by: Marti Bolivar <marti.bolivar@linaro.org>
1 parent 52ce7ab commit e97c7b6

File tree

2 files changed

+223
-6
lines changed

2 files changed

+223
-6
lines changed

drivers/sensor/stm32/temp_stm32.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2017 Linaro Limited.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef _STM32_TEMP_H_
8+
#define _STM32_TEMP_H_
9+
10+
#include <stdint.h>
11+
12+
#include <device.h>
13+
#include <soc.h>
14+
15+
struct temp_stm32_config {
16+
ADC_TypeDef *adc;
17+
ADC_Common_TypeDef *adc_common;
18+
u8_t adc_channel;
19+
struct stm32_pclken pclken;
20+
};
21+
22+
struct temp_stm32_data {
23+
struct device *clock;
24+
};
25+
26+
#endif /* _STM32_TEMP_H_ */

drivers/sensor/stm32/temp_stm32f401x.c

Lines changed: 197 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,166 @@
1515
* as 45 degrees C.
1616
*/
1717

18-
#include <device.h>
18+
#define SYS_LOG_DOMAIN "TEMPSTM32F401"
19+
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SENSOR_LEVEL
20+
21+
#include <drivers/clock_control/stm32_clock_control.h>
22+
#include <errno.h>
23+
#include <logging/sys_log.h>
24+
#include <misc/__assert.h>
1925
#include <sensor.h>
2026

21-
/* TODO */
27+
#include "temp_stm32.h"
28+
29+
#define DEV_CFG(dev) \
30+
((const struct temp_stm32_config*)(dev)->config->config_info)
31+
#define DEV_DATA(dev) ((struct temp_stm32_data *)(dev)->driver_data)
32+
33+
/*
34+
* HACK: absent a better way to learn the analog voltage reference on
35+
* the board we are running on, hard-code V_REF+ to 3.3V, and assume
36+
* V_REF- = 0V.
37+
*/
38+
#define VREF_MILLIVOLTS 3300
39+
#define VREF_VOLTS ((float)VREF_MILLIVOLTS / 1000.0f)
40+
41+
/*
42+
* See STM32F401xD, STM32F401xE datasheet 6.3.21 and chip
43+
* reference manual and ST RM0368 chapter 11.
44+
*/
45+
#define STM32F401_V25 0.76f /* volts */
46+
#define STM32F401_AVG_SLOPE 0.0025f /* volts / (degree C) */
47+
48+
#define ADC_RESOLUTION_12BIT 0x00
49+
#define ADC_TO_VOLTS(adc_val) ((float)(adc_val) * VREF_VOLTS / 4095.0f)
50+
#define ADC_PRESCALER_PCLK_DIV_8 0x03
51+
#define ADC_SAMPLE_480_CYCLES 0x07
52+
#define ADC_ONE_CONVERSION 0x00
53+
54+
/* This is valid for STM32F401x. */
55+
#define ADC_TEMP_CHANNEL 18
56+
#define ADC_TEMP_SMPR(adc) (adc)->SMPR1
57+
#define ADC_TEMP_SMPR_SMP ADC_SMPR1_SMP18
58+
#define ADC_TEMP_SMPR_POS ADC_SMPR1_SMP18_Pos
59+
2260
static int temp_stm32f401x_sample_fetch(struct device *dev,
2361
enum sensor_channel chan)
2462
{
63+
const struct temp_stm32_config *cfg = DEV_CFG(dev);
64+
ADC_TypeDef *adc = cfg->adc;
65+
u32_t tmp;
66+
67+
__ASSERT(cfg->adc_channel == ADC_TEMP_CHANNEL,
68+
"Expected temperature sensor channel %d", ADC_TEMP_CHANNEL);
69+
70+
/*
71+
* Configure ADC for polled conversion of temperature sensor.
72+
*
73+
* TODO: this configuration could be improved (timing, etc.).
74+
*/
75+
76+
/*
77+
* CR1:
78+
*
79+
* - 12 bit resolution (the maximum).
80+
*
81+
* - Software initiated, polled conversion only (no
82+
* interrupts, injected groups, scan mode, etc.).
83+
*/
84+
85+
tmp = adc->CR1;
86+
tmp &= ~(ADC_CR1_RES | ADC_CR1_JDISCEN | ADC_CR1_DISCEN |
87+
ADC_CR1_JAUTO | ADC_CR1_SCAN | ADC_CR1_JEOCIE |
88+
ADC_CR1_AWDIE | ADC_CR1_EOCIE);
89+
tmp |= (ADC_RESOLUTION_12BIT << ADC_CR1_RES_Pos);
90+
adc->CR1 = tmp;
91+
92+
/*
93+
* CR2:
94+
*
95+
* - Software initiated, polled conversion of a single channel
96+
* only (no external triggers, DMA, continuous conversion).
97+
*
98+
* - Right-aligned data in DR.
99+
*
100+
* - EOC bit in SR should be set at end of conversion.
101+
*/
102+
103+
adc->CR2 &= ~(ADC_CR2_EXTEN | ADC_CR2_JEXTEN | ADC_CR2_ALIGN |
104+
ADC_CR2_EOCS | ADC_CR2_DDS | ADC_CR2_DMA |
105+
ADC_CR2_CONT);
106+
107+
/*
108+
* SMPR:
109+
*
110+
* Temperature sensor sample time is 480 ADC cycles (the
111+
* maximum).
112+
*/
113+
114+
tmp = ADC_TEMP_SMPR(adc);
115+
tmp &= ~ADC_TEMP_SMPR_SMP;
116+
tmp |= (ADC_SAMPLE_480_CYCLES << ADC_TEMP_SMPR_POS);
117+
ADC_TEMP_SMPR(adc) = tmp;
118+
119+
/*
120+
* SQRx: Convert channel. One channel is being converted.
121+
*/
122+
123+
tmp = adc->SQR3;
124+
tmp &= ~ADC_SQR3_SQ1;
125+
tmp |= (ADC_TEMP_CHANNEL << ADC_SQR3_SQ1_Pos);
126+
adc->SQR3 = tmp;
127+
128+
tmp = adc->SQR1;
129+
tmp &= ~ADC_SQR1_L;
130+
tmp |= (ADC_ONE_CONVERSION << ADC_SQR1_L_Pos);
131+
adc->SQR1 = tmp;
132+
133+
/*
134+
* Start the ADC conversion, and wait for it to complete.
135+
*/
136+
137+
adc->SR = ~(ADC_SR_EOC | ADC_SR_STRT);
138+
adc->CR2 |= ADC_CR2_SWSTART;
139+
140+
do {
141+
} while (!(adc->SR & ADC_SR_EOC));
142+
adc->SR = ~(ADC_SR_EOC | ADC_SR_STRT);
143+
25144
return 0;
26145
}
27146

28-
/* TODO */
147+
static inline float stm32f401x_temp_c(float v_sense)
148+
{
149+
/*
150+
* The voltage read by the temperature sensor, v_sense, is a
151+
* linear function of the temperature. The point-slope form of
152+
* the line is:
153+
*
154+
* (Temperature - 25.0°C) * Avg_Slope = v_sense - V_25
155+
*
156+
* Where V_25 is the measured voltage at 25°C, and Avg_Slope
157+
* is the slope of the line in V/°C.
158+
*
159+
* Use this formula to convert the voltage to a
160+
* temperature. See ST RM0368 11.9 and the chip datasheet for
161+
* more details.
162+
*/
163+
return ((v_sense - STM32F401_V25) / STM32F401_AVG_SLOPE + 25.0f);
164+
}
165+
29166
static int temp_stm32f401x_channel_get(struct device *dev,
30167
enum sensor_channel chan,
31168
struct sensor_value *val)
32169
{
33-
val->val1 = 0;
170+
const struct temp_stm32_config *cfg = DEV_CFG(dev);
171+
ADC_TypeDef *adc = cfg->adc;
172+
u32_t adc_dr = adc->DR;
173+
float v_sense = ADC_TO_VOLTS(adc_dr);
174+
float deg_c = stm32f401x_temp_c(v_sense);
175+
176+
/* TODO fractional part */
177+
val->val1 = (int32_t)deg_c;
34178
val->val2 = 0;
35179
return 0;
36180
}
@@ -40,12 +184,59 @@ static const struct sensor_driver_api temp_stm32f401x_driver_api = {
40184
.channel_get = temp_stm32f401x_channel_get,
41185
};
42186

43-
/* TODO */
187+
static inline void __temp_stm32f401x_get_clock(struct device *dev)
188+
{
189+
struct temp_stm32_data *data = DEV_DATA(dev);
190+
struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
191+
192+
__ASSERT_NO_MSG(clk);
193+
data->clock = clk;
194+
}
195+
44196
static int temp_stm32f401x_init(struct device *dev)
45197
{
198+
const struct temp_stm32_config *cfg = DEV_CFG(dev);
199+
struct temp_stm32_data *data = DEV_DATA(dev);
200+
ADC_TypeDef *adc = cfg->adc;
201+
ADC_Common_TypeDef *adc_common = cfg->adc_common;
202+
u32_t tmp;
203+
204+
/* Turn on digital clock. */
205+
__temp_stm32f401x_get_clock(dev);
206+
clock_control_on(data->clock,
207+
(clock_control_subsys_t*)&cfg->pclken);
208+
209+
/* Turn on ADC. */
210+
adc->CR2 |= ADC_CR2_ADON;
211+
212+
/* ADC configuration for temperature sensor.
213+
*
214+
* - ADC clock prescaler to slowest possible (HACK) to avoid
215+
* reading data sheets to figure out fastest possible.
216+
*
217+
* - Select temperature sensor, deselect VBAT (they are
218+
* mutually exclusive, and VBAT has precedence).
219+
*/
220+
tmp = adc_common->CCR;
221+
tmp &= ~(ADC_CCR_VBATE | ADC_CCR_ADCPRE);
222+
tmp |= ((ADC_PRESCALER_PCLK_DIV_8 << ADC_CCR_ADCPRE_Pos) |
223+
ADC_CCR_TSVREFE);
224+
adc_common->CCR = tmp;
225+
46226
return 0;
47227
}
48228

229+
static struct temp_stm32_config temp_stm32f401x_config = {
230+
.adc = ADC1,
231+
.adc_common = ADC,
232+
.adc_channel = ADC_TEMP_CHANNEL,
233+
.pclken = { .bus = STM32_CLOCK_BUS_APB2,
234+
.enr = LL_APB2_GRP1_PERIPH_ADC1 },
235+
};
236+
237+
static struct temp_stm32_data temp_stm32f401x_data;
238+
49239
DEVICE_AND_API_INIT(temp_stm32f401x, CONFIG_TEMP_STM32F401X_NAME,
50-
temp_stm32f401x_init, NULL, NULL, POST_KERNEL,
240+
temp_stm32f401x_init, &temp_stm32f401x_data,
241+
&temp_stm32f401x_config, POST_KERNEL,
51242
CONFIG_SENSOR_INIT_PRIORITY, &temp_stm32f401x_driver_api);

0 commit comments

Comments
 (0)