2026-02-27 1:35 AM - last edited on 2026-02-27 1:43 AM by Andrew Neil
Hi everyone,
I am working on STM32H723 (H7 series) using HAL, and I am facing an issue with ADC1 + ADC2 dual regular simultaneous mode.
MCU: STM32H723
HAL based project
ADC1 = Master
ADC2 = Slave
Mode: ADC_DUALMODE_REGSIMULT
DualModeData: ADC_DUALMODEDATAFORMAT_32_10_BITS
Resolution: 12-bit
Scan mode: Disabled
Continuous mode: Disabled
Trigger source: TIM2_TRGO (rising edge)
DMA: Circular mode
ADC3 runs independently (also TIM2 triggered) and works perfectly
Dual mode started using:
HAL_ADCEx_MultiModeStart_DMA(&hadc1,
(uint32_t*)g_adc12_packed_buffer,
TP_DMA_BUFFER_SIZE);Buffer unpacking:
uint32_t packed = g_adc12_packed_buffer[i];
uint16_t adc1_raw = packed & 0xFFFF;
uint16_t adc2_raw = packed >> 16;When I apply voltage to ADC1 input:
Both ADC1 and ADC2 values change
Both show identical values (mirrored)
When I apply voltage to ADC2 input:
No change in readings
ADC2 does not respond
When inputs are floating (level shifted mid-scale ~2048 expected):
Both ADC1 and ADC2 periodically read 0
Then both read ~2048
Alternates in blocks
ADC3 continues working correctly
But when i configure an all adc independently it works correctly, when i configure an dual regular simultaneous mode it behaves like this.
//MCU : STM32 H723VET6
//HAL PROJECT
//SYS clk : 550MHZ
//ADC Clk : 36MHZ
//-----------------adc1 configurations master----------------------
void ThreePhase_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
ADC_MultiModeTypeDef multimode = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T2_TRGO;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
Error_Handler();
/* Regular simultaneous mode */
multimode.Mode = ADC_DUALMODE_REGSIMULT;
// multimode.Mode = ADC_MODE_INDEPENDENT;
multimode.DualModeData = ADC_DUALMODEDATAFORMAT_32_10_BITS;
multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_1CYCLE;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
Error_Handler();
/* Channel 16 (PA0) */
sConfig.Channel = ADC_CHANNEL_16;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_32CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
Error_Handler();
HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
}
//----------------adc2 configurations slave------------------------------------
void ThreePhase_ADC2_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc2.Init.LowPowerAutoWait = DISABLE;
hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.NbrOfConversion = 1;
hadc2.Init.DiscontinuousConvMode = DISABLE;
/* NO external trigger — slave follows master */
hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc2.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
hadc2.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc2.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc2.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc2) != HAL_OK)
Error_Handler();
/* Channel 3 (PA6) */
sConfig.Channel = ADC_CHANNEL_3;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_32CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
Error_Handler();
HAL_ADCEx_Calibration_Start(&hadc2, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
}
//-----------------------adc3 configurations independent-----------------------
void ThreePhase_ADC3_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc3.Instance = ADC3;
hadc3.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc3.Init.Resolution = ADC_RESOLUTION_12B;
hadc3.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc3.Init.LowPowerAutoWait = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.NbrOfConversion = 1;
hadc3.Init.DiscontinuousConvMode = DISABLE;
hadc3.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T2_TRGO;
hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc3.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
hadc3.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc3.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc3.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc3) != HAL_OK)
Error_Handler();
/* Channel 10 (PC0) */
sConfig.Channel = ADC_CHANNEL_10;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC3_SAMPLETIME_47CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
Error_Handler();
HAL_ADCEx_Calibration_Start(&hadc3, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
}
//-------------------Bufffer initialisation------------------------------------
#define TP_DMA_BUFFER_SIZE 2048U
volatile uint32_t g_adc12_packed_buffer[TP_DMA_BUFFER_SIZE] __attribute__((aligned(32)));
volatile uint16_t g_adc1_buffer[TP_DMA_BUFFER_SIZE] __attribute__((aligned(32)));
volatile uint16_t g_adc2_buffer[TP_DMA_BUFFER_SIZE] __attribute__((aligned(32)));
volatile uint16_t g_adc3_buffer[TP_DMA_BUFFER_SIZE] __attribute__((aligned(32)));
//--------------------Callbacks Half & Full callback-----------------------------
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
{
if (adc_mode == ADC_MODE_THREEPHASE)
{
if (hadc == &hadc1)
{
/* Invalidate first half of packed buffer */
SCB_InvalidateDCache_by_Addr(
(uint32_t*)g_adc12_packed_buffer,
(TP_DMA_BUFFER_SIZE / 2) * sizeof(uint32_t)
);
/* Unpack first half */
for (uint32_t i = 0; i < TP_DMA_BUFFER_SIZE / 2; i++)
{
uint32_t packed = g_adc12_packed_buffer[i];
g_adc1_buffer[i] = (uint16_t)(packed & 0xFFFF);
g_adc2_buffer[i] = (uint16_t)(packed >> 16);
}
}
else if (hadc == &hadc2)
{
SCB_InvalidateDCache_by_Addr(
(uint32_t*)g_adc2_buffer,
(TP_DMA_BUFFER_SIZE / 2) * sizeof(uint16_t)
);
}
else if (hadc == &hadc3)
{
SCB_InvalidateDCache_by_Addr(
(uint32_t*)g_adc3_buffer,
(TP_DMA_BUFFER_SIZE / 2) * sizeof(uint16_t)
);
}
}
else
{
if (hadc == &hadc3)
{
SCB_InvalidateDCache_by_Addr(
(uint32_t*)adc3Buffer,
(ADC_BUFFER_SIZE / 2) * sizeof(uint16_t)
);
dma3_half_ready = 1;
}
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
/* ================= STREAMING ================= */
if (adc_mode == ADC_MODE_STREAMING)
{
if (hadc == &hadc3)
{
SCB_InvalidateDCache_by_Addr(
(uint32_t*)&adc3Buffer[ADC_BUFFER_SIZE / 2],
(ADC_BUFFER_SIZE / 2) * sizeof(uint16_t)
);
dma3_full_ready = 1;
}
}
/* ================= FREQ ================= */
else if (adc_mode == ADC_MODE_FREQ)
{
if (hadc == &hadc3)
dma3_full_ready = 1;
}
/* ================= LCR ================= */
else if (adc_mode == ADC_MODE_LCR)
{
if (hadc == &hadc1)
adc_done = true;
if (hadc == &hadc3)
dma3_full_ready = 1;
}
/* ================= THREE PHASE ================= */
else if (adc_mode == ADC_MODE_THREEPHASE)
{
if (hadc == &hadc1)
{
SCB_InvalidateDCache_by_Addr(
(uint32_t*)&g_adc12_packed_buffer[TP_DMA_BUFFER_SIZE / 2],
(TP_DMA_BUFFER_SIZE / 2) * sizeof(uint32_t)
);
/* Unpack */
for (uint32_t i = TP_DMA_BUFFER_SIZE / 2;
i < TP_DMA_BUFFER_SIZE;
i++)
{
uint32_t packed = g_adc12_packed_buffer[i];
g_adc1_buffer[i] = (uint16_t)(packed & 0xFFFF);
g_adc2_buffer[i] = (uint16_t)(packed >> 16);
}
}
// else if (hadc == &hadc2)
// {
// SCB_InvalidateDCache_by_Addr(
// (uint32_t*)&g_adc2_buffer[TP_DMA_BUFFER_SIZE / 2],
// (TP_DMA_BUFFER_SIZE / 2) * sizeof(uint16_t)
// );
// }
else if (hadc == &hadc3)
{
SCB_InvalidateDCache_by_Addr(
(uint32_t*)&g_adc3_buffer[TP_DMA_BUFFER_SIZE / 2],
(TP_DMA_BUFFER_SIZE / 2) * sizeof(uint16_t)
);
g_buffer_timestamp_us = __HAL_TIM_GET_COUNTER(&htim5);
g_buffer_ready = 1;
}
if (g_test_scenario == SCENARIO_VOLTAGE_ONLY)
{
g_voltage_ready = 1;
}
else if (g_test_scenario == SCENARIO_CURRENT_ONLY)
{
g_current_ready = 1;
}
else
{
/* alternating mode */
if (g_current_mode == MODE_VOLTAGE)
g_voltage_ready = 1;
else
g_current_ready = 1;
}
}
}
//-----------------------------Callbacks end----------------------------
2026-04-15 3:17 AM
Hello @Pandian
ADC configuration is correct ; could you please add precisions on system configuration?
ADC kernel clock frequency? (STM32H723 ADC freq max is 50MHz with boost=0b11)
The issue could be related also to impedance at board level: please refer to datasheet parameter "Rain".
For debug, could be interesting to increase sampling time from 32.5 to 810.5 ADC clock cycles.