2025-07-11 2:46 PM
I used the STM32H533 Nucleo board example project ADC_MultiChannelSingleConversion as a reference for this code. The actual project is running on an STM32H523.
I am attempting to have a software-triggered array of ADC reads across two channels using the GPDMA. The plan is to be alerted via interrupt callback that the sequence (triggered by HAL_ADC_Start_DMA) is complete. I do not want the ADC to continue to sample or overwrite the data in the buffer, so I am turning off the ADC in the callback.
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
HAL_ADC_Stop_DMA(hadc);
...
}
The issue is that this callback is being triggered after every two-channel sequence is completed, instead of once after the DMA buffer is filled.
The system does not generate any DMA complete or half complete callbacks in this configuration.
Here's what appears to be working:
Here is the relevant code with most boilerplate CubeMX comments removed for brevity:
void MX_ADC1_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
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 = 2;
hadc1.Init.DiscontinuousConvMode = DISABLE;
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_PRESERVED;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Error_Handler();
}
__HAL_ADC_ENABLE_IT(&hadc1, ADC_IT_EOC | ADC_IT_OVR);
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_92CYCLES_5; // TODO: make shorter
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Error_Handler();
}
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
DMA_NodeConfTypeDef DMA_NodeConfig;
if (adcHandle->Instance == ADC1) {
LL_RCC_SetADCDACClockSource(LL_RCC_ADCDAC_CLKSOURCE_HCLK);
HAL_RCC_ADC_CLK_ENABLED++;
if (HAL_RCC_ADC_CLK_ENABLED == 1) {
__HAL_RCC_ADC_CLK_ENABLE();
}
__HAL_RCC_GPIOA_CLK_ENABLE();
/**ADC1 GPIO Configuration
PA0 ------> ADC1_INP0
PA1 ------> ADC1_INP1
*/
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
DMA_NodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
DMA_NodeConfig.Init.Request = GPDMA2_REQUEST_ADC1;
DMA_NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
DMA_NodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;
DMA_NodeConfig.Init.SrcInc = DMA_SINC_FIXED;
DMA_NodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;
DMA_NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
DMA_NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
DMA_NodeConfig.Init.SrcBurstLength = 1;
DMA_NodeConfig.Init.DestBurstLength = 1;
DMA_NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0;
DMA_NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
DMA_NodeConfig.Init.Mode = DMA_NORMAL;
DMA_NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
DMA_NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
DMA_NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
if (HAL_DMAEx_List_BuildNode(&DMA_NodeConfig, &Node_GPDMA2_Channel0) != HAL_OK) {
Error_Handler();
}
if (HAL_DMAEx_List_InsertNode(&List_GPDMA2_Channel0, NULL, &Node_GPDMA2_Channel0) != HAL_OK) {
Error_Handler();
}
if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA2_Channel0) != HAL_OK) {
Error_Handler();
}
handle_GPDMA2_Channel0.Instance = GPDMA2_Channel0;
handle_GPDMA2_Channel0.InitLinkedList.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
handle_GPDMA2_Channel0.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
handle_GPDMA2_Channel0.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
handle_GPDMA2_Channel0.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
handle_GPDMA2_Channel0.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA2_Channel0) != HAL_OK) {
Error_Handler();
}
if (HAL_DMAEx_List_LinkQ(&handle_GPDMA2_Channel0, &List_GPDMA2_Channel0) != HAL_OK) {
Error_Handler();
}
__HAL_LINKDMA(adcHandle, DMA_Handle, handle_GPDMA2_Channel0);
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA2_Channel0, DMA_CHANNEL_NPRIV) != HAL_OK) {
Error_Handler();
}
HAL_NVIC_SetPriority(ADC1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC1_IRQn);
} // ... ADC 2 not used with DMA
}
int main(void) {
// ... Other init
MX_GPDMA2_Init();
MX_ADC1_Init();
MX_ADC2_Init();
// ... Other init
}
Called to start with:
HAL_ADC_Start_DMA(&hadc1, dma_buffer, dma_buffer_size);
Any assistance or advice on this would be greatly appreciated! I will be as responsive as possible for any follow-up questions.
2025-07-11 3:13 PM
Disable ContinuousConvMode
Make sure DMA is not in circular mode. You won't have to disable the DMA in the interrupt as it'll only run once. You will need to call HAL_ADC_Start_DMA when needed again.