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