Why Every Embedded Engineer Should Master DMA (Direct Memory Access)
This article explains the purpose, operation modes, configuration parameters, and register-level setup of DMA in STM32 microcontrollers, illustrating how DMA offloads data transfers from the CPU, improves performance, and provides concrete code examples for UART and memory‑to‑memory transfers.
Definition and purpose
Direct Memory Access (DMA) copies data between peripherals and memory (or between memory regions) without CPU intervention, allowing the CPU to focus on computation and control tasks.
Transfer modes
Peripheral → memory
Memory → peripheral
Memory → memory
Peripheral → peripheral
All modes fundamentally copy data from a source address to a destination address.
Core transfer parameters
Four parameters are required for each DMA transfer: source address, destination address, transfer size (number of data items), and transfer mode (number of repetitions or circular operation).
STM32 DMA resources
High‑density STM32 devices provide two DMA controllers:
DMA1 – 7 channels
DMA2 – 5 channels
Each channel can be mapped to specific peripherals (TIM, ADC, SPI, I²C, USART, DAC, SDIO, etc.).
Arbitration and priority
Each channel has a programmable priority (very high, high, medium, low). When priorities are equal, the lower‑numbered channel wins. In large‑capacity parts DMA1 generally has higher priority than DMA2.
Data‑flow operation
Peripheral asserts a DMA request.
DMA controller acknowledges and starts the transfer.
DMA reads data from the peripheral (e.g., ADC) into its channel buffer.
DMA writes the data to SRAM via the AHB bus, completing the transfer without CPU involvement.
Transfer completion
A DMA transfer consists of three operations: read from the source address, write to the destination address, and decrement the DMA_CNDTRx register until it reaches zero.
Operation modes
Normal mode (DMA_Mode_Normal) – the transfer occurs once and stops.
Circular mode (DMA_Mode_Circular) – the transfer count is automatically reloaded, enabling continuous transfers.
Memory‑to‑memory transfers
Only DMA2 supports memory‑to‑memory mode. The MEM2MEM bit must be set, and circular mode cannot be used simultaneously.
Interrupts
Each channel can generate interrupts for half‑transfer, transfer‑complete, and error events. Status flags are read from DMA_ISR and cleared by writing zeros to the corresponding bits of DMA_IFCR.
Key registers
DMA_CPARx– peripheral address. DMA_CMARx – memory address. DMA_CNDTRx – remaining data count (0‑65535). DMA_CCRx – controls direction, increment mode, data width, priority, circular mode, and enable.
Configuration procedure
Write the peripheral address to DMA_CPARx.
Write the memory address to DMA_CMARx.
Write the transfer count to DMA_CNDTRx.
Set channel priority in DMA_CCRx[PL].
Configure direction, circular mode, increment modes, data widths, and interrupt enables in DMA_CCRx.
Enable the channel by setting the ENABLE bit in DMA_CCRx.
Standard Peripheral Library functions (STM32 SPL)
DMA_DeInit(DMA_ChannelX)– reset channel registers to default. DMA_Init(DMA_ChannelX, &DMA_InitStructure) – configure address, direction, size, increment, data width, mode, priority, and memory‑to‑memory flag. DMA_Cmd(DMA_ChannelX, ENABLE) – enable the channel. DMA_ITConfig(DMA_ChannelX, DMA_IT_TC, ENABLE) – enable transfer‑complete interrupt. DMA_SetCurrDataCounter(DMA_ChannelX, N) – set the transfer count. DMA_GetCurrDataCounter(DMA_ChannelX) – read the remaining count.
UART DMA example (STM32F1)
void dma_init(void) {
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base; // UART data register
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; // buffer address
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // memory → peripheral
DMA_InitStructure.DMA_BufferSize = 500;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel4, ENABLE);
DMA_SetCurrDataCounter(DMA1_Channel4, DMA1_MEM_LEN);
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
}
void DMA1_Channel4_IRQHandler(void) {
if (DMA_GetFlagStatus(DMA1_FLAG_TC4) == SET) {
DMA_ClearFlag(DMA1_FLAG_TC4);
}
}
int main(void) {
uart_init(115200);
for (uint16_t i = 0; i < 500; i++) {
SendBuff[i] = 0xAF;
}
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
while (1);
}This code configures DMA1 Channel 4 to transfer a 500‑byte buffer to the USART1 transmitter without CPU‑driven byte‑by‑byte copying.
Data‑flow comparison (ADC example)
Without DMA : the CPU reads ADC data from the peripheral register via the AHB bus, then writes it to SRAM, consuming CPU cycles for each sample.
With DMA :
ADC asserts a DMA request.
DMA controller acknowledges the request.
DMA reads the ADC sample from the peripheral and stores it in its channel buffer.
DMA writes the sample to SRAM via the AHB bus. The CPU is not involved.
Arbitration details
Software sets channel priority in DMA_CCRx (four levels). If two requests have equal software priority, the hardware grants the request to the lower‑numbered channel.
In large‑capacity and connectivity devices, DMA1 has higher overall priority than DMA2.
DMA data streams (STM32F4/M4)
Each data stream provides a unidirectional link between a source and a destination. Streams support:
Normal transactions (memory ↔ peripheral, memory ↔ memory).
Double‑buffer mode (two memory pointers for ping‑pong buffering).
Transfer size is programmable up to 65535 items and is decremented after each transaction.
Transfer channel configuration
Set peripheral address in DMA_CPARx.
Set memory address in DMA_CMARx.
Set data count in DMA_CNDTRx.
Set priority in DMA_CCRx[PL].
Configure direction, circular mode, increment modes, data widths, and interrupt enables in DMA_CCRx.
Enable the channel by setting the ENABLE bit in DMA_CCRx.
During operation, the half‑transfer flag (HTIF) is set after half the data is moved; the transfer‑complete flag (TCIF) is set when the count reaches zero. Corresponding interrupt enable bits (HTIE, TCIE) generate IRQs.
Pointer increment mode
The PINC and MINC bits in DMA_SxCR control whether peripheral and memory addresses are incremented after each transfer. Increment step equals the configured data width (8, 16, or 32 bits).
Memory‑to‑memory mode
When the MEM2MEM bit in DMA_CCRx is set, the DMA channel starts a transfer without a peripheral request. The transfer ends when DMA_CNDTRx reaches zero. This mode is only available on DMA2; DMA1 does not support it. Memory‑to‑memory cannot be combined with circular mode.
DMA interrupt handling
Each channel can generate three interrupt sources: half‑transfer, transfer‑complete, and transfer error. The status register DMA_ISR holds the flags; writing zero to the corresponding bits of DMA_IFCR clears them. In many devices DMA2 Channel 4 and Channel 5 share an interrupt vector; other channels have dedicated vectors.
DMA register overview
DMA_ISR – read‑only status flags (HTIFx, TCIFx, TEIFx).
DMA_IFCR – write‑only flag‑clear register.
DMA_CCRx – control register (direction, priority, increment, data size, mode, enable).
DMA_CNDTRx – current transfer count (decrements automatically).
DMA_CPARx – peripheral address (e.g., 0x40013804 for USART1 data register).
DMA_CMARx – memory address (e.g., address of SendBuff array).
Library‑function configuration flow
Enable the DMA clock: RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); Initialize the channel with DMA_Init() (set addresses, direction, size, widths, mode, priority, MEM2MEM flag).
Enable the peripheral’s DMA request (e.g., USART_DMACmd()).
Enable the DMA channel: DMA_Cmd().
Optionally enable interrupts with DMA_ITConfig() and monitor status via DMA_GetFlagStatus() or DMA_GetCurrDataCounter().
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Linux Tech Enthusiast
Focused on sharing practical Linux technology content, covering Linux fundamentals, applications, tools, as well as databases, operating systems, network security, and other technical knowledge.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
