1 /**
2   ******************************************************************************
3   * @file    stm32u0xx_hal_adc_ex.c
4   * @author  MCD Application Team
5   * @brief   This file provides firmware functions to manage the following
6   *          functionalities of the Analog to Digital Converter (ADC)
7   *          peripheral:
8   *           + Peripheral Control functions
9   *          Other functions (generic functions) are available in file
10   *          "stm32u0xx_hal_adc.c".
11   *
12   ******************************************************************************
13   * @attention
14   *
15   * Copyright (c) 2023 STMicroelectronics.
16   * All rights reserved.
17   *
18   * This software is licensed under terms that can be found in the LICENSE file
19   * in the root directory of this software component.
20   * If no LICENSE file comes with this software, it is provided AS-IS.
21   *
22   ******************************************************************************
23   @verbatim
24   [..]
25   (@) Sections "ADC peripheral features" and "How to use this driver" are
26       available in file of generic functions "stm32u0xx_hal_adc.c".
27   [..]
28   @endverbatim
29   ******************************************************************************
30   */
31 
32 /* Includes ------------------------------------------------------------------*/
33 #include "stm32u0xx_hal.h"
34 
35 /** @addtogroup STM32U0xx_HAL_Driver
36   * @{
37   */
38 
39 /** @defgroup ADCEx ADCEx
40   * @brief ADC Extended HAL module driver
41   * @{
42   */
43 
44 #ifdef HAL_ADC_MODULE_ENABLED
45 
46 /* Private typedef -----------------------------------------------------------*/
47 /* Private define ------------------------------------------------------------*/
48 
49 /** @defgroup ADCEx_Private_Constants ADC Extended Private Constants
50   * @{
51   */
52 
53 /* Fixed timeout value for ADC calibration.                                   */
54 /* Values defined to be higher than worst cases: maximum ratio between ADC    */
55 /* and CPU clock frequencies.                                                 */
56 /* Example of profile low frequency : ADC frequency at 31.25kHz (ADC clock    */
57 /* source PLL 8MHz, ADC clock prescaler 256), CPU frequency 48MHz.            */
58 /* Calibration time max = 116 / fADC (refer to datasheet)                     */
59 /*                      = 178 176 CPU cycles                                  */
60 #define ADC_CALIBRATION_TIMEOUT         (178176UL)   /*!< ADC calibration time-out value (unit: CPU cycles) */
61 #define ADC_DISABLE_TIMEOUT             (2UL)
62 
63 /**
64   * @}
65   */
66 
67 /* Private macro -------------------------------------------------------------*/
68 /* Private variables ---------------------------------------------------------*/
69 /* Private function prototypes -----------------------------------------------*/
70 /* Exported functions --------------------------------------------------------*/
71 
72 /** @defgroup ADCEx_Exported_Functions ADC Extended Exported Functions
73   * @{
74   */
75 
76 /** @defgroup ADCEx_Exported_Functions_Group1 Extended Input and Output operation functions
77   * @brief    Extended IO operation functions
78   *
79 @verbatim
80  ===============================================================================
81                       ##### IO operation functions #####
82  ===============================================================================
83     [..]  This section provides functions allowing to:
84 
85       (+) Perform the ADC self-calibration.
86       (+) Get calibration factors.
87       (+) Set calibration factors.
88 
89 @endverbatim
90   * @{
91   */
92 
93 /**
94   * @brief  Perform an ADC automatic self-calibration
95   *         Calibration prerequisite: ADC must be disabled (execute this
96   *         function before HAL_ADC_Start() or after HAL_ADC_Stop() ).
97   * @note   Calibration factor can be read after calibration, using function
98   *         HAL_ADC_GetValue() (value on 7 bits: from DR[6;0]).
99   * @param  hadc       ADC handle
100   * @retval HAL status
101   */
HAL_ADCEx_Calibration_Start(ADC_HandleTypeDef * hadc)102 HAL_StatusTypeDef HAL_ADCEx_Calibration_Start(ADC_HandleTypeDef *hadc)
103 {
104   HAL_StatusTypeDef tmp_hal_status;
105   __IO uint32_t wait_loop_index = 0UL;
106   uint32_t backup_setting_cfgr1;
107   uint32_t calibration_index;
108   uint32_t calibration_factor_accumulated = 0;
109   uint32_t tickstart;
110   uint32_t adc_clk_async_presc;
111   __IO uint32_t delay_cpu_cycles;
112 
113   /* Check the parameters */
114   assert_param(IS_ADC_ALL_INSTANCE(hadc->Instance));
115 
116   __HAL_LOCK(hadc);
117 
118   /* Calibration prerequisite: ADC must be disabled. */
119 
120   /* Disable the ADC (if not already disabled) */
121   tmp_hal_status = ADC_Disable(hadc);
122 
123   /* Check if ADC is effectively disabled */
124   if (LL_ADC_IsEnabled(hadc->Instance) == 0UL)
125   {
126     /* Set ADC state */
127     ADC_STATE_CLR_SET(hadc->State,
128                       HAL_ADC_STATE_REG_BUSY,
129                       HAL_ADC_STATE_BUSY_INTERNAL);
130 
131     /* Manage settings impacting calibration                                  */
132     /* - Disable ADC mode auto power-off                                      */
133     /* - Disable ADC DMA transfer request during calibration                  */
134     /* Note: Specificity of this STM32 series: Calibration factor is          */
135     /*       available in data register and also transferred by DMA.          */
136     /*       To not insert ADC calibration factor among ADC conversion data   */
137     /*       in array variable, DMA transfer must be disabled during          */
138     /*       calibration.                                                     */
139     backup_setting_cfgr1 = READ_BIT(hadc->Instance->CFGR1, ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG | ADC_CFGR1_AUTOFF);
140     CLEAR_BIT(hadc->Instance->CFGR1, ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG | ADC_CFGR1_AUTOFF);
141 
142     /* ADC calibration procedure */
143     /* Note: Perform an averaging of 8 calibrations for optimized accuracy */
144     for (calibration_index = 0UL; calibration_index < 8UL; calibration_index++)
145     {
146       /* Start ADC calibration */
147       LL_ADC_StartCalibration(hadc->Instance);
148 
149       /* Wait for calibration completion */
150       while (LL_ADC_IsCalibrationOnGoing(hadc->Instance) != 0UL)
151       {
152         wait_loop_index++;
153         if (wait_loop_index >= ADC_CALIBRATION_TIMEOUT)
154         {
155           /* Update ADC state machine to error */
156           ADC_STATE_CLR_SET(hadc->State,
157                             HAL_ADC_STATE_BUSY_INTERNAL,
158                             HAL_ADC_STATE_ERROR_INTERNAL);
159 
160           __HAL_UNLOCK(hadc);
161 
162           return HAL_ERROR;
163         }
164       }
165 
166       calibration_factor_accumulated += LL_ADC_GetCalibrationFactor(hadc->Instance);
167     }
168     /* Compute average */
169     calibration_factor_accumulated /= calibration_index;
170 
171     /* Apply calibration factor (requires ADC enable and disable process) */
172     LL_ADC_Enable(hadc->Instance);
173 
174     /* Case of ADC clocked at low frequency: Delay required between ADC enable and disable actions */
175     if (LL_ADC_GetClock(hadc->Instance) == LL_ADC_CLOCK_ASYNC)
176     {
177       adc_clk_async_presc = LL_ADC_GetCommonClock(__LL_ADC_COMMON_INSTANCE(hadc->Instance));
178 
179       if (adc_clk_async_presc >= LL_ADC_CLOCK_ASYNC_DIV16)
180       {
181         /* Delay loop initialization and execution */
182         /* Delay depends on ADC clock prescaler: Compute ADC clock asynchronous prescaler to decimal format */
183         delay_cpu_cycles = (1UL << ((adc_clk_async_presc >> ADC_CCR_PRESC_Pos) - 3UL));
184         /* Divide variable by 2 to compensate partially CPU processing cycles */
185         delay_cpu_cycles >>= 1UL;
186 
187         while (delay_cpu_cycles != 0UL)
188         {
189           delay_cpu_cycles--;
190         }
191       }
192     }
193 
194     LL_ADC_SetCalibrationFactor(hadc->Instance, calibration_factor_accumulated);
195     LL_ADC_Disable(hadc->Instance);
196 
197     /* Wait for ADC effectively disabled before changing configuration */
198     /* Get tick count */
199     tickstart = HAL_GetTick();
200 
201     while (LL_ADC_IsEnabled(hadc->Instance) != 0UL)
202     {
203       if ((HAL_GetTick() - tickstart) > ADC_DISABLE_TIMEOUT)
204       {
205         /* New check to avoid false timeout detection in case of preemption */
206         if (LL_ADC_IsEnabled(hadc->Instance) != 0UL)
207         {
208           /* Update ADC state machine to error */
209           SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);
210 
211           /* Set ADC error code to ADC peripheral internal error */
212           SET_BIT(hadc->ErrorCode, HAL_ADC_ERROR_INTERNAL);
213 
214           return HAL_ERROR;
215         }
216       }
217     }
218 
219     /* Restore configuration after calibration */
220     SET_BIT(hadc->Instance->CFGR1, backup_setting_cfgr1);
221 
222     /* Set ADC state */
223     ADC_STATE_CLR_SET(hadc->State,
224                       HAL_ADC_STATE_BUSY_INTERNAL,
225                       HAL_ADC_STATE_READY);
226   }
227   else
228   {
229     SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);
230 
231     /* Note: No need to update variable "tmp_hal_status" here: already set    */
232     /*       to state "HAL_ERROR" by function disabling the ADC.              */
233   }
234 
235   __HAL_UNLOCK(hadc);
236 
237   return tmp_hal_status;
238 }
239 
240 /**
241   * @brief  Get the calibration factor.
242   * @param hadc ADC handle.
243   * @retval Calibration value.
244   */
HAL_ADCEx_Calibration_GetValue(const ADC_HandleTypeDef * hadc)245 uint32_t HAL_ADCEx_Calibration_GetValue(const ADC_HandleTypeDef *hadc)
246 {
247   /* Check the parameters */
248   assert_param(IS_ADC_ALL_INSTANCE(hadc->Instance));
249 
250   /* Return the selected ADC calibration value */
251   return ((hadc->Instance->CALFACT) & 0x0000007FU);
252 }
253 
254 /**
255   * @brief  Set the calibration factor to overwrite automatic conversion result.
256   *         ADC must be enabled and no conversion is ongoing.
257   * @param hadc ADC handle
258   * @param CalibrationFactor Calibration factor (coded on 7 bits maximum)
259   * @retval HAL state
260   */
HAL_ADCEx_Calibration_SetValue(ADC_HandleTypeDef * hadc,uint32_t CalibrationFactor)261 HAL_StatusTypeDef HAL_ADCEx_Calibration_SetValue(ADC_HandleTypeDef *hadc, uint32_t CalibrationFactor)
262 {
263   HAL_StatusTypeDef tmp_hal_status = HAL_OK;
264   uint32_t tmp_adc_is_conversion_on_going_regular;
265 
266   /* Check the parameters */
267   assert_param(IS_ADC_ALL_INSTANCE(hadc->Instance));
268   assert_param(IS_ADC_CALFACT(CalibrationFactor));
269 
270   __HAL_LOCK(hadc);
271 
272   /* Verification of hardware constraints before modifying the calibration    */
273   /* factors register: ADC must be enabled, no conversion on going.           */
274   tmp_adc_is_conversion_on_going_regular = LL_ADC_REG_IsConversionOngoing(hadc->Instance);
275 
276   if ((LL_ADC_IsEnabled(hadc->Instance) != 0UL)
277       && (tmp_adc_is_conversion_on_going_regular == 0UL)
278      )
279   {
280     hadc->Instance->CALFACT &= ~ADC_CALFACT_CALFACT;
281     hadc->Instance->CALFACT |= CalibrationFactor;
282   }
283   else
284   {
285     /* Update ADC state machine */
286     SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_CONFIG);
287     /* Update ADC error code */
288     SET_BIT(hadc->ErrorCode, HAL_ADC_ERROR_INTERNAL);
289 
290     /* Update ADC state machine to error */
291     tmp_hal_status = HAL_ERROR;
292   }
293 
294   __HAL_UNLOCK(hadc);
295 
296   return tmp_hal_status;
297 }
298 
299 /**
300   * @brief  Analog watchdog 2 callback in non-blocking mode.
301   * @param hadc ADC handle
302   * @retval None
303   */
HAL_ADCEx_LevelOutOfWindow2Callback(ADC_HandleTypeDef * hadc)304 __weak void HAL_ADCEx_LevelOutOfWindow2Callback(ADC_HandleTypeDef *hadc)
305 {
306   /* Prevent unused argument(s) compilation warning */
307   UNUSED(hadc);
308 
309   /* NOTE : This function should not be modified. When the callback is needed,
310             function HAL_ADCEx_LevelOutOfWindow2Callback must be implemented in the user file.
311   */
312 }
313 
314 /**
315   * @brief  Analog watchdog 3 callback in non-blocking mode.
316   * @param hadc ADC handle
317   * @retval None
318   */
HAL_ADCEx_LevelOutOfWindow3Callback(ADC_HandleTypeDef * hadc)319 __weak void HAL_ADCEx_LevelOutOfWindow3Callback(ADC_HandleTypeDef *hadc)
320 {
321   /* Prevent unused argument(s) compilation warning */
322   UNUSED(hadc);
323 
324   /* NOTE : This function should not be modified. When the callback is needed,
325             function HAL_ADCEx_LevelOutOfWindow3Callback must be implemented in the user file.
326   */
327 }
328 
329 
330 /**
331   * @brief  End Of Sampling callback in non-blocking mode.
332   * @param hadc ADC handle
333   * @retval None
334   */
HAL_ADCEx_EndOfSamplingCallback(ADC_HandleTypeDef * hadc)335 __weak void HAL_ADCEx_EndOfSamplingCallback(ADC_HandleTypeDef *hadc)
336 {
337   /* Prevent unused argument(s) compilation warning */
338   UNUSED(hadc);
339 
340   /* NOTE : This function should not be modified. When the callback is needed,
341             function HAL_ADCEx_EndOfSamplingCallback must be implemented in the user file.
342   */
343 }
344 
345 /**
346   * @brief  ADC channel configuration ready callback in non-blocking mode.
347   * @param hadc ADC handle
348   * @retval None
349   */
HAL_ADCEx_ChannelConfigReadyCallback(ADC_HandleTypeDef * hadc)350 __weak void HAL_ADCEx_ChannelConfigReadyCallback(ADC_HandleTypeDef *hadc)
351 {
352   /* Prevent unused argument(s) compilation warning */
353   UNUSED(hadc);
354 
355   /* NOTE : This function should not be modified. When the callback is needed,
356             function HAL_ADCEx_ChannelConfigReadyCallback must be implemented in the user file.
357   */
358 }
359 
360 /**
361   * @}
362   */
363 
364 /**
365   * @brief  Disable ADC voltage regulator.
366   * @note   Disabling voltage regulator allows to save power. This operation can
367   *         be carried out only when ADC is disabled.
368   * @note   To enable again the voltage regulator, the user is expected to
369   *         resort to HAL_ADC_Init() API.
370   * @param hadc ADC handle
371   * @retval HAL status
372   */
HAL_ADCEx_DisableVoltageRegulator(ADC_HandleTypeDef * hadc)373 HAL_StatusTypeDef HAL_ADCEx_DisableVoltageRegulator(ADC_HandleTypeDef *hadc)
374 {
375   HAL_StatusTypeDef tmp_hal_status;
376 
377   /* Check the parameters */
378   assert_param(IS_ADC_ALL_INSTANCE(hadc->Instance));
379 
380   /* Setting of this feature is conditioned to ADC state: ADC must be ADC disabled */
381   if (LL_ADC_IsEnabled(hadc->Instance) == 0UL)
382   {
383     LL_ADC_DisableInternalRegulator(hadc->Instance);
384     tmp_hal_status = HAL_OK;
385   }
386   else
387   {
388     tmp_hal_status = HAL_ERROR;
389   }
390 
391   return tmp_hal_status;
392 }
393 
394 /**
395   * @}
396   */
397 
398 /**
399   * @}
400   */
401 
402 #endif /* HAL_ADC_MODULE_ENABLED */
403 /**
404   * @}
405   */
406 
407 /**
408   * @}
409   */
410