Showing results for 
Search instead for 
Did you mean: 

Using DMA in circular mode to buffer ADC values triggered by TIM "trgo" on STM32U575xx



I'm working on an application on the STM32U575 platform, involving the usage of the ADC1, GPDMA and TIM15 peripherals.

During operations, the TIM15 is running in OC mode and has an effective period of 1kHz. The trgo event of said timer should also drive the sampling of a single channel on ADC1, which then sends samples to a buffer via DMA.

I'm using the latest version of the U5 HAL.

I managed to accomplish the first part of the operation (i.e. TIM15 --> ADC1), which works fine in interrupt mode, however I'm unable to implement a good solution involving the use of DMA. The issues I'm facing is overrun on the ADC1 in certain implementations, and DMA related errors in others.

What I tried so far:

>> Using GPDMA in simple mode, with a single buffer, kicking off operations after initialization and restarting it afterwards when conversion is complete.

// Operation is started once after initialization
HAL_ADC_Start_DMA(&hadc1, (uint16_t *)buffer, 64);
// Start TIM15
HAL_TIM_OC_Start(&htim15, TIM_CHANNEL_2);
/* ... in stm32u5xx_it.c ... */
void GPDMA1_Channel1_IRQHandler(){
  // Restart the operations
  HAL_ADC_Start_DMA(&hadc1, (uint16_t *)buffer, 64);

This method seems to be unreliable (maybe it isn't) and will result in OVR issues after a couple of successfully completed DMA runs.

>> Adapting the method above with different parameters from ADC and DMA side results in the same issue.

>> I then tried to use the GPDMA in circular linked list mode to implement a double buffering method, which IMHO may also be more elegant, and I almost managed to make it work (that is, it makes a couple of successful runs and populates both buffers), but the only callback I get is related to a DMA busy error.

I'm posting the code I'm currently using, complete with linked list, dma and ADC configuration; I'd be more than happy to provide further details if needed.

ADC Related configuration:

/* MX_ADC1_Init */
hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.Resolution = ADC_RESOLUTION_14B;
  hadc1.Init.GainCompensation = 0;
  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.DMAContinuousRequests = ENABLE;
  hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
  hadc1.Init.OversamplingMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T15_TRGO;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIG_EDGE_RISING;
  hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
  __HAL_LINKDMA(&hadc1, DMA_Handle, hdma2);
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  ADC_ChannelConfTypeDef sConfig = {0};
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC4_SAMPLINGTIME_COMMON_1;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
  HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
/* ADC_Msp_Init */
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  /** Initializes the peripherals clock
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADCDAC;
    PeriphClkInit.AdcDacClockSelection = RCC_ADCDACCLKSOURCE_HSI;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    /* Peripheral clock enable */
    /**ADC1 GPIO Configuration
    PC2     ------> ADC1_IN3
    GPIO_InitStruct.Pin = VBUS_SENSE_Pin | GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(VBUS_SENSE_GPIO_Port, &GPIO_InitStruct);

Linked list initalization

DMA_NodeTypeDef ADCNode_Circular_1, ADCNode_Circular_2;
DMA_QListTypeDef ADCQueue;
extern uint16_t adc_values_1[64], adc_values_2[64];
  * @brief  DMA Linked-list ADCQueue configuration
  * @param  None
  * @retval None
HAL_StatusTypeDef MX_ADCQueue_Config(void)
  HAL_StatusTypeDef ret = HAL_OK;
  DMA_NodeConfTypeDef pNodeConfig;
  pNodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
  pNodeConfig.Init.Request = GPDMA1_REQUEST_ADC1;
  pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
  pNodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;
  pNodeConfig.Init.SrcInc = DMA_SINC_FIXED;
  pNodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;
  pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
  pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
  pNodeConfig.Init.SrcBurstLength = 1;
  pNodeConfig.Init.DestBurstLength = 1;
  pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
  pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
  pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
  pNodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
  pNodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
  pNodeConfig.SrcAddress = &(ADC1->DR);
  pNodeConfig.DstAddress = (uint32_t) adc_values_1;
  pNodeConfig.DataSize = 4*64;
  /* Build ADCNode Node #1 */
  ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &ADCNode_Circular_1);
  /* Insert ADCNode #1 to Queue */
  ret |= HAL_DMAEx_List_InsertNode_Tail(&ADCQueue, &ADCNode_Circular_1);
  pNodeConfig.SrcAddress = &(ADC1->DR);
  pNodeConfig.DstAddress = (uint32_t) adc_values_2;
    /* Build ADCNode Node #2 */
  ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &ADCNode_Circular_2);
  /* Insert ADCNode #2 to Queue */
  ret |= HAL_DMAEx_List_InsertNode_Tail(&ADCQueue, &ADCNode_Circular_2);
  ret |= HAL_DMAEx_List_SetCircularModeConfig(&ADCQueue, &ADCNode_Circular_1);
   return ret;

DMA initialization

hdma2.Instance = GPDMA1_Channel1_NS;
if (MX_ADCQueue_Config() != HAL_OK)
  hdma2.InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
  hdma2.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
  hdma2.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT1;
  hdma2.InitLinkedList.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;
  hdma2.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
  if (HAL_DMAEx_List_Init(&hdma2) != HAL_OK)
  if (HAL_DMA_ConfigChannelAttributes(&hdma2, DMA_CHANNEL_NPRIV) != HAL_OK)

Finally, kickoff operations during main():

  if (HAL_DMAEx_List_LinkQ(&hdma2, &ADCQueue) != HAL_OK)
  if (HAL_OK != HAL_ADC_Start_DMA(&hadc1, (uint16_t *)adc_values_1, 64))
  HAL_TIM_OC_Start(&htim15, TIM_CHANNEL_2);

Thanks to anyone who can help!!

Mohamed Aymen HZAMI
ST Employee

Hello @DBrus.2​ and welcome to the community,

Try to call the "HAL_ADC_Start_DMA" function in the call_back function instead of calling it in the Handler function.

Mohamed Aymen

Associate II

facing same issue with ADC and DMA anybody have any idea how to solve these things 



Are you using the same device as initial post? Do you have the same configuration?
Did you tried using one of the STM32CubeU5 package (in case used device is STM32U5) like STM32CubeU5/Projects/NUCLEO-U575ZI-Q/Examples/ADC/ADC_DMA_Transfer?

It is preferable to create a new post describing with details your current issue.


To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Yes I am using same  device STM32U575ZI-Q 
About configuration I am doing without the LinkedList i,e (Standard request mode) where I am following the as per standard method but still have the same issue 

I already created new post please kindly visit my post for more details