cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H753: DMA sometimes does not transfer data

Moritz1
Associate III

Hello,
I have a problem on a STM32H753 which I do not understand.

I want to sample some data from ADC with a given time period.
So I use the TIM2_TRGO as Event generator for the ADC3 (via EXTSEL)
ADC3 is configured in 12 Bit single conversion, in the sequence register there is only one entry, the length ist 1.

TIM2 is configured in a way that it generates an Event each 4ms

For Data transfer I use the DMA1_Stream0
The corresponding DMAMUX channel is set to 115 accordingly.

On the ADC input Pin I provide a sinus signal.

For debugging reasons I initialise my RAM array with 1.

When the transfer is complete, I see in the Data Array that there are missing some data.
One result for example:

 

1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2252, 2283, 2258, 2178, 2049, 1880, 1680, 1463, 1245, 1048, 924, 825, 768, 730, 734, 775, 830, 935, 1052, 1250, 1468, 1685, 1883, 2054, 2182, 2260, 2283, 2250, 2165, 2031, 1855, 1654, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1

 

The data not equal to 1 are correct ADC values which I expect.

The result differs each run. Here for example the result of another record:

 

1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2631, 2827, 2971, 3043, 3049, 2971, 2828, 2626, 2384, 2097, 1810, 1531, 1273, 1059, 888, 795, 767, 802, 914, 1097, 1332, 1585, 1869, 2159, 2428, 2660, 2855, 2980, 3042, 3025, 2948, 2803, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2963, 3036, 3041, 2970, 2833, 2635, 2392, 2118, 1840, 1560, 1297, 1071, 902, 797, 760, 802, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1

 

  • The transfer complete and half transfer interrput flags are set. So the DMA really do its correct amount of transfers.
  • If I sampe the data by CPU/software just doing single conversions of the ADC with sleep() between, everything looks fine. So the signal ist korrekt and the ADC works correctly too.
  • There is no other DMA_Stream active/configured
  • For testing I disabled all Interrupts to be shure that no other code can interrupt the DMA transfer. And I did the function blocking, waiting until transfer is complete to be 100% shure, no other code can influence anything during transfer.
  • If I Initialize the RAM Array with e.g. 9 instead of 1, all the 1s in the data above are 9s. So its shure that the DMA have not overwritten this Array fields.

 

9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 807, 912, 1090, 1315, 1578, 1869, 2168, 2431, 2659, 2843, 2991, 3031, 3028, 2953, 2806, 2589, 2351, 2073, 1779, 1497, 1245, 1033, 876, 789, 768, 815, 943, 1119, 1350, 1622, 1906, 2192, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 820, 942, 1129, 1359, 1626, 1915, 2207, 2471, 2708, 2861, 2989, 3040, 3020, 2935, 2770, 2549, 9, 9, 9, 9

 

 

Here is the code I use:

 

	//Config ADC
	ADC3->SMPR1 |= ADC_SMPR1_SMP1_1;
	ADC3->PCSEL |= ADC_PCSEL_PCSEL_1;												//Select input channel
	ADC3->CFGR = (ADC3->CFGR &~ADC_CFGR_RES_Msk) | ADC_CFGR_RES_1;					//set 12 Bit resolution
	ADC3->CFGR = (ADC3->CFGR &~ADC_CFGR_EXTSEL_Msk) | (11 << ADC_CFGR_EXTSEL_Pos); 	//use adc_ext_trg11 as trigger (TIM2_TRGO)
	ADC3->CFGR = (ADC3->CFGR &~ADC_CFGR_EXTEN_Msk) | ADC_CFGR_EXTEN_0;	            //enable external trigger on rising edge
	ADC3->CFGR = (ADC3->CFGR &~ADC_CFGR_DMNGT_Msk) | ADC_CFGR_DMNGT_0;	            //DMA One Shot Mode selected
	ADC3->SQR1 = 0;	        //clear complete sampling sequence (automatically set sequencelength = 1)
	ADC3->SQR1 |= (1 << ADC_SQR1_SQ1_Pos);		//set channel to be converted
	ADC3->CR |= ADC_CR_ADSTART;

	for(int i = 0; i < traceObj->length; i++)
	{
		traceObj->data[i] = 0;
	}

	//Config DMA
	__HAL_RCC_DMA1_CLK_ENABLE();
	DMA1->LIFCR |= DMA_LIFCR_CTCIF0;					//Clear Transfer complete Interrupt
	DMA1->LIFCR |= DMA_LIFCR_CHTIF0;					//Clear Half Transfer Interrupt
	DMA1_Stream0->PAR = (uint32_t) &ADC3->DR;	        //Peripheral Address
	DMA1_Stream0->M0AR = (uint32_t) traceObj->data;		//MemoryAddress
	DMA1_Stream0->CR |= DMA_SxCR_MINC;			        //memory increment
	DMA1_Stream0->CR &= ~DMA_SxCR_DIR_Msk;			    //transfer direction: peripheral -> memory
	DMA1_Stream0->NDTR = traceObj->length;
	DMA1_Stream0->CR &= ~DMA_SxCR_CIRC;			        //disable circular mode
	DMA1_Stream0->CR |= DMA_SxCR_PSIZE_0;			    //Peripheral Size: 16 Bit
	DMA1_Stream0->CR |= DMA_SxCR_MSIZE_0;			    //Memory size: 16 Bit
	DMA1_Stream0->CR |= DMA_SxCR_PL_1;			        //High priority
	DMA1_Stream0->CR |= DMA_SxCR_EN;				    //enable DMA channel

	//Config DMAMUX
	DMAMUX1_Channel0->CCR |= 115;	    //select ADC3 as input for DMA1 Channel 0

    //Config Timer
	__HAL_RCC_TIM2_CLK_ENABLE();
	TIM2->CR1 &= ~TIM_CR1_CEN;      //Disable Timer
	TIM2->PSC = 200;
	TIM2->ARR = 4000;				//je nach Frequenz...
	TIM2->CR2 |= TIM_CR2_MMS_1;		//update mode --> update event is selected as trigger Output

	__disable_irq();

    TIM2->CR1 |= TIM_CR1_CEN;       //Enable Timer

    while(DMA1_Stream0->NDTR != 0);

    __enable_irq();

 

 

What can be the reason that the DMA decrement its NDTR correctly, but sometimes/often transfers no data to the RAM?
Maybe someone here has an idea what the problem could be?

1 ACCEPTED SOLUTION

Accepted Solutions

Hi,

ST support was able to help me.

You are right. Its a problem with the D-Cache. The D-Cache does not recognize that the DMA has changed the RAM content. If you try to read the data, you only get the D-Cache Date. So you have to invalidate the D-Cache before.

Adding the line

SCB_CleanInvalidateDCache();

has solved my problem.

I am using the M7 for the first time and was not yet familiar with this mechanism with the D-Cache.

View solution in original post

6 REPLIES 6
Moritz1
Associate III

Maybe some additional info:

I also tryed it with ADC1 and ADC2, but still have the same problem.

traceObj->data is placed in SRAM3, beginning from address 0x30040000.

So in this case all used peripherals are in D2 Domain (DMA1, ADC1/2 [AHB1], SRAM3)

 

I really need help. I dont understand what is going wrong here.

BarryWhit
Senior III

Here  are some ideas you may want to try:

 

1. You seem like a bare-metal purist but, if CubeMX supports your intended usage, perhaps it would help to generate a project and see if you get the same kind of skips. There's a good chance you can find an official CubeIDE example of DMA+ADC for the H7x series which you can get running fairly quickly on your board.

2. does changing clock frequency up or down have any marked effect on the result?

3. Does changing the sample rate (from 4ms to say 50ms) have any effect?

4. Have you independently verified the timer's firing rate (perhaps you got the calculation wrong?). Can you use the same settings to toggle a pin with output compare? or implement an interrupt handler that captures the tick delta between firings?

5. I'm not familiar with the H7xxx series, but on more modest chips you can adjust the acquisition time in cycles (and you need to adjust it for faster clock rates). If this exists on your chip, have you tried playing with it?

6. Are you checking for any kind of error experienced by the ADC (I'm assuming there's some kind of interrupt/register bit for that)?

- If a post has answered your question, please acknowledge the help you received by clicking "Accept as Solution".
- Once you've solved your issue, please consider posting a summary of any additional details you've learned. Your new knowledge may help others in the future.

 


@waclawek.jan wrote:

Data cache issue?


Good Catch.

From README of example  ADC_DMA_Transfer  for H743 (probably applies to H753 too)

 

@Note If the application is using the DTCM/ITCM memories (@0x20000000/ 0x0000000: not cacheable and only accessible
by the Cortex M7 and the  MDMA), no need for cache maintenance when the Cortex M7 and the MDMA access these RAMs.
      If the application needs to use DMA(or other masters) based access or requires more RAM, then  the user has to:
              - Use a non TCM SRAM. (example : D1 AXI-SRAM @ 0x24000000)
              - Add a cache maintenance mechanism to ensure the cache coherence between CPU and other masters(DMAs,DMA2D,LTDC,MDMA).
        - The addresses and the size of cacheable buffers (shared between CPU and other masters)
must be properly defined to be aligned to L1-CACHE line size (32 bytes).
 

 

- If a post has answered your question, please acknowledge the help you received by clicking "Accept as Solution".
- Once you've solved your issue, please consider posting a summary of any additional details you've learned. Your new knowledge may help others in the future.

Hi,

ST support was able to help me.

You are right. Its a problem with the D-Cache. The D-Cache does not recognize that the DMA has changed the RAM content. If you try to read the data, you only get the D-Cache Date. So you have to invalidate the D-Cache before.

Adding the line

SCB_CleanInvalidateDCache();

has solved my problem.

I am using the M7 for the first time and was not yet familiar with this mechanism with the D-Cache.

You still should read @Piranha's post I gave link to. And, of course, the Cortex-M7 Programming Manual. And the H7 memory/architectural appnote.

JW