aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios/os/hal/ports/STM32/STM32L1xx/hal_adc_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios/os/hal/ports/STM32/STM32L1xx/hal_adc_lld.c')
-rw-r--r--lib/chibios/os/hal/ports/STM32/STM32L1xx/hal_adc_lld.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/lib/chibios/os/hal/ports/STM32/STM32L1xx/hal_adc_lld.c b/lib/chibios/os/hal/ports/STM32/STM32L1xx/hal_adc_lld.c
new file mode 100644
index 000000000..b61a73d20
--- /dev/null
+++ b/lib/chibios/os/hal/ports/STM32/STM32L1xx/hal_adc_lld.c
@@ -0,0 +1,294 @@
1/*
2 ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17/**
18 * @file STM32L1xx/hal_adc_lld.c
19 * @brief STM32L1xx ADC subsystem low level driver source.
20 *
21 * @addtogroup ADC
22 * @{
23 */
24
25#include "hal.h"
26
27#if HAL_USE_ADC || defined(__DOXYGEN__)
28
29/*===========================================================================*/
30/* Driver local definitions. */
31/*===========================================================================*/
32
33/*===========================================================================*/
34/* Driver exported variables. */
35/*===========================================================================*/
36
37/** @brief ADC1 driver identifier.*/
38#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__)
39ADCDriver ADCD1;
40#endif
41
42/*===========================================================================*/
43/* Driver local variables and types. */
44/*===========================================================================*/
45
46/*===========================================================================*/
47/* Driver local functions. */
48/*===========================================================================*/
49
50/**
51 * @brief ADC DMA ISR service routine.
52 *
53 * @param[in] adcp pointer to the @p ADCDriver object
54 * @param[in] flags pre-shifted content of the ISR register
55 */
56static void adc_lld_serve_rx_interrupt(ADCDriver *adcp, uint32_t flags) {
57
58 /* DMA errors handling.*/
59 if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
60 /* DMA, this could help only if the DMA tries to access an unmapped
61 address space or violates alignment rules.*/
62 _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE);
63 }
64 else {
65 /* It is possible that the conversion group has already be reset by the
66 ADC error handler, in this case this interrupt is spurious.*/
67 if (adcp->grpp != NULL) {
68 if ((flags & STM32_DMA_ISR_TCIF) != 0) {
69 /* Transfer complete processing.*/
70 _adc_isr_full_code(adcp);
71 }
72 else if ((flags & STM32_DMA_ISR_HTIF) != 0) {
73 /* Half transfer processing.*/
74 _adc_isr_half_code(adcp);
75 }
76 }
77 }
78}
79
80/*===========================================================================*/
81/* Driver interrupt handlers. */
82/*===========================================================================*/
83
84#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__)
85/**
86 * @brief ADC interrupt handler.
87 *
88 * @isr
89 */
90OSAL_IRQ_HANDLER(Vector88) {
91 uint32_t sr;
92
93 OSAL_IRQ_PROLOGUE();
94
95 sr = ADC1->SR;
96 ADC1->SR = 0;
97 /* Note, an overflow may occur after the conversion ended before the driver
98 is able to stop the ADC, this is why the DMA channel is checked too.*/
99 if ((sr & ADC_SR_OVR) && (dmaStreamGetTransactionSize(ADCD1.dmastp) > 0)) {
100 /* ADC overflow condition, this could happen only if the DMA is unable
101 to read data fast enough.*/
102 if (ADCD1.grpp != NULL)
103 _adc_isr_error_code(&ADCD1, ADC_ERR_OVERFLOW);
104 }
105 /* CHTODO: Add here analog watchdog handling.*/
106
107 OSAL_IRQ_EPILOGUE();
108}
109#endif
110
111/*===========================================================================*/
112/* Driver exported functions. */
113/*===========================================================================*/
114
115/**
116 * @brief Low level ADC driver initialization.
117 *
118 * @notapi
119 */
120void adc_lld_init(void) {
121
122#if STM32_ADC_USE_ADC1
123 /* Driver initialization.*/
124 adcObjectInit(&ADCD1);
125 ADCD1.adc = ADC1;
126 ADCD1.dmastp = NULL;
127 ADCD1.dmamode = STM32_DMA_CR_PL(STM32_ADC_ADC1_DMA_PRIORITY) |
128 STM32_DMA_CR_DIR_P2M |
129 STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD |
130 STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE |
131 STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE;
132#endif
133
134 /* The shared vector is initialized on driver initialization and never
135 disabled.*/
136 nvicEnableVector(ADC1_IRQn, STM32_ADC_IRQ_PRIORITY);
137}
138
139/**
140 * @brief Configures and activates the ADC peripheral.
141 *
142 * @param[in] adcp pointer to the @p ADCDriver object
143 *
144 * @notapi
145 */
146void adc_lld_start(ADCDriver *adcp) {
147
148 /* If in stopped state then enables the ADC and DMA clocks.*/
149 if (adcp->state == ADC_STOP) {
150#if STM32_ADC_USE_ADC1
151 if (&ADCD1 == adcp) {
152 adcp->dmastp = dmaStreamAllocI(STM32_DMA_STREAM_ID(1, 1),
153 STM32_ADC_ADC1_DMA_IRQ_PRIORITY,
154 (stm32_dmaisr_t)adc_lld_serve_rx_interrupt,
155 (void *)adcp);
156 osalDbgAssert(adcp->dmastp != NULL, "unable to allocate stream");
157
158 dmaStreamSetPeripheral(adcp->dmastp, &ADC1->DR);
159 rccEnableADC1(true);
160 }
161#endif /* STM32_ADC_USE_ADC1 */
162
163 ADC->CCR = (ADC->CCR & ADC_CCR_TSVREFE) | (STM32_ADC_ADCPRE << 16);
164
165 /* ADC initial setup, starting the analog part here in order to reduce
166 the latency when starting a conversion.*/
167 adcp->adc->CR1 = 0;
168 adcp->adc->CR2 = 0;
169 adcp->adc->CR2 = ADC_CR2_ADON;
170 }
171}
172
173/**
174 * @brief Deactivates the ADC peripheral.
175 *
176 * @param[in] adcp pointer to the @p ADCDriver object
177 *
178 * @notapi
179 */
180void adc_lld_stop(ADCDriver *adcp) {
181
182 /* If in ready state then disables the ADC clock and analog part.*/
183 if (adcp->state == ADC_READY) {
184 dmaStreamFreeI(adcp->dmastp);
185 adcp->dmastp = NULL;
186
187 adcp->adc->CR1 = 0;
188 adcp->adc->CR2 = 0;
189
190#if STM32_ADC_USE_ADC1
191 if (&ADCD1 == adcp)
192 rccDisableADC1();
193#endif
194 }
195}
196
197/**
198 * @brief Starts an ADC conversion.
199 *
200 * @param[in] adcp pointer to the @p ADCDriver object
201 *
202 * @notapi
203 */
204void adc_lld_start_conversion(ADCDriver *adcp) {
205 uint32_t mode;
206 uint32_t cr2;
207 const ADCConversionGroup *grpp = adcp->grpp;
208
209 /* DMA setup.*/
210 mode = adcp->dmamode;
211 if (grpp->circular) {
212 mode |= STM32_DMA_CR_CIRC;
213 if (adcp->depth > 1) {
214 /* If circular buffer depth > 1, then the half transfer interrupt
215 is enabled in order to allow streaming processing.*/
216 mode |= STM32_DMA_CR_HTIE;
217 }
218 }
219 dmaStreamSetMemory0(adcp->dmastp, adcp->samples);
220 dmaStreamSetTransactionSize(adcp->dmastp, (uint32_t)grpp->num_channels *
221 (uint32_t)adcp->depth);
222 dmaStreamSetMode(adcp->dmastp, mode);
223 dmaStreamEnable(adcp->dmastp);
224
225 /* ADC setup.*/
226 adcp->adc->SR = 0;
227 adcp->adc->SMPR1 = grpp->smpr1;
228 adcp->adc->SMPR2 = grpp->smpr2;
229 adcp->adc->SMPR3 = grpp->smpr3;
230 adcp->adc->SQR1 = grpp->sqr1;
231 adcp->adc->SQR2 = grpp->sqr2;
232 adcp->adc->SQR3 = grpp->sqr3;
233 adcp->adc->SQR4 = grpp->sqr4;
234 adcp->adc->SQR5 = grpp->sqr5;
235
236 /* ADC configuration and start.*/
237 adcp->adc->CR1 = grpp->cr1 | ADC_CR1_OVRIE | ADC_CR1_SCAN;
238
239 /* Enforcing the mandatory bits in CR2.*/
240 cr2 = grpp->cr2 | ADC_CR2_DMA | ADC_CR2_DDS | ADC_CR2_ADON;
241
242 /* The start method is different dependign if HW or SW triggered, the
243 start is performed using the method specified in the CR2 configuration.*/
244 if ((cr2 & ADC_CR2_SWSTART) != 0) {
245 /* Initializing CR2 while keeping ADC_CR2_SWSTART at zero.*/
246 adcp->adc->CR2 = (cr2 | ADC_CR2_CONT) & ~ADC_CR2_SWSTART;
247
248 /* Finally enabling ADC_CR2_SWSTART.*/
249 adcp->adc->CR2 = (cr2 | ADC_CR2_CONT);
250 }
251 else
252 adcp->adc->CR2 = cr2;
253}
254
255/**
256 * @brief Stops an ongoing conversion.
257 *
258 * @param[in] adcp pointer to the @p ADCDriver object
259 *
260 * @notapi
261 */
262void adc_lld_stop_conversion(ADCDriver *adcp) {
263
264 dmaStreamDisable(adcp->dmastp);
265 adcp->adc->CR1 = 0;
266 adcp->adc->CR2 = 0;
267 adcp->adc->CR2 = ADC_CR2_ADON;
268}
269
270/**
271 * @brief Enables the TSVREFE bit.
272 * @details The TSVREFE bit is required in order to sample the internal
273 * temperature sensor and internal reference voltage.
274 * @note This is an STM32-only functionality.
275 */
276void adcSTM32EnableTSVREFE(void) {
277
278 ADC->CCR |= ADC_CCR_TSVREFE;
279}
280
281/**
282 * @brief Disables the TSVREFE bit.
283 * @details The TSVREFE bit is required in order to sample the internal
284 * temperature sensor and internal reference voltage.
285 * @note This is an STM32-only functionality.
286 */
287void adcSTM32DisableTSVREFE(void) {
288
289 ADC->CCR &= ~ADC_CCR_TSVREFE;
290}
291
292#endif /* HAL_USE_ADC */
293
294/** @} */