cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H725 ADC3 circular DMA not working? (It does with ADC1)

I've been stumbled upon this issue for a week now.

On STM32H725ZG, ADC3 circular DMA is not working.

A test codelet below, designed to demonstrate the issue, repeatedly takes 12-bit readings from PC0 (ADC123_INP10) and PC1 (ADC123_INP11) with ADC1 or ADC3, using circular DMA.

This code works with ADC1 flawlessly, but with ADC3, it stops reading, probably after the very first round.

Is there anything special about ADC3 that is different from ADC1 which is preventing this code from working with ADC3?

ADC_HandleTypeDef AdcHandle;
#define ADC_CONVERTED_DATA_BUFFER_SIZE 32
ALIGN_32BYTES (static uint16_t   aADCxConvertedData[ADC_CONVERTED_DATA_BUFFER_SIZE]);
 
void adcCircularDmaTest(void)
{
    __HAL_RCC_ADC12_CLK_ENABLE();
    __HAL_RCC_ADC3_CLK_ENABLE();
 
    __HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_CLKP);
 
    ADC_ChannelConfTypeDef sConfig;
 
    AdcHandle.Instance = ADC1;
    //AdcHandle.Instance = ADC3;
 
    AdcHandle.Init.ClockPrescaler           = ADC_CLOCK_ASYNC_DIV2;          /* Asynchronous clock mode, input ADC clock divided by 2*/
    AdcHandle.Init.Resolution               = ADC_RESOLUTION_12B;            /* 16-bit resolution for converted data */
    AdcHandle.Init.ScanConvMode             = ENABLE;                        /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */
    AdcHandle.Init.EOCSelection             = ADC_EOC_SINGLE_CONV;           /* EOC flag picked-up to indicate conversion end */
    AdcHandle.Init.LowPowerAutoWait         = DISABLE;                       /* Auto-delayed conversion feature disabled */
    AdcHandle.Init.ContinuousConvMode       = ENABLE;                        /* Continuous mode disabled to have only 1 conversion at each conversion trig */
    AdcHandle.Init.NbrOfConversion          = 2;                             /* Parameter discarded because sequencer is disabled */
    AdcHandle.Init.DiscontinuousConvMode    = DISABLE;                       /* Parameter discarded because sequencer is disabled */
    AdcHandle.Init.NbrOfDiscConversion      = 1;                             /* Parameter discarded because sequencer is disabled */
    AdcHandle.Init.ExternalTrigConv         = ADC_SOFTWARE_START;            /* Software start to trig the 1st conversion manually, without external event */
    AdcHandle.Init.ExternalTrigConvEdge     = ADC_EXTERNALTRIGCONVEDGE_NONE; /* Parameter discarded because software trigger chosen */
    AdcHandle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR; /* Use circular DMA to retrieve data */
    AdcHandle.Init.Overrun                  = ADC_OVR_DATA_OVERWRITTEN;      /* DR register is overwritten with the last conversion result in case of overrun */
    AdcHandle.Init.OversamplingMode         = DISABLE;                       /* No oversampling */
 
    if (HAL_ADC_Init(&AdcHandle) != HAL_OK) {
        /* ADC initialization Error */
        while (1);
    }
 
    // Channels INP10 on PC0 and INP11 on PC1, both available for ADC1 and ADC3
 
    GPIO_InitTypeDef GPIO_InitStruct;
 
    // INP10 on PC0
 
    __HAL_RCC_GPIOC_CLK_ENABLE();
 
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
    sConfig.Channel      = ADC_CHANNEL_10;              /* Sampled channel number */
    sConfig.Rank         = ADC_REGULAR_RANK_1;          /* Rank of sampled channel number ADCx_CHANNEL */
    sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5;  /* Sampling time (number of clock cycles unit) */
    sConfig.SingleDiff   = ADC_SINGLE_ENDED;            /* Single-ended input channel */
    sConfig.OffsetNumber = ADC_OFFSET_NONE;             /* No offset subtraction */
    sConfig.Offset = 0;                                 /* Parameter discarded because offset correction is disabled */
 
    if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK) {
        /* Channel Configuration Error */
        while (1);
    }
 
    // INP11 on PC1
 
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
    sConfig.Channel      = ADC_CHANNEL_11;              /* Sampled channel number */
    sConfig.Rank         = ADC_REGULAR_RANK_2;          /* Rank of sampled channel number ADCx_CHANNEL */
    sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5;  /* Sampling time (number of clock cycles unit) */
    sConfig.SingleDiff   = ADC_SINGLE_ENDED;            /* Single-ended input channel */
    sConfig.OffsetNumber = ADC_OFFSET_NONE;             /* No offset subtraction */
    sConfig.Offset = 0;                                 /* Parameter discarded because offset correction is disabled */
 
    if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK) {
        /* Calibration Error */
        while (1);
    }
 
    // Use DMA2_Stream2
 
    DMA_HandleTypeDef DmaHandle;
 
    __HAL_RCC_DMA2_CLK_ENABLE();
 
    DmaHandle.Instance                 = DMA2_Stream2;
 
    DmaHandle.Init.Request             = DMA_REQUEST_ADC1;
    // DmaHandle.Init.Request             = DMA_REQUEST_ADC3;
 
    DmaHandle.Init.Direction           = DMA_PERIPH_TO_MEMORY;
    DmaHandle.Init.PeriphInc           = DMA_PINC_DISABLE;
    DmaHandle.Init.MemInc              = DMA_MINC_ENABLE;
    DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    DmaHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
    DmaHandle.Init.Mode                = DMA_CIRCULAR;
    DmaHandle.Init.Priority            = DMA_PRIORITY_MEDIUM;
 
    HAL_DMA_DeInit(&DmaHandle);
    HAL_DMA_Init(&DmaHandle);
 
    __HAL_LINKDMA(&AdcHandle, DMA_Handle, DmaHandle);
 
    if (HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)aADCxConvertedData, ADC_CONVERTED_DATA_BUFFER_SIZE) != HAL_OK) {
        while(1);
    }
 
    while (1);
}

