Skip to main content
Visitor II
October 1, 2025
Question

Can't Explain STM32H563 ADC Timing

  • October 1, 2025
  • 2 replies
  • 252 views

ADC1 clocked @ 65 MHz, prescalar 0 for divide by 1, continuous conversion, DMA circular mode, 640.5 cycle sampling time, 256x oversampling, 12-bit resolution, 3 channels per sequence. The channels in the sequence are: IN3, temp sensor channel, Vrefint channel. I would expect a sequence to be converted in (3 * 256 * (640.5 + 12.5)) / 65000000 => ~7.7 ms.

HAL_ADC_ConvCpltCallback() increments a counter for TC and HT. In 120 seconds (used debugger to stop) I get 60102. TC count = 60102/ 2 = 30051 for 120 seconds or 3.9 ms average/sequence. HAL_ADC_ErrorCallback() never called.

Why the discrepancy: 7.7 ms expected vs 3.9 ms observed?

MX_ADC1_Init() looks like…

static void MX_ADC1_Init(void)
{

 /* USER CODE BEGIN ADC1_Init 0 */

 /* USER CODE END ADC1_Init 0 */

 ADC_ChannelConfTypeDef sConfig = {0};

 /* USER CODE BEGIN ADC1_Init 1 */

 /* USER CODE END ADC1_Init 1 */

 /** Common config
 */
 hadc1.Instance = ADC1;
 hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
 hadc1.Init.Resolution = ADC_RESOLUTION_12B;
 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
 hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
 hadc1.Init.LowPowerAutoWait = DISABLE;
 hadc1.Init.ContinuousConvMode = ENABLE;
 hadc1.Init.NbrOfConversion = 3;
 hadc1.Init.DiscontinuousConvMode = DISABLE;
 hadc1.Init.NbrOfDiscConversion = 3;
 hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
 hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
 hadc1.Init.DMAContinuousRequests = ENABLE;
 hadc1.Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL;
 hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
 hadc1.Init.OversamplingMode = ENABLE;
 hadc1.Init.Oversampling.Ratio = ADC_OVERSAMPLING_RATIO_256;
 hadc1.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_8;
 hadc1.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
 hadc1.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_CONTINUED_MODE;
 if (HAL_ADC_Init(&hadc1) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure Regular Channel
 */
 sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
 sConfig.Rank = ADC_REGULAR_RANK_1;
 sConfig.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
 sConfig.SingleDiff = ADC_SINGLE_ENDED;
 sConfig.OffsetNumber = ADC_OFFSET_NONE;
 sConfig.Offset = 0;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure Regular Channel
 */
 sConfig.Channel = ADC_CHANNEL_VREFINT;
 sConfig.Rank = ADC_REGULAR_RANK_2;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }

 /** Configure Regular Channel
 */
 sConfig.Channel = ADC_CHANNEL_3;
 sConfig.Rank = ADC_REGULAR_RANK_3;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN ADC1_Init 2 */

 /* USER CODE END ADC1_Init 2 */

}

IOC file selects PLL2R for 65 MHz ADC clock...

mccabehm_0-1759357945906.png

 

 

    This topic has been closed for replies.

    2 replies

    Technical Moderator
    October 2, 2025

    Hello @mccabehm,

    The transfer complete (TC) interrupt triggers when the DMA finishes transferring the entire buffer. If you increment your counter on both the half-transfer and full-transfer interrupts, you will count two events for each full buffer transfer. This will make your measured event rate appear twice as fast as the actual ADC sequence rate. Have you tried counting only the TC interrupt and comparing the observed value to the expected one?

    Best regards,

    mccabehmAuthor
    Visitor II
    October 2, 2025

    Yes, HAL_ADC_ConvCpltCallback() is called for both HT and TC. This is why I divided the counter by 2. By the time this callback is invoked, the HT and TC flags are cleared.

    Another observation: HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_ADC) returns 250 MHz, not 65 MHz as I expected. Still doesn't explain why I measure 3.9 ms for average conversion.

    Technical Moderator
    October 3, 2025

    Hello @mccabehm 

    In HAL driver HAL_ADC_ConvCpltCallback is only called for TC, we have a dedicated callback for HT : HAL_ADC_ConvHalfCpltCallback. This would be the reason behind the difference observed.

    if the ADC is indeed running at 250MHz : 

    • 3 * 256 * (640.5 + 12.5)) / 250000000 = 2.0ms / sequence
    • TC count = 60102 for 120 seconds = 1.99ms / sequence

    Could you please share your RCC/ADC clock config?

    mccabehmAuthor
    Visitor II
    October 3, 2025

    HSE @ 26 MHz. SystemClock_Config()...

    void SystemClock_Config(void)
    {
     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    
     /** Configure the main internal regulator output voltage
     */
     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
    
     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    
     /** Configure LSE Drive Capability
     * Warning : Only applied when the LSE is disabled.
     */
     HAL_PWR_EnableBkUpAccess();
     __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);
    
     /** Initializes the RCC Oscillators according to the specified parameters
     * in the RCC_OscInitTypeDef structure.
     */
     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48|RCC_OSCILLATORTYPE_LSI
     |RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
     RCC_OscInitStruct.LSEState = RCC_LSE_ON;
     RCC_OscInitStruct.LSIState = RCC_LSI_ON;
     RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
     RCC_OscInitStruct.PLL.PLLSource = RCC_PLL1_SOURCE_HSE;
     RCC_OscInitStruct.PLL.PLLM = 13;
     RCC_OscInitStruct.PLL.PLLN = 250;
     RCC_OscInitStruct.PLL.PLLP = 2;
     RCC_OscInitStruct.PLL.PLLQ = 2;
     RCC_OscInitStruct.PLL.PLLR = 2;
     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1_VCIRANGE_1;
     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1_VCORANGE_WIDE;
     RCC_OscInitStruct.PLL.PLLFRACN = 0;
     if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
     {
     Error_Handler();
     }
    
     /** Initializes the CPU, AHB and APB buses clocks
     */
     RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
     |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
     |RCC_CLOCKTYPE_PCLK3;
     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
     RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
     RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
     RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
     RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;
    
     if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
     {
     Error_Handler();
     }
    
     /** Configure the programming delay
     */
     __HAL_FLASH_SET_PROGRAM_DELAY(FLASH_PROGRAMMING_DELAY_2);
    }

    PeriphCommonClock_Config()...

    void PeriphCommonClock_Config(void)
    {
     RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
    
     /** Initializes the peripherals clock
     */
     PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADCDAC|RCC_PERIPHCLK_SPI4;
     PeriphClkInitStruct.PLL2.PLL2Source = RCC_PLL2_SOURCE_HSE;
     PeriphClkInitStruct.PLL2.PLL2M = 8;
     PeriphClkInitStruct.PLL2.PLL2N = 40;
     PeriphClkInitStruct.PLL2.PLL2P = 2;
     PeriphClkInitStruct.PLL2.PLL2Q = 2;
     PeriphClkInitStruct.PLL2.PLL2R = 2;
     PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2_VCIRANGE_1;
     PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2_VCORANGE_WIDE;
     PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
     PeriphClkInitStruct.PLL2.PLL2ClockOut = RCC_PLL2_DIVQ|RCC_PLL2_DIVR;
     PeriphClkInitStruct.AdcDacClockSelection = RCC_ADCDACCLKSOURCE_PLL2R;
     PeriphClkInitStruct.Spi4ClockSelection = RCC_SPI4CLKSOURCE_PLL2Q;
     if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
     {
     Error_Handler();
     }
    }

    To corelate ADC conversion rate with a thread period I changed ADC prescalar divisor from 1 to 2, and decreased oversampling from 256x to 64x. Other ADC parameters unchanged.

    I suspect HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_ADC) is not returning ADC clock correctly because:

    • Datasheet shows max ADC clock 75 MHz

    mccabehm_0-1759501596156.png

    • Conversion times change as expected when I change ADC prescalar (now that I know HAL_ADC_ConvCpltCallback does not count HT)

    With ADC prescalar divisor at 1, the ADC counts I read are not accurate. With ADC prescalar divisor at 2 or more, the ADC counts I read are as expected.

    Could I be overclocking the ADC?