cancel
Showing results for 
Search instead for 
Did you mean: 

ADC Calibration Return Fails due to ADC_CALIBRATION_TIMEOUT on STM32G030X6

ogulcanyanik
Associate

First of all , I read this post (https://community.st.com/t5/stm32-mcus-products/adc-calibration-doesn-t-complete-on-stm32g0c1re/td-p/590459)  that was previously opened on this subject. I think the issue is noy completly resolved because there is a misunderstanding. 

I'm using STM32Cube MCU Package for STM32G0 Series, software package.

As @Imen.D  replied on 2024-03-26 , 04:44 pm in this post (https://community.st.com/t5/stm32-mcus-products/adc-calibration-doesn-t-complete-on-stm32g0c1re/td-p/590459:(

  • In fact, the delay is required for ADC disable to be effective (therefore, applicable between ADC disable and calibration start, or between ADC disable and calibration factor set). 

I couldnt see delay placed in the relevant code sections. 

Even if I put uS delays to those sections. It didnt work. 

 

Here is the problem : 

HAL_StatusTypeDef HAL_ADCEx_Calibration_Start(ADC_HandleTypeDef *hadc)
{
HAL_StatusTypeDef tmp_hal_status;
__IO uint32_t wait_loop_index = 0UL;
uint32_t backup_setting_cfgr1;
uint32_t calibration_index;
uint32_t calibration_factor_accumulated = 0;
uint32_t tickstart;
uint32_t adc_clk_async_presc;
__IO uint32_t delay_cpu_cycles;

/* Check the parameters */
assert_param(IS_ADC_ALL_INSTANCE(hadc->Instance));

__HAL_LOCK(hadc);

/* Calibration prerequisite: ADC must be disabled. */

/* Disable the ADC (if not already disabled) */
tmp_hal_status = ADC_Disable(hadc);
delay_uS(20); // added by me
/* Check if ADC is effectively disabled */
if (LL_ADC_IsEnabled(hadc->Instance) == 0UL)
{
/* Set ADC state */
ADC_STATE_CLR_SET(hadc->State,
HAL_ADC_STATE_REG_BUSY,
HAL_ADC_STATE_BUSY_INTERNAL);

/* Manage settings impacting calibration */
/* - Disable ADC mode auto power-off */
/* - Disable ADC DMA transfer request during calibration */
/* Note: Specificity of this STM32 series: Calibration factor is */
/* available in data register and also transferred by DMA. */
/* To not insert ADC calibration factor among ADC conversion data */
/* in array variable, DMA transfer must be disabled during */
/* calibration. */
backup_setting_cfgr1 = READ_BIT(hadc->Instance->CFGR1, ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG | ADC_CFGR1_AUTOFF);
CLEAR_BIT(hadc->Instance->CFGR1, ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG | ADC_CFGR1_AUTOFF);

/* ADC calibration procedure */
/* Note: Perform an averaging of 8 calibrations for optimized accuracy */
for (calibration_index = 0UL; calibration_index < 8UL; calibration_index++)
{
/* Start ADC calibration */
LL_ADC_StartCalibration(hadc->Instance);
delay_uS(20); // added by me
/* Wait for calibration completion */
while (LL_ADC_IsCalibrationOnGoing(hadc->Instance) != 0UL)
{
wait_loop_index++;
if (wait_loop_index >= ADC_CALIBRATION_TIMEOUT)
{
/* Update ADC state machine to error */
ADC_STATE_CLR_SET(hadc->State,
HAL_ADC_STATE_BUSY_INTERNAL,
HAL_ADC_STATE_ERROR_INTERNAL);

__HAL_UNLOCK(hadc);

return HAL_ERROR; // Code always return HAL_ERROR  due to ADC_CALIBRATION_TIMEOUT
}
}

calibration_factor_accumulated += LL_ADC_GetCalibrationFactor(hadc->Instance);
}
/* Compute average */
calibration_factor_accumulated /= calibration_index;

/* Apply calibration factor (requires ADC enable and disable process) */
LL_ADC_Enable(hadc->Instance);

/* Case of ADC clocked at low frequency: Delay required between ADC enable and disable actions */
if (LL_ADC_GetClock(hadc->Instance) == LL_ADC_CLOCK_ASYNC)
{
adc_clk_async_presc = LL_ADC_GetCommonClock(__LL_ADC_COMMON_INSTANCE(hadc->Instance));

if (adc_clk_async_presc >= LL_ADC_CLOCK_ASYNC_DIV16)
{
/* Delay loop initialization and execution */
/* Delay depends on ADC clock prescaler: Compute ADC clock asynchronous prescaler to decimal format */
delay_cpu_cycles = (1UL << ((adc_clk_async_presc >> ADC_CCR_PRESC_Pos) - 3UL));
/* Divide variable by 2 to compensate partially CPU processing cycles */
delay_cpu_cycles >>= 1UL;

while (delay_cpu_cycles != 0UL)
{
delay_cpu_cycles--;
}
}
}

LL_ADC_SetCalibrationFactor(hadc->Instance, calibration_factor_accumulated);
LL_ADC_Disable(hadc->Instance);

/* Wait for ADC effectively disabled before changing configuration */
/* Get tick count */
tickstart = HAL_GetTick();

while (LL_ADC_IsEnabled(hadc->Instance) != 0UL)
{
if ((HAL_GetTick() - tickstart) > ADC_DISABLE_TIMEOUT)
{
/* New check to avoid false timeout detection in case of preemption */
if (LL_ADC_IsEnabled(hadc->Instance) != 0UL)
{
/* Update ADC state machine to error */
SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);

/* Set ADC error code to ADC peripheral internal error */
SET_BIT(hadc->ErrorCode, HAL_ADC_ERROR_INTERNAL);

return HAL_ERROR;
}
}
}

/* Restore configuration after calibration */
SET_BIT(hadc->Instance->CFGR1, backup_setting_cfgr1);

/* Set ADC state */
ADC_STATE_CLR_SET(hadc->State,
HAL_ADC_STATE_BUSY_INTERNAL,
HAL_ADC_STATE_READY);
}
else
{
SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);

/* Note: No need to update variable "tmp_hal_status" here: already set */
/* to state "HAL_ERROR" by function disabling the ADC. */
}

__HAL_UNLOCK(hadc);

return tmp_hal_status;
}

 

 

 

1 REPLY 1
Imen.D
ST Employee

Hello @ogulcanyanik and welcome to the Community,

I will check your reported issue and update this thread accordingly.

An internal ticket ID 197378 is submitted to track and work on this request.

(PS: Ticket ID 197378 is an internal tracking number and is not accessible outside of ST).

When your question is answered, please close this topic by clicking "Accept as Solution".
Thanks
Imen