2026-01-20 6:14 PM - edited 2026-01-20 6:17 PM
I am using STM32U575 (MCLK = 160 MHz) with ADC1 in scan mode (4 channels).
ADC conversions are triggered by TIM3 TRGO and results are transferred by DMA in circular mode.
Continuous conversion is disabled.
TIM3 is configured to trigger ADC every 100 us (10 samples per ms).
ADC results are copied to a snapshot buffer in HAL_ADC_ConvCpltCallback().
In the application task, ADC values are read from this snapshot buffer every 100 us.
However, I observe repeated ADC values (e.g. A, A, B, B…) instead of updated data at each 100 us read.
Reducing the application read rate decreases the number of repeated samples.
Is this behavior expected when using ADC scan mode + DMA circular?
What is the recommended way to ensure ADC data is updated strictly at 100 us intervals
(1 ms / 10 samples) without repeated values?
Should ADC data be processed only in the DMA transfer complete callback,
or is there a better synchronization method?
Related code:
//task
for (uint8_t i = 0; i < POINTS_PER_MS ; i++)
{
adc = ADC_GetValue(ch1);
if (temp_adc > adc)
{
temp_adc = adc;
OPT_ADC = adc;
}
DWT_Delay_us(POINTS_DELAY_US);
}
uint16_t ADC_GetValue(uint8_t channel)
{
if (channel < ADC_BUFFRER_SIZE)
return ADCValueSnapshot[channel];
else
return 0;
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if (hadc->Instance == ADC1)
{
#if 1
for (uint8_t i = 0; i < ADC_BUFFRER_SIZE; i++)
{
ADCValueSnapshot[i] = (uint16_t)ADCValue[i];
}
// ADC_ConversionComplete = 1;
#endif
}
}
//ADC setting
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_DIV8;
hadc1.Init.Resolution = ADC_RESOLUTION_14B;
hadc1.Init.GainCompensation = 0;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 4;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T3_TRGO;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_LOW;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_36CYCLES;
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_2;
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;
sConfig.SamplingTime = ADC_SAMPLETIME_68CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_4;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
// Note: 68 cycle->3.4us, 814 cycles->40.7us
/* USER CODE END ADC1_Init 2 */
}
//Tim3 setting
static void MX_TIM3_Init(void)
{
/* USER CODE BEGIN TIM3_Init 0 */
/* USER CODE END TIM3_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 160 - 1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 100 - 1;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
}
//DMA
void ADC_DMA_Init()
{
__HAL_LINKDMA(&hadc1, DMA_Handle, handle_GPDMA1_Channel1);
if (HAL_ADC_Start_DMA(&hadc1, (uint32_t *)ADCValue, (ADC_BUFFRER_SIZE)) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_Base_Start_IT(&htim3);
}
2026-01-20 6:21 PM
Set the values to 0 after you read them to ensure they are updated the next time around. If you still get duplicates, that's what the ADC is reading.
Ensure you have data cache disabled or are managing it correctly.
It would be better to double the buffer size and use the half-full and full interrupt to process each half of the buffer. Otherwise you have race conditions as the ADC is updating the same portions of the buffer you are reading.