Chapter 7: Introduction to DMA

📚 CMPE2250: Lesson — Introducing DMA with UART (Based on RM0444, Chapter 10)

UART by itself is simple: it sends and receives bytes through a shift register. But without DMA, the CPU must service every byte through interrupts or polling. That’s fine for low throughput, but it collapses when:

  • Bursts of data arrive unpredictably
  • The CPU is busy with timing-sensitive tasks

To understand real embedded workflows, it is required:

  • Deterministic latency

DMA solves this by autonomously moving data between UART and memory.

🎯 Learning Objectives

By the end of this lesson students should be able to:

  • Describe how the DMA controller autonomously transfers UART data to memory.

  • Configure a DMA channel for UART reception, explain the role of key DMA parameters (direction, increment modes, data width, request mapping).

  • Verify correct operation by observing buffer behavior and DMA status registers.

  • Use the DMA controller in conjunction with the RTO USART feature.

1️⃣ What DMA actually does (RM0444 Ch. 10 distilled)

DMA is a hardware engine that performs memory transfers without CPU intervention.

For UART, the relevant transfer types are:

  • Peripheral → Memory - Used for UART reception.

    • DMA reads from the UART RDR register and writes into a buffer.
  • Memory → Peripheral - Used for UART transmission.

    • DMA reads from a memory buffer and writes into UART TDR.
  • Peripheral → Peripheral - Used for UART passthrough, for instance.

2️⃣ Key DMA concepts from Chapter 10

These are the ones students must internalize:

Concept Meaning
Channel A DMA “pipe” that moves data for one peripheral request.
Request mapping UART RX/TX are mapped to specific DMA channels.
Direction P→M for RX, M→P for TX.
Increment mode Memory increments, peripheral does not.
Data width UART is 8‑bit, so byte transfers.
Circular mode Ideal for continuous UART reception.
Transfer complete / half‑transfer flags Used for buffer management and verification.

3️⃣ Minimal mental model

I like to frame DMA as a robot assistant:

  • UART says: “I have a byte ready.”
  • DMA robot hears the request.
  • DMA robot picks up the byte from RDR.
  • DMA robot places it into the next memory slot.
  • CPU only checks in occasionally (“robot, how full is the buffer?”).

4️⃣ The simplest useful scenario: UART RX with DMA

Workflow

  1. Configure UART normally (baud, pins, enable).

  2. Enable DMA clock. (e.g.RCC->AHBENR |= RCC_AHBENR_DMA1EN; //Enable DMA controller 1)

  3. Select the DMA channel mapped to UART RX. (e.g. DMA1_Channel1)

  4. Set:

    • Direction -> peripheral → memory

    • Memory increment = enabled (DMAChannel->CCR |= DMA_CCR_MINC;)

    • Peripheral increment = disabled (DMAChannel->CCR &= ~DMA_CCR_PINC;)

    • Data width = byte (MSIZE and PSIZE in DMAChannel->CCR)

    • Circular mode = optional (For later)

    • Set memory address (DMAChannel->CMAR = (uint32_t)memAddr;)

    • Set peripheral address (DMAChannel->CPAR = (uint32_t)perAddr;)

    • Configure the DMAMUX(New feature in new STM32 micros)

      • (e.g. DMAMUX1_Channel0->CCR = 52; // Table 55, RM 11.3.2)

      • Reference Table

    • Start transfer

      • Disable channel (DMAChannel->CCR &= ~DMA_CCR_EN)

      • Set transfer size (DMAChannel->CNDTR = size;)

      • Enable channel (DMAChannel->CCR |= DMA_CCR_EN)

    • Enable UART DMA reception (Set DMAR in USART->CR3).

    Now UART will continuously fill the buffer.

4.1 DMAMUX table

DMA Request MUX Input Resource DMA Request MUX Input Resource DMA Request MUX Input Resource
1 dmamux_req_gen0 27 TIM2_CH2 53 USART2_TX
2 dmamux_req_gen1 28 TIM2_CH3 54 USART3_RX
3 dmamux_req_gen2 29 TIM2_CH4 55 USART3_TX
4 dmamux_req_gen3 30 TIM2_TRIG 56 USART4_RX
5 ADC 31 TIM2_UP 57 USART4_TX
6 AES_IN 32 TIM3_CH1 58 UCPD1_RX
7 AES_OUT 33 TIM3_CH2 59 UCPD1_TX
8 DAC_Channel1 34 TIM3_CH3 60 UCPD2_RX
9 DAC_Channel2 35 TIM3_CH4 61 UCPD2_TX
10 I2C1_RX 36 TIM3_TRIG 62 I2C3_RX
11 I2C1_TX 37 TIM3_UP 63 I2C3_TX
12 I2C2_RX 38 TIM6_UP 64 LPUART2_RX
13 I2C2_TX 39 TIM7_UP 65 LPUART2_TX
14 LPUART_RX 40 TIM15_CH1 66 SPI3_RX
15 LPUART_TX 41 TIM15_CH2 67 SPI3_TX
16 SPI1_RX 42 TIM15_TRIG_COM 68 TIM4_CH1
17 SPI1_TX 43 TIM15_UP 69 TIM4_CH2
18 SPI2_RX 44 TIM16_CH1 70 TIM4_CH3
19 SPI2_TX 45 TIM16_COM 71 TIM4_CH4
20 TIM1_CH1 46 TIM16_UP 72 TIM4_TRIG
21 TIM1_CH2 47 TIM17_CH1 73 TIM4_UP
22 TIM1_CH3 48 TIM17_COM 74 USART5_RX
23 TIM1_CH4 49 TIM17_UP 75 USART5_TX
24 TIM1_TRIG_COM 50 USART1_RX 76 USART6_RX
25 TIM1_UP 51 USART1_TX 77 USART6_TX
26 TIM2_CH1 52 USART2_RX

How to verify it’s working

  • Check the DMA NDTR register
    • Watch the remaining transfer count decrease and wrap around.
  • Print the buffer over UART TX -A loopback demonstration is extremely satisfying.

  • Toggle a GPIO on half-transfer / transfer-complete interrupts
    • Great for oscilloscope validation.
  • Inject known patterns
    • e.g., send “AAAAABBBBBCCCCCDDDD” and watch the buffer fill.
  1. Conceptual diagram
    flowchart TD
        UART[UART RX / Receives bytes] --> RDR[UART RDR Register]

        RDR -- DMA request --> DMA[DMA Channel\nP→M Transfer Engine]

        DMA --> BUFFER[Buffer in RAM]

5️⃣ Where we can go next

  • What happens if the RX buffer overflows? Can we catch this?

  • How to add UART TX with DMA

  • Explore DMA interrupts

  • Compare DMA vs. interrupt-driven UART