cancel
Showing results for 
Search instead for 
Did you mean: 

ADC DMA values are refreshed only if other DMA peripheral are working

jean
Senior

Hi everyone!

I've got something strange with my ADCs (working on STM32H743).

Everything was working until I realized that both of my ADC values are refreshed only if use other DMA peripheral. For example, it seems that sending SPI refresh the ADC, otherwise values stay the same.

You can have a look at my code :

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
  GPIO_InitTypeDef GPIO_InitStruct = { 0 };
  if(hadc->Instance == ADC2)
  {
    /* Initializes the peripherals clock */
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = { 0 };
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_CLKP;
    if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) __DEBUG_BKPT();
 
    /* Peripheral clock enable */
    __HAL_RCC_ADC12_CLK_ENABLE();
    __ADC12_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
 
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
    hdma_adc2.Instance = DMA1_Stream3;
    hdma_adc2.Init.Request = DMA_REQUEST_ADC2;
    hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc2.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_adc2.Init.Mode = DMA_CIRCULAR;
    hdma_adc2.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_adc2.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_adc2.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_adc2.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_adc2.Init.PeriphBurst = DMA_PBURST_SINGLE;
    if(HAL_DMA_Init(&hdma_adc2) != HAL_OK) __DEBUG_BKPT();
    __HAL_LINKDMA(hadc, DMA_Handle, hdma_adc2);
 
  HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
  }
}

__IO uint16_t VOLTAGE_DMA[2];
 
void adc::Init()
{
  /* ADC 2 (PB0 + PB1) */
  hadc2.Instance = ADC2;
  hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc2.Init.Resolution = ADC_RESOLUTION_16B;
  hadc2.Init.ScanConvMode = ADC_SCAN_ENABLE;
  hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc2.Init.LowPowerAutoWait = DISABLE;
  hadc2.Init.ContinuousConvMode = ENABLE;
  hadc2.Init.NbrOfConversion = 2;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  //hadc2.Init.NbrOfDiscConversion = 1;
  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_PRESERVED;
  hadc2.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  hadc2.Init.OversamplingMode = DISABLE;
  if(HAL_ADC_Init(&hadc2) != HAL_OK) __DEBUG_BKPT();
 
  /* ADC_CV_IN_1 */
  ADC_ChannelConfTypeDef sConfig;
  sConfig.Channel = ADC_CHANNEL_9;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_387CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;
  if(HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK) __DEBUG_BKPT();
 
  /* ADC_CV_IN_2 */
  sConfig.Channel = ADC_CHANNEL_5;
  sConfig.Rank = ADC_REGULAR_RANK_2;
  if(HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK) __DEBUG_BKPT();
 
  if(HAL_ADC_Start_DMA(&hadc2, const_cast<uint32_t*>(reinterpret_cast<volatile uint32_t*>(VOLTAGE_DMA)), 2) != HAL_OK) __DEBUG_BKPT();
}
void DMA1_Stream0_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_spi1_tx); // SPI1
}
 
void DMA1_Stream1_IRQHandler(void) 
{
  HAL_DMA_IRQHandler(&hdma_spi2_tx); // SPI2
}
 
void DMA1_Stream2_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_spi4_tx); // SPI4
}
 
void DMA1_Stream3_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_adc2); // ADC
}

The values VOLTAGE_DMA[0] and VOLTAGE_DMA[1] should be always scanned and auto-refreshed, but they are updated only when I use an other DMA peripheral (SPI1, SP2, SP4).

As you can see, these other DMA peripheral also uses DMA1.

Do you have an idea of what can be wrong in my code?

Thanks a lot for your help!

Jean

1 ACCEPTED SOLUTION

Accepted Solutions
jean
Senior

Thanks a lot for your help, fixed the issue!

Indeed, turning off D-cache was fixing the problem.

I defined the VOLTAGE_DMA (buffer to read) to be in the linker section .dma_ram :

static uint16_t CV_INPUTS_DMA[kAdcInputs] __attribute__((section(".dma_ram")));
   .dma_ram (NOLOAD) :
  {
    . = ALIGN(4);
    _s_dma_ram = .;
    KEEP(*(.dma_ram))
    . = ALIGN(4);
    _e_dma_ram = .;
  } >DMA_RAM

So this buffer is not affected by the cache.

It's working perfectly now!

View solution in original post

6 REPLIES 6
KnarfB
Principal III

What chip are you using? What is your code intend, ADC self triggered continous scan mode?

My CubeMX generated code has a line

hadc1.Init.DMAContinuousRequests = ENABLE;

which is missing in your code

hth

KnarfB

jean
Senior

Hello,

Thanks for your reply!

I'm using a STM32H743 (sorry for not mentioned it)

Indeed, I want an ADC self triggered continuous scan mode (I want the ADC to always update the value, and pick up the value when I need it, the refresh rate should be faster than 8kHz).

It seems than with STM32H743, DMAContinuousRequests parameter is available only when using ADC3 (I use ADC2, and I can't use ADC3), as you can see on hal_adc.h :

#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

Is there an other option to get a continuous scan mode ?

Thanks!

Read out and check/post content of DMA/DMAMUX/ADC registers.

JW

Pavel A.
Evangelist III

Your MCU is STM32H743, It is not 72x/73x. ADC_VER_V5_V90 is not defined for H743.

Piranha
Chief II

My crystal ball could be wrong, but it says that the problem is because of a missing D-cache management. Turn off D-cache to test for it.

jean
Senior

Thanks a lot for your help, fixed the issue!

Indeed, turning off D-cache was fixing the problem.

I defined the VOLTAGE_DMA (buffer to read) to be in the linker section .dma_ram :

static uint16_t CV_INPUTS_DMA[kAdcInputs] __attribute__((section(".dma_ram")));
   .dma_ram (NOLOAD) :
  {
    . = ALIGN(4);
    _s_dma_ram = .;
    KEEP(*(.dma_ram))
    . = ALIGN(4);
    _e_dma_ram = .;
  } >DMA_RAM

So this buffer is not affected by the cache.

It's working perfectly now!