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