STM32 Programming. Part 11: Copying Arrays via DMA
Posted: 17 Oct 2023, 04:36
In this short article we will look at the MEM2MEM mode and learn how to copy one memory location to another using DMA. All examples are, as always, for the stm32f103c8 microcontroller.
MEM2MEM mode has three features compared to normal DMA operation when we send any data to or receive anything from a peripheral:
We will copy data[] to buff[] via DMA. Initialize the arrays in this way:
for(int i=0; i<sizeof(data); i++)
{
data = i+1;
buff = 0;
}
Don't forget to enable DMA1 clocking and disable the selected DMA channel just in case:
Configuring the transfer registers:
DMA channel setup:
Pay attention to the direction of data transfer. Since we have entered the address of the array we are going to copy into the memory address register, the direction of data transfer is from "memory" to "periphery". Also, in comparison with the "normal" mode of DMA channel operation, here we use both memory address increment and peripheral address increment.
You can start the process with this line:
Full function code:
If you look at the values of data[] and buff[] arrays in the IAR debugger, they had these values before DMA-copying was started:
and after the process is complete, it's like this:
It's all working!
It's worth noting that if instead of
do it like this:
and remove DMA_CCR_DIR in DMA channel initialization:
Then everything will work exactly the same way.
MEM2MEM mode has three features compared to normal DMA operation when we send any data to or receive anything from a peripheral:
- MEM2MEM mode does not require any request from the peripheral modules. Once the DMA channel is enabled, the transfer process starts immediately.
- The memory address register DMA_CMARx and the peripheral address register DMA_CPARx are populated with memory area addresses.
- MEM2MEM mode and CIRC ring mode cannot be used at the same time.
Code: Select all
uint8_t data[10];
uint8_t buff[sizeof(data)];
for(int i=0; i<sizeof(data); i++)
{
data = i+1;
buff = 0;
}
Don't forget to enable DMA1 clocking and disable the selected DMA channel just in case:
Code: Select all
RCC->AHBENR |= RCC_AHBENR_DMA1EN; //Enable DMA1 clocking
DMA1_Channel1->CCR &= ~DMA_CCR_EN; //Disable the channel before tuning.
Code: Select all
DMA1_Channel1->CNDTR = sizeof(data); //how many elements to copy
DMA1_Channel1->CMAR = (uint32_t)(data); //what we copy (memory address)
DMA1_Channel1->CPAR = (uint32_t)(buff); //where we copy (address of "periphery")
Code: Select all
DMA1_Channel1->CCR =.
DMA_CCR_MEM2MEM //from memory to memory
| DMA_CCR_MINC //memory increment
| DMA_CCR_PINC // peripheral increment
| DMA_CCR_DIR; //direction from "memory" to "peripheral".
You can start the process with this line:
Code: Select all
DMA1_Channel1->CCR |= DMA_CCR_EN; //start the process
Code: Select all
uint8_t data[10];
uint8_t buff[sizeof(data)];
void main()
{
for(int i=0; i<sizeof(data); i++)
{
data[i] = i+1;
buff[i] = 0;
}
RCC->AHBENR |= RCC_AHBENR_DMA1EN; //Enable DMA1 clocking
DMA1_Channel1->CCR &= ~DMA_CCR_EN; //Disable channel before tuning
DMA1_Channel1->CNDTR = sizeof(data); //how many elements to copy
DMA1_Channel1->CMAR = (uint32_t)(data); //what we copy (memory address)
DMA1_Channel1->CPAR = (uint32_t)(buff); //where we copy (address of "periphery")
DMA1_Channel1->CCR = (uint32_t(buff); //where we copy (address of "periphery").
DMA_CCR_MEM2MEM //from memory to memory
| DMA_CCR_MINC //memory increment
| DMA_CCR_PINC // peripheral increment
| DMA_CCR_DIR; //direction from "memory" to "periphery"
DMA1_Channel1->CCR |= DMA_CCR_EN; //start the process
for(;;)
{
}
}
and after the process is complete, it's like this:
It's all working!
It's worth noting that if instead of
Code: Select all
DMA1_Channel1->CMAR = (uint32_t)(data); //what we copy (address of "memory")
DMA1_Channel1->CPAR = (uint32_t)(buff); //where we copy (address of "periphery")
Code: Select all
DMA1_Channel1->CMAR = (uint32_t)(buff); //where we copy (address of "memory")
DMA1_Channel1->CPAR = (uint32_t)(data); //what we copy (address of "periphery")
Code: Select all
DMA1_Channel1->CCR =.
DMA_CCR_MEM2MEM //from memory to memory
| DMA_CCR_MINC //memory increment
| DMA_CCR_PINC; //increment of peripherals.