Skip to main content
Stecklo
Senior
November 11, 2019
Solved

DMA + ADC doesn't work

  • November 11, 2019
  • 14 replies
  • 15362 views

CubeIDE 1.1.0

STM32Cube_FW_WB_V1.3.0

Trying to make DMA transfer ADC results.

I'm putting code in main() after MX_..._Init() and before while(1) {}

HAL_ADC_Start_IT(&hadc1); works fine

HAL_DMA_Start_IT(&hdma_memtomem_dma1_channel2, (uint32_t) src, (uint32_t) dest, 40); works fine too

but

HAL_ADC_Start_DMA(&hadc1, dest, 10) doesn't work as it should

ADC is activated and keeps throwing Overrun_Error

But DMA doesn't transfer anything

static void MX_ADC1_Init(void)
{
 
 /* USER CODE BEGIN ADC1_Init 0 */
 
 /* USER CODE END ADC1_Init 0 */
 
 ADC_ChannelConfTypeDef sConfig = {0};
 
 /* USER CODE BEGIN ADC1_Init 1 */
 
 /* USER CODE END ADC1_Init 1 */
 /** Common config 
 */
 hadc1.Instance = ADC1;
 hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV8;
 hadc1.Init.Resolution = ADC_RESOLUTION_12B;
 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
 hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
 hadc1.Init.LowPowerAutoWait = DISABLE;
 hadc1.Init.ContinuousConvMode = ENABLE;
 hadc1.Init.NbrOfConversion = 1;
 hadc1.Init.DiscontinuousConvMode = DISABLE;
 hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
 hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
 hadc1.Init.DMAContinuousRequests = ENABLE;
 hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
 hadc1.Init.OversamplingMode = ENABLE;
 hadc1.Init.Oversampling.Ratio = ADC_OVERSAMPLING_RATIO_16;
 hadc1.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_NONE;
 hadc1.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
 hadc1.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_CONTINUED_MODE;
 if (HAL_ADC_Init(&hadc1) != HAL_OK)
 {
 Error_Handler();
 }
 /** Configure Regular Channel 
 */
 sConfig.Channel = ADC_CHANNEL_5;
 sConfig.Rank = ADC_REGULAR_RANK_1;
 sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
 sConfig.SingleDiff = ADC_DIFFERENTIAL_ENDED;
 sConfig.OffsetNumber = ADC_OFFSET_NONE;
 sConfig.Offset = 0;
 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN ADC1_Init 2 */
 
 /* USER CODE END ADC1_Init 2 */
 
}
static void MX_DMA_Init(void) 
{
 
 /* DMA controller clock enable */
 __HAL_RCC_DMAMUX1_CLK_ENABLE();
 __HAL_RCC_DMA1_CLK_ENABLE();
 
 /* Configure DMA request hdma_memtomem_dma1_channel2 on DMA1_Channel2 */
 hdma_memtomem_dma1_channel2.Instance = DMA1_Channel2;
 hdma_memtomem_dma1_channel2.Init.Request = DMA_REQUEST_MEM2MEM;
 hdma_memtomem_dma1_channel2.Init.Direction = DMA_MEMORY_TO_MEMORY;
 hdma_memtomem_dma1_channel2.Init.PeriphInc = DMA_PINC_ENABLE;
 hdma_memtomem_dma1_channel2.Init.MemInc = DMA_MINC_ENABLE;
 hdma_memtomem_dma1_channel2.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
 hdma_memtomem_dma1_channel2.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
 hdma_memtomem_dma1_channel2.Init.Mode = DMA_NORMAL;
 hdma_memtomem_dma1_channel2.Init.Priority = DMA_PRIORITY_LOW;
 if (HAL_DMA_Init(&hdma_memtomem_dma1_channel2) != HAL_OK)
 {
 Error_Handler( );
 }
 
 /* DMA interrupt init */
 /* DMA1_Channel1_IRQn interrupt configuration */
 HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 15, 0);
 HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
 
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
 GPIO_InitTypeDef GPIO_InitStruct = {0};
 if(hadc->Instance==ADC1)
 {
 /* USER CODE BEGIN ADC1_MspInit 0 */
 
 /* USER CODE END ADC1_MspInit 0 */
 /* Peripheral clock enable */
 __HAL_RCC_ADC_CLK_ENABLE();
 
 __HAL_RCC_GPIOA_CLK_ENABLE();
 /**ADC1 GPIO Configuration 
 PA0 ------> ADC1_IN5
 PA1 ------> ADC1_IN6 
 */
 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);
 
 /* ADC1 DMA Init */
 /* ADC1 Init */
 hdma_adc1.Instance = DMA1_Channel1;
 hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
 hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
 hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
 hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
 hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
 hdma_adc1.Init.Mode = DMA_NORMAL;
 hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
 if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
 {
 Error_Handler();
 }
 
 __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
 
 /* USER CODE BEGIN ADC1_MspInit 1 */
 
 /* USER CODE END ADC1_MspInit 1 */
 }
 
}

I've tried this confuguration on PNUCLEO-WB55 Dongle in far more complex code and it worked. Now I'm using custom board and just main() body. But I think the board should not matter as DMA is totally internal thing

This topic has been closed for replies.
Best answer by Stecklo

The answer is to put

MX_DMA_Init();

before

MX_ADC1_Init();

As the first one calls

__HAL_RCC_DMAMUX1_CLK_ENABLE();

 __HAL_RCC_DMA1_CLK_ENABLE();

14 replies

Ozone
Principal
November 11, 2019

I don't know the WB55 device, and usually do not play with Cube code.

Have you checked the DMA (instance/channel) is correct ?

Stecklo
SteckloAuthor
Senior
November 11, 2019

I'm away from my PC now. But the code above looks like it should work (it is all auto generated by CubeMX). Altough I can't see the explicit initialization of hadc->DMA_handle. But Ithink it is handled in __HAL_LINKDMA()

waclawek.jan
Super User
November 11, 2019

Read out and check the DMA and DMAMUX registers.

> hdma_memtomem_dma1_channel2.Init.Request = DMA_REQUEST_MEM2MEM;

> hdma_memtomem_dma1_channel2.Init.Direction = DMA_MEMORY_TO_MEMORY;

Most probably not.

JW

Stecklo
SteckloAuthor
Senior
November 12, 2019

@Community member​ , you were right.

DMAMUX_CxCR is empty

This line does nothing. While operands are 5 and 255. The result should be 5, but CCR is 0.

HAL_StatusTypeDef HAL_DMA_Init(DMA_HandleTypeDef *hdma) 
{
 <...>
 hdma->DMAmuxChannel->CCR = (hdma->Init.Request & DMAMUX_CxCR_DMAREQ_ID);
 <...>
}

I havent located the reason so far, but this is a good clue.

Stecklo
SteckloAuthorBest answer
Senior
November 12, 2019

The answer is to put

MX_DMA_Init();

before

MX_ADC1_Init();

As the first one calls

__HAL_RCC_DMAMUX1_CLK_ENABLE();

 __HAL_RCC_DMA1_CLK_ENABLE();

Stecklo
SteckloAuthor
Senior
November 12, 2019

Is there any way to make CubeMX aka Device Configuration Tool to change the Init functions order? There is a corresponding view in Project Manager tab, but the order seems to be fixed.

Stecklo
SteckloAuthor
Senior
November 12, 2019

Well, I've changed the order manually editing .ioc file with text editor. But the whole thing should be considered as a CubeMX bug

Amel NASRI
ST Technical Moderator
November 13, 2019

Hello,

Glad that you found the root cause of your issue.

I'll report this limitation to our STM32CubeMX team.

-Amel

To give better visibility on the answered topics, please click on "Best Answer" on the reply which solved your issue or answered your question.
Nesrine.JLASSI
Visitor II
November 15, 2019

Hello @Community member​ 

Thanks for the feedback,

It will be fixed on the next version of CubeMX.

Best regards,

Nesrine

Fibo
Associate II
January 13, 2020

Hey @Nesrine.JLASSI​ 

This still hasn't been fixed in CubeMX v5.5.0

I spent many hours debugging

Jim Seymour
Senior
November 15, 2019

In case it helps, I've just spent the last two days debugging this issue on a STM32F030RC.

In my code (also generated by CubeIDE), MX_DMA_Init() does very little (enables the clock and interrupt handler). Later, MX_ADC_Init() calls HAL_ADC_Init() - which calls HAL_ADC_MspInit() - which calls HAL_DMA_Init().

This last function tries to set the CCR register - but it has no effect. (The register remains zero). I'm guessing this is because __HAL_RCC_DMA1_CLK_ENABLE() hasn't been called (?)

Regardless, if I move MX_DMA_Init() just before MX_ADC_Init(), everything seems to work.

Definitely a bug in CubeIDE - but at least I have a workaround.

Jim Seymour
Senior
November 15, 2019

One last comment: In my case, adding __HAL_RCC_DMA1_CLK_ENABLE(); to the "USER CODE BEGIN SysInit" block in main() also does the trick.

This fix has the advantage of allowing future code generation cycles without having to manually edit the results.