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+
2260static 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+
29166static 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+
44196static 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+
49239DEVICE_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