Getting Started with STM32F103: DMA in Memory to Memory Mode

In this guide, we shall configure the DMA to transfer data from one memory location to another.

This can save tone of CPU cycles compare to using traditional memcpy where it uses cpu rather than DMA.

In this guide, we shall cover the following:

  • DMA configuration for memory to memory transfer.
  • Code.
  • Results.

1. DMA Configuration for Memory to Memory transfer:

The DMA can be triggered to transfer data without a request from a peripheral as following:

Within main.c, declare a function that will enable clock access to DMA1:

From the datasheet, we can find that DMA1 is connected to AHB bus, hence we can enable clock access to DMA1 as following (STM32F103C8T6 has only single DMA):

void DMA1_CLK_Enable()
{
	RCC->AHBENR|=RCC_AHBENR_DMA1EN;
}

declare a volatile variable to be used as flag:

volatile uint8_t done;

Declare two array to to hold the send and received data:

uint32_t data_send[5];
uint32_t data_rece[5];

Now, we need to configure the DMA as following:

  • M2M Mode.
  • Peripheral and Memory size to word (32-bit).
  • Memory and peripheral increment.
  • Transfer complete interrupt.

Note: Circular mode in not valid in this configuration.

Declare a function that will transfer between two memory location using DMA as following:

void DMA1_M2M_Transfer(uint32_t *src, uint32_t *dest, uint16_t len)

The function takes three parameters:

  • Source array.
  • Destination array.
  • Length of the array.

Within thins function put the DMA configuration:

	DMA1_Channel1->CCR|=DMA_CCR_MEM2MEM|DMA_CCR_MINC|DMA_CCR_PINC|DMA_CCR_TCIE
			|DMA_CCR_MSIZE|DMA_CCR_PSIZE;

Enable the DMA1_Channle1 interrupt in NVIC:

NVIC_EnableIRQ(DMA1_Channel1_IRQn);

Set the peripheral address to be the source:

DMA1_Channel1->CPAR= (uint32_t) src;

Memory address to be the destination:

DMA1_Channel1->CMAR= (uint32_t)dest;

Set number of transfer to be the required length:

DMA1_Channel1->CNDTR = len;

Finally launch the DMA:

DMA1_Channel1->CCR|=DMA_CCR_EN;

For the interrupt handler:

Check if the source is interrupt transfer complete:

If it is, set done to 1, clear the flag and disable the DMA.

void DMA1_Channel1_IRQHandler (void)
{
	if((DMA1->ISR & DMA_ISR_TCIF1)==DMA_ISR_TCIF1)
	{
		done=1;
		DMA1->IFCR=DMA_IFCR_CTCIF1;
		DMA1_Channel1->CCR&=~DMA_CCR_EN;
	}

}

Hence, the entire code as following:

#include "stm32f1xx.h"
#include "stdlib.h"

uint32_t data_send[5];
uint32_t data_rece[5];


volatile uint8_t done;


void DMA1_CLK_Enable()
{
	RCC->AHBENR|=RCC_AHBENR_DMA1EN;
}


void DMA1_M2M_Transfer(uint32_t *src, uint32_t *dest, uint16_t len)
{

	DMA1_Channel1->CCR|=DMA_CCR_MEM2MEM|DMA_CCR_MINC|DMA_CCR_PINC|DMA_CCR_TCIE
			|DMA_CCR_MSIZE|DMA_CCR_PSIZE;

	NVIC_EnableIRQ(DMA1_Channel1_IRQn);

	DMA1_Channel1->CPAR= (uint32_t) src;

	DMA1_Channel1->CMAR= (uint32_t)dest;

	DMA1_Channel1->CNDTR = len;

	DMA1_Channel1->CCR|=DMA_CCR_EN;


}



int main(void)
{
	DMA1_CLK_Enable();

	while(1)
	{

		for(int i=0;i<5;i++)
		{
			data_send[i]=random()%256;
		}

		DMA1_M2M_Transfer(data_send,data_rece,5);

		while(done==0);
		done=0;

		for (int i=0;i<10000;i++);
	}
}

void DMA1_Channel1_IRQHandler (void)
{
	if((DMA1->ISR & DMA_ISR_TCIF1)==DMA_ISR_TCIF1)
	{
		done=1;
		DMA1->IFCR=DMA_IFCR_CTCIF1;
		DMA1_Channel1->CCR&=~DMA_CCR_EN;
	}

}

For testing purposes, we shall fill the 5 elements of the array with random values from 0 to 255 as following:

		for(int i=0;i<5;i++)
		{
			data_send[i]=random()%256;
		}

2. Code:

You may download the code from here:

3. Results:

Happy coding 🙂

Add Comment

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