How to Use DMA for Memory to Memory Transfer in Embedded C
To use
DMA for memory to memory transfer in embedded C, configure the DMA controller with source and destination addresses, set the transfer size, and enable the DMA channel. Then start the transfer and wait for completion or an interrupt. This offloads data copying from the CPU, improving efficiency.Syntax
DMA memory to memory transfer requires setting up the DMA controller registers or using a HAL library. Key parts include:
- Source Address: The start address of the data to copy.
- Destination Address: The target memory location.
- Transfer Size: Number of bytes or words to transfer.
- DMA Channel: The specific DMA stream or channel to use.
- Control Flags: Settings like data size, increment mode, and transfer direction.
Example syntax using STM32 HAL:
c
DMA_HandleTypeDef hdma_memtomem; hdma_memtomem.Instance = DMA1_Stream0; hdma_memtomem.Init.Channel = DMA_CHANNEL_0; hdma_memtomem.Init.Direction = DMA_MEMORY_TO_MEMORY; hdma_memtomem.Init.PeriphInc = DMA_PINC_ENABLE; hdma_memtomem.Init.MemInc = DMA_MINC_ENABLE; hdma_memtomem.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hdma_memtomem.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; hdma_memtomem.Init.Mode = DMA_NORMAL; hdma_memtomem.Init.Priority = DMA_PRIORITY_HIGH; hdma_memtomem.Init.FIFOMode = DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&hdma_memtomem); HAL_DMA_Start(&hdma_memtomem, (uint32_t)srcAddress, (uint32_t)dstAddress, dataLength);
Example
This example shows how to use DMA on an STM32 microcontroller to copy an array from one memory location to another without CPU intervention.
c
#include "stm32f4xx_hal.h" #include <string.h> uint32_t srcBuffer[5] = {10, 20, 30, 40, 50}; uint32_t dstBuffer[5] = {0}; DMA_HandleTypeDef hdma_memtomem; void SystemClock_Config(void) {} void Error_Handler(void) { while(1); } int main(void) { HAL_Init(); SystemClock_Config(); __HAL_RCC_DMA1_CLK_ENABLE(); hdma_memtomem.Instance = DMA1_Stream0; hdma_memtomem.Init.Channel = DMA_CHANNEL_0; hdma_memtomem.Init.Direction = DMA_MEMORY_TO_MEMORY; hdma_memtomem.Init.PeriphInc = DMA_PINC_ENABLE; hdma_memtomem.Init.MemInc = DMA_MINC_ENABLE; hdma_memtomem.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hdma_memtomem.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; hdma_memtomem.Init.Mode = DMA_NORMAL; hdma_memtomem.Init.Priority = DMA_PRIORITY_HIGH; hdma_memtomem.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_memtomem) != HAL_OK) { Error_Handler(); } if (HAL_DMA_Start(&hdma_memtomem, (uint32_t)srcBuffer, (uint32_t)dstBuffer, 5) != HAL_OK) { Error_Handler(); } // Wait for transfer complete while (HAL_DMA_PollForTransfer(&hdma_memtomem, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY) != HAL_OK) {} // Now dstBuffer contains copied data for (int i = 0; i < 5; i++) { // Print or use dstBuffer[i] } while (1) {} }
Common Pitfalls
Common mistakes when using DMA for memory to memory transfer include:
- Not enabling the DMA clock before configuration.
- Incorrect source or destination address alignment causing transfer errors.
- Forgetting to enable increment mode for source and destination addresses.
- Not waiting for the DMA transfer to complete before accessing destination data.
- Using wrong data size settings (byte, half-word, word) leading to corrupted data.
Always check return values of HAL functions and handle errors.
c
/* Wrong: Missing increment mode */ hdma_memtomem.Init.PeriphInc = DMA_PINC_DISABLE; // Incorrect hdma_memtomem.Init.MemInc = DMA_MINC_DISABLE; // Incorrect /* Correct: Enable increment mode */ hdma_memtomem.Init.PeriphInc = DMA_PINC_ENABLE; hdma_memtomem.Init.MemInc = DMA_MINC_ENABLE;
Quick Reference
Tips for DMA memory to memory transfer:
- Enable DMA controller clock before use.
- Set source and destination addresses correctly.
- Enable increment mode for both source and destination.
- Choose correct data alignment (byte, half-word, word).
- Start DMA and wait for transfer completion.
- Use interrupts for efficient transfer completion handling.
Key Takeaways
Configure DMA with correct source, destination, and transfer size for memory to memory copy.
Enable increment mode for both source and destination addresses to copy data properly.
Always enable the DMA clock and check for errors during initialization and start.
Wait for DMA transfer completion before using the destination data to avoid corruption.
Use DMA interrupts for efficient CPU usage during large or frequent transfers.