4 REPLIES 4
TDK
Guru

It's in a different domain. Why does it stop? Gotta be an error flag raised somewhere.

Probably isn't it, but you never initialize rank 2.

If you feel a post has answered your question, please click "Accept as Solution".

Thanks for pointing out the rank 2 init flaw.

I think I've nailed the cause of the issue. Will write as a separate answer.

Okay, I think I have a solution for this issue.

It seems that ADC3 of STM32H72X and H73X has different way of specifying circular DMA mode.

In Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_adc.h,

typedef struct {
...
#if defined(ADC_VER_V5_V90)
  /*Note: On devices STM32H72xx and STM32H73xx, this parameter is specific to ADC3 only. */
 
  uint32_t SamplingMode;          /*!< Select the sampling mode to be used for ADC group regular conversion.
                                       This parameter can be a value of @ref ADC_regular_sampling_mode.
                                       Note:
                                            - On devices STM32H72xx and STM32H73xx, this parameter is specific to ADC3 only. */
 
  FunctionalState DMAContinuousRequests; /*!< Specify whether the DMA requests are performed in one shot mode (DMA transfer stops when number of conversions is reached)
                                       or in continuous mode (DMA transfer unlimited, whatever number of conversions).
                                       This parameter can be set to ENABLE or DISABLE.
                                       Notes:
                                             - In continuous mode, DMA must be configured in circular mode. Otherwise an overrun will be triggered when DMA buffer maximum pointer is reached.
                                             - Specific to ADC3 only on devices STM32H72xx and STM32H73xx */
#endif
...
} ADC_InitTypeDef;

And then in Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_adc.c

HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef *hadc)
{
...
#if defined(ADC_VER_V5_V90)
      if (hadc->Instance == ADC3)
      {
        tmpCFGR = (
                    ADC_CFGR_AUTOWAIT((uint32_t)hadc->Init.LowPowerAutoWait)        |
                    ADC3_CFGR_DMACONTREQ((uint32_t)hadc->Init.DMAContinuousRequests));
      }
      else
      {
        tmpCFGR = (
                    ADC_CFGR_AUTOWAIT((uint32_t)hadc->Init.LowPowerAutoWait)        |
                    ADC_CFGR_DMACONTREQ((uint32_t)hadc->Init.ConversionDataManagement));
      }
#else
      tmpCFGR = (
                  ADC_CFGR_AUTOWAIT((uint32_t)hadc->Init.LowPowerAutoWait)        |
                  ADC_CFGR_DMACONTREQ((uint32_t)hadc->Init.ConversionDataManagement));
#endif
...
}

The ADC3_CFGR_DMACONTREQ macro sets the circular DMA flag in CFGR.

So, it seems that we should use a code like this one when initializing the ADC.

...
    // Use circular DMA to retrieve data 
    if (AdcHandle.Instance == ADC3) {
        AdcHandle.Init.DMAContinuousRequests = ENABLE;
    } else {
        AdcHandle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR; 
    }
...

When line 28 of the test program is replaced with this mod, voila!!! the program started to work for both ADC1 and ADC3.

I believe this discrepancy of H72X/H73X should be documented somewhere, may be in an migration application note, if not already done so.