Skip to main content
DSchl.1
Associate III
April 15, 2020
Question

STM32H7 ADC with DMA - DMA-Transfer not initiated

  • April 15, 2020
  • 5 replies
  • 4892 views

Hi everyone

I'm trying to get the ADC1 running with a DMA transfer on the STM32H757ZI. The goal is to measure 10 channels with 8 samples each and transfer these measurements to a separated SRAM1 buffer with DMA, each time the conversion sequence has been finished.

The problem I have is, that the ADC1 seems to work properly, since I'm reading some data in the DR, but the DMA transfer is never initiated. I have enabled the EOC and EOS interrupts, the corresponding ISR clears the EOC / EOS bits. I have set the DMNGT to DMA circular mode, which I assume enables the DMA functionality for the ADC peripheral. The transfer complete interrupt for the DMA is triggered, but no data is written to the buffer...

In addition, I have used the following DMA settings:

  • DMA1 clock enable
  • Set direction to peripheral to memory
  • disabled FIFO mode
  • Set memory size to Half Word
  • Set memory increment mode to increment
  • Set memory address to SRAM1 buffer address
  • Set mode to dma mode circular
  • Set peripheral size to Half Word
  • Set peripheral increment mode to no increment
  • Set peripheral request to DMAMUX1_REQ_ADC1
  • Set peripheral address to address of the DR
  • Enable TC interrupt

However, there's no data copied into the SRAM1 buffer... what am I missing here? Is there some additional setting I have to enable?

edit: I've set up the buffer according to this link :

https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices

I'm using Solution 2 - placing the buffer in a different memory region.

Regards

Daniel.

This topic has been closed for replies.

5 replies

April 15, 2020

Seeing the code would help.

DSchl.1
DSchl.1Author
Associate III
April 15, 2020

It surely would.. I just thought there was an obvious thing I missed...

This is the init routine

void init_ADC()
{
	__ADC12_CLK_ENABLE();
	__HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_CLKP);
	LL_ADC_Disable(ADC1);
 
	LL_C1_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_ADC12);
 
	if (LL_ADC_IsDeepPowerDownEnabled(ADC1) != 0UL)
		LL_ADC_DisableDeepPowerDown(ADC1);
 
	if (LL_ADC_IsInternalRegulatorEnabled(ADC1) == 0UL)
	{
		LL_ADC_EnableInternalRegulator(ADC1); 								 
		__IO uint32_t wait_loop_index = 0UL;
		wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US / 10UL) * (SystemCoreClock / (100000UL * 2UL)))+1000;
		while (wait_loop_index != 0UL)
		{
		 wait_loop_index--;
		}
	}
 
	LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_CLOCK_ASYNC_DIV2);
	LL_ADC_SetResolution(ADC1, LL_ADC_RESOLUTION_16B);
	LL_ADC_REG_SetSequencerLength(ADC1, LL_ADC_REG_SEQ_SCAN_ENABLE_10RANKS);
	LL_ADC_SetLowPowerMode(ADC1, LL_ADC_LP_MODE_NONE);
	LL_ADC_REG_SetContinuousMode(ADC1, LL_ADC_REG_CONV_CONTINUOUS);
	LL_ADC_REG_SetSequencerDiscont(ADC1, LL_ADC_REG_SEQ_DISCONT_DISABLE);
	LL_ADC_REG_SetTriggerSource(ADC1, LL_ADC_REG_TRIG_SOFTWARE);
	LL_ADC_REG_SetTriggerEdge(ADC1, ADC_EXTERNALTRIGCONVEDGE_NONE);
	LL_ADC_REG_SetDataTransferMode(ADC1, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);
	LL_ADC_REG_SetOverrun(ADC1, LL_ADC_REG_OVR_DATA_OVERWRITTEN);
	LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
 
	ADC1->PCSEL |= BIT(2)|BIT(3)|BIT(10)|BIT(11) // Preselect channels
				 |BIT(14)|BIT(15)|BIT(16)
				 |BIT(17)|BIT(18)|BIT(19);
 
	LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_2, 
 LL_ADC_SAMPLINGTIME_387CYCLES_5);
	LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_3, 
 LL_ADC_SAMPLINGTIME_387CYCLES_5);
	LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_10, 
 LL_ADC_SAMPLINGTIME_387CYCLES_5);
// other channels omitted
 
	LL_ADC_SetChannelSingleDiff(ADC1, LL_ADC_CHANNEL_2, LL_ADC_SINGLE_ENDED);
	LL_ADC_SetChannelSingleDiff(ADC1, LL_ADC_CHANNEL_3, LL_ADC_SINGLE_ENDED);
	LL_ADC_SetChannelSingleDiff(ADC1, LL_ADC_CHANNEL_10, LL_ADC_SINGLE_ENDED);
// other channels omitted
 
	LL_ADC_SetOffset(ADC1, ADC_OFFSET_NONE, LL_ADC_CHANNEL_2, 0);
	LL_ADC_SetOffset(ADC1, ADC_OFFSET_NONE, LL_ADC_CHANNEL_3, 0);
	LL_ADC_SetOffset(ADC1, ADC_OFFSET_NONE, LL_ADC_CHANNEL_10, 0);
// other channels omitted
 
	LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_2);
	LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_3);
	LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_3, LL_ADC_CHANNEL_10);
// other channels omitted
 
	
	LL_ADC_EnableIT_EOS(ADC1); // End of regular sequence of conversions IR enable
	LL_ADC_EnableIT_EOC(ADC1); // End of conversion IR enable
}

This is the DMA init routine, called right after the one above:

void init_DMA_ADC()
{
	__HAL_RCC_DMA1_CLK_ENABLE();
	LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
	LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);
	LL_DMA_SetMemoryBurstxfer(DMA1, LL_DMA_STREAM_1, LL_DMA_MBURST_SINGLE);
	LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_1, LL_DMA_MDATAALIGN_HALFWORD);
	LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MEMORY_INCREMENT);
	LL_DMA_SetMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MODE_CIRCULAR);
	LL_DMA_SetPeriphBurstxfer(DMA1, LL_DMA_STREAM_1, LL_DMA_PBURST_SINGLE);
	LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_HALFWORD);
	LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_PERIPH_NOINCREMENT);
	LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_ADC1);
	LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_1, LL_DMA_PRIORITY_HIGH);
 
	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_1, reinterpret_cast<uint32_t>(&adcTypeDef->DR));
 LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_1, 0x3000A000); // SRAM1 buffer for ADC data
 
	LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_1);
}

The interrupts are enabled :

void enable_adc_interrupt()
{
	HAL_NVIC_SetPriority(ADC_IRQn, 6, 0);
	HAL_NVIC_EnableIRQ(ADC_IRQn);
}
 
void enable_dma_interrupt()
{
	HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 4, 0);
	HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
}

Then I enable and start the ADC :

void enable_ADC()
{
	LL_ADC_Enable(ADC1);
	while (LL_ADC_IsEnabled(adcTypeDef) == 0UL);
}
 
void start_ADC(uint32_t size)
{
	LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, size);
	LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_1);
	LL_ADC_REG_StartConversion(adcTypeDef);
}

In the ISR I clear the EOS / EOC flags if they are set. Same for the Transfer Complete flag of the DMA.

Imen GH
ST Employee
April 15, 2020

​Hello,

There's an FAQ giving a good explanation and provides the solution.

https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices

The below link can help you to solve your problem (Please focus on my answer)

https://community.st.com/s/question/0D50X0000C7blnwSQA/adc-scan-continuous-mode-with-dma-in-stm32h7-all-values-are-0?t=1582799150965

Regards

DSchl.1
DSchl.1Author
Associate III
April 15, 2020

Hi,

I already know the FAQ... The buffer I'm using is a static buffer defined in the linker file and located in the SRAM1 at 0x3000A000. I can see that this address space gets correctly initialized to all zeros. In the MPU I have also configured this memory as not cacheable etc. In the second link, the address is 0x24000000 which would be AXI SRAM, but this should not be the issue here isn't it?

The point is, I have already set up the USART1 in a similar way, and this works without problems with DMA...

I can also see that the Transfer Complete interrupt is triggered, but no data is written...

Curtis B.
Associate III
April 17, 2020

Hi, did you align the base adress of your buffer according to the buffersize? I struggled with this for a long time, but it seems that this information is now added in the FAQ. Back then, taht was one of these valuable informations that were only handed down from father to son...

DSchl.1
DSchl.1Author
Associate III
April 17, 2020

The base address of the buffer is 0x3000A000, alignment is 2 Bytes. The ADC value is 16bits, and the DMA should automatically increase the memory pointer by half word, so the alignment should be ok. I have set up the USART1 TX DMA in a similar way regarding alignment and configuration, and this one works.

Curtis B.
Associate III
April 17, 2020

Hi Daniel,

it depends on the size you protect with your MPU. E.g. your DMA Buffer is 666 bytes in size you might set the Region size in the MPU to 1k. That means, that the region you protect with your MPU needs to be aligned to 1024 bytes. The DMA Buffer needs to be located in that region.

Regards,

Daniel

waclawek.jan
Super User
April 20, 2020

> I can also see that the Transfer Complete interrupt is triggered,

And how are other DMA status bits set? Isn't there an error flag set?

JW

DSchl.1
DSchl.1Author
Associate III
May 7, 2020

Hi and sorry for this late reply.. I have setup the ADC without DMA for now... we are developing other parts and are a bit under pressure (as usual)... I'll come back to this as soon as I'm working on the ADC again..

Thanks and regards

Daniel.

JJilg.1
Associate
December 10, 2021

Hi,

I am having the same problem, did you find something?

SavasSahin
Associate II
May 9, 2022

Hi DSchl.1,

I faced the same problem.

I do not know if you solve the problem, but you should invalidate cache when DMA transfer completed by using "SCB_InvalidateDCache_by_Addr" function.

All the bests,

Savas

Piranha
Principal III
May 10, 2022

Wrong. The invalidation must be done before starting the reception on a particular buffer by DMA.

An instruction of proper cache management:

https://community.st.com/s/question/0D53W00000oXSzySAG/different-cache-behavior-between-stm32h7-and-stm32f7

Topic about the cache eviction:

https://community.st.com/s/question/0D50X0000C9hGozSQE/weird-cache-writeback-behavior-for-stm32f7508

SavasSahin
Associate II
May 17, 2022

Hi Piranha,

Thank you for your answer. I will check them.

I align the DMA buffers so that when invalidation no other addresses affected.

After DMA transmission completed. I only invalidate cache with that dma buffer. In this way no need to clean the cache. Cache reloads with new values which was updated by DMA transfer with new values.

This works with stm32h750 and stm32h753/743 devices perfectly.

Will it cause any problems that i can not see yet?

All the bests,

Savas