Skip to main content
Explorer
March 19, 2025
Question

Delay between HAL_ADCEx_Calibration_Start and HAL_ADC_Start_DMA required for unknown reason

  • March 19, 2025
  • 2 replies
  • 637 views

I want to initialise ADC in my FreeRTOS task. If I remove/comment out HAL_ADCEx_Calibration_Start function, put a delay (osDelay(1)) between it and HAL_ADC_Start_DMA or place a breakpoint at HAL_ADC_Start_DMA and pause code execution at it during debugging, everything is fine and dmaBuffer gets filled with data from ADC.

However, if I do not do any of those things, then __HAL_ADC_ENABLE(hadc) macro inside of ADC_Enable function called by HAL_ADC_Start_DMA won't set ADEN flag in CR register to enable ADC and ADC_Enable will return HAL_ERROR.

Is it because calibration requires some time to finish and until then ADC can't be enabled? If so, how can I make sure calibration ended? Thank You for help in advance!

 

My FreeRTOS task initialising ADC:

volatile HAL_StatusTypeDef adcCalibrationReturn;
volatile HAL_StatusTypeDef adcStartReturn;
uint32_t adcTaskTick=0;
void startAdcTask(void *argument) {

	adcCalibrationReturn=HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
	osDelay(1); //if this is uncommented or if program stops at instruction breakpoint placed right below, then ADC
				//sets HAL function ADC_Enable properly sets ISR to 1 and 0x10000001
	adcStartReturn=HAL_ADC_Start_DMA(&hadc1, (uint32_t *)dmaBuffer, sizeof(dmaBuffer));

	for (;;) {
		adcTaskTick++;
		osDelay(1000);
	}
}

 

ADC_Enable function called by HAL_ADC_Start_DMA. There __HAL_ADC_ENABLE(hadc) macro either succeeds or not, depending on whether delay has been used or not:

static HAL_StatusTypeDef ADC_Enable(ADC_HandleTypeDef* hadc)
{
 uint32_t tickstart = 0U;
 
 /* ADC enable and wait for ADC ready (in case of ADC is disabled or */
 /* enabling phase not yet completed: flag ADC ready not yet set). */
 /* Timeout implemented to not be stuck if ADC cannot be enabled (possible */
 /* causes: ADC clock not running, ...). */
 if (ADC_IS_ENABLE(hadc) == RESET)
 {
 /* Check if conditions to enable the ADC are fulfilled */
 if (ADC_ENABLING_CONDITIONS(hadc) == RESET)
 {
 /* Update ADC state machine to error */
 SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);
 
 /* Set ADC error code to ADC IP internal error */
 SET_BIT(hadc->ErrorCode, HAL_ADC_ERROR_INTERNAL);
 
 return HAL_ERROR;
 }

 /* Enable the ADC peripheral */
 __HAL_ADC_ENABLE(hadc); //XXX this is where enabling ADC fails 
 						//if code executes too soon (i.e. without delay
 
 /* Wait for ADC effectively enabled */
 tickstart = HAL_GetTick(); 
 
 while(__HAL_ADC_GET_FLAG(hadc, ADC_FLAG_RDY) == RESET)
 {
 if((HAL_GetTick() - tickstart) > ADC_ENABLE_TIMEOUT)
 {
 /* New check to avoid false timeout detection in case of preemption */
 if(__HAL_ADC_GET_FLAG(hadc, ADC_FLAG_RDY) == RESET)
 {
 /* Update ADC state machine to error */
 SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);

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

 return HAL_ERROR;
 }
 }
 }
 }
 
 /* Return HAL status */
 return HAL_OK;
}

 

EDIT 1
I tried to wait for calibration to complete and voltage regulator to be enabled. It didn't work:

volatile HAL_StatusTypeDef adcCalibrationReturn;
volatile HAL_StatusTypeDef adcStartReturn;
uint32_t adcTaskTick=0;
void startAdcTask(void *argument) {

	adcCalibrationReturn=HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
//	osDelay(1); //if this is uncommented or if program stops at instruction breakpoint placed right below, then ADC
				//sets HAL function ADC_Enable properly sets ISR to 1 and 0x10000001
	while ( ( ((hadc1.Instance->CR) & (ADC_CR_ADCAL)) !=0 ) //while calibration is not complete...
			&& ( ((hadc1.Instance->CR) & (ADC_CR_ADVREGEN)) != (ADC_CR_ADVREGEN_0) ) //while voltage regulator is not enabled...
	) {
		//wait
	}
	adcStartReturn=HAL_ADC_Start_DMA(&hadc1, (uint32_t *)dmaBuffer, sizeof(dmaBuffer));

	for (;;) {
		adcTaskTick++;
		osDelay(1000);
	}
}

 

EDIT 2

I forgot to mention I use STM32F302K6U6.

    This topic has been closed for replies.

    2 replies

    _AdamNtrxAuthor
    Explorer
    March 20, 2025

    I think I found a bug inside of my code that caused many other problems:

    adcStartReturn=HAL_ADC_Start_DMA(&hadc1, (uint32_t *)dmaBuffer, sizeof(dmaBuffer));

     

    The last argument above should be:

    sizeof(dmaBuffer)/sizeof(dmaBuffer[0])

     

    So the whole instruction should look like:

    adcStartReturn=HAL_ADC_Start_DMA( &hadc1, (uint32_t *)dmaBuffer, sizeof(dmaBuffer)/sizeof(dmaBuffer[0]) );

     

     

    I wondered why other structures and variables get corrupted during execution of my program and the cause was incorrect memory range specified by last argument of HAL_ADC_Start_DMA. Still, a delay is required when I use ADC Asynchronous Clock Mode.

     

    EDIT 1

    Weird, ADC seems to be starting just fine, even without delay between calibration and start.

     

    EDIT 2

    If ADC is using Asynchronous Clock Mode and prescaler is set to 64, then starting ADC right after calibration without delay fails. Other prescaler values work fine:

    Prescaler.png

     

    _AdamNtrxAuthor
    Explorer
    March 26, 2025

    Does anyone have any idea why this program works like that?