Working with DMA and STM32: Memory to Memory Mode

In pervious multiple guides, we took look at ADC with DMA (here), USART with DMA (here) and SPI-TX with DMA (here). In this guide, we shall use DMA to transfer data from one location to another using DMA.

In this guide, we will cover the following:

  • DMA memory to memory initialization
  • Memory to Memory transfer
  • Code

1. DMA Memory to Memory Initialization:

The DMA channels can also work without being triggered by a request from a peripheral. This is the memory-to-memory mode, described in figure below.

When the stream is enabled by setting the Enable bit (EN) in the DMA_SxCR register, the stream immediately starts to fill the FIFO up to the threshold level. When the threshold level is reached, the FIFO contents are drained and stored into the destination.The transfer stops once the DMA_SxNDTR register reaches zero or when the EN bit in the DMA_SxCR register is cleared by software.

Hence, we can use any DMA and any stream for memory to memory transfer mode.

In this guide, we shall use DMA2 Stream 0 for this experiment.

We start off by enabling clock access to DMA2 as following:

RCC->AHB1ENR|=RCC_AHB1ENR_DMA2EN;

Reseting DMA2 Stream 0 and wait until the stream is disabled

	DMA2_Stream0->CR=0x00;
	//wait until 
	while(DMA2_Stream0->CR & DMA_SxCR_EN){;}

Then, the DMA configuration shall be as following:

  • Set memory and peripheral size to 16-bit.
  • Memory increment, peripheral increment
  • direction memory to memory, transfer complete interrupt, transfer error interrupt and direct mode error.
DMA2_Stream0->CR|=DMA_SxCR_MSIZE_0
	|DMA_SxCR_PSIZE_0|DMA_SxCR_MINC|DMA_SxCR_PINC
	|DMA_SxCR_DIR_1|DMA_SxCR_TCIE|DMA_SxCR_TEIE|DMA_SxCR_DMEIE;

Set FIFO threshold to Full and disable direct mode

	DMA2_Stream0->FCR |= DMA_SxFCR_DMDIS;
  DMA2_Stream0->FCR |= (DMA_SxFCR_FTH_0 | DMA_SxFCR_FTH_1);

Finally enable interrupt in NVIC

NVIC_EnableIRQ(DMA2_Stream0_IRQn);

For interrupt handler

//DMA2 interrupt handler
	void DMA2_Stream0_IRQHandler(void)
			{
			if((DMA2->LISR)&DMA_LISR_TCIF0)
					{
					//set finished to 1
					finished=1;
					//clear transfer complete interrupt
					DMA2->LIFCR=DMA_LIFCR_CTCIF0;
					}
			if((DMA2->LISR)&DMA_LISR_TEIF0)
					{
					//transfer error interrupt
					DMA2->LIFCR=DMA_LIFCR_CTEIF0;
					}
					
			if((DMA2->LISR)&DMA_LISR_DMEIF0)
					{
				//direct mode error
					DMA2->LIFCR=DMA_LIFCR_CDMEIF0;
					}
			
			}

2. Memory to Memory transfer

We start of defining our function

void send_data(uint32_t src, uint32_t des, uint16_t len)

It takes three argument:

  • Source
  • Destination
  • Length

In order to transfer data from memory location to another, we need to set the source data in the peripheral address as mentioned in the reference manual

Hence, we can set it as following:

DMA2_Stream0->PAR=(uint32_t)src;

Note: it is important to typecast it to uint32_t

Now, we can set the destination as following:

DMA2_Stream0->M0AR=(uint32_t)des;

The number of transfers

DMA2_Stream0->NDTR=len;

Finally we can enable the transfer

DMA2_Stream0->CR|=DMA_SxCR_EN;

3. Code:

#include "stm32f4xx.h"                  // Device header

//data to be send
uint16_t data_s[4];
//data to be received
uint16_t data_r[4];
uint16_t var=2;
//indicate that DMA finished transfered to out application
volatile uint8_t finished=0;



void send_data(uint32_t src, uint32_t des, uint16_t len)
	{
	DMA2_Stream0->PAR=(uint32_t)src;
	DMA2_Stream0->M0AR=(uint32_t)des;
	DMA2_Stream0->NDTR=len;
	DMA2_Stream0->CR|=DMA_SxCR_EN;
	
	}



int main(void)
	{
	//Enable DMA1 clock access
	RCC->AHB1ENR|=RCC_AHB1ENR_DMA2EN;
	//reset DMA1_stream0 
	DMA2_Stream0->CR=0x00;
	//wait until 
	while(DMA2_Stream0->CR & DMA_SxCR_EN){;}
	/*set memory and peripheral size to 16-bit, 
	Memory increment, peripheral increment,
	direction memory to memory and transfer complete interrupt
	*/
	DMA2_Stream0->CR|=DMA_SxCR_MSIZE_0
	|DMA_SxCR_PSIZE_0|DMA_SxCR_MINC|DMA_SxCR_PINC
	|DMA_SxCR_DIR_1|DMA_SxCR_TCIE|DMA_SxCR_TEIE|DMA_SxCR_DMEIE;
	DMA2_Stream0->FCR |= DMA_SxFCR_DMDIS;
  DMA2_Stream0->FCR |= (DMA_SxFCR_FTH_0 | DMA_SxFCR_FTH_1);
	NVIC_EnableIRQ(DMA2_Stream0_IRQn);
		
	while(1)
		{
		for (int i=0;i<4;i++)
				{
				data_s[i]=var;
				}
			send_data((uint32_t)data_s,(uint32_t)data_r,4);
			var++;	
			while(finished==0){;}
			finished=0;		
				for (int j=0;j<400000;j++);
				
		}
	
	
	
	}
	//DMA2 interrupt handler
	void DMA2_Stream0_IRQHandler(void)
			{
			if((DMA2->LISR)&DMA_LISR_TCIF0)
					{
					//set finished to 1
					finished=1;
					//clear transfer complete interrupt
					DMA2->LIFCR=DMA_LIFCR_CTCIF0;
					}
			if((DMA2->LISR)&DMA_LISR_TEIF0)
					{
					//transfer error interrupt
					DMA2->LIFCR=DMA_LIFCR_CTEIF0;
					}
					
			if((DMA2->LISR)&DMA_LISR_DMEIF0)
					{
				//direct mode error
					DMA2->LIFCR=DMA_LIFCR_CDMEIF0;
					}
			
			}

Add Comment

Your email address will not be published. Required fields are marked *