2025-10-01 3:37 PM
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...
2025-10-02 4:08 AM
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,
2025-10-02 6:55 AM - edited 2025-10-02 7:17 AM
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.
2025-10-03 2:47 AM
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 :
Could you please share your RCC/ADC clock config?
2025-10-03 7:34 AM
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:
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?