Introduction to UART + DMA (Fixed Length Reception)

πŸ“˜ Introduction to UART + DMA (Fixed Length Reception)

(This article focuses solely on DMA, excluding IDLE, which is essential to understand before learning about variable length reception.)

🎯 Objectives of this article:

  • β€’ Understand what DMA is
  • β€’ Why UART should use DMA
  • β€’ Configure UART + DMA
  • β€’ Implement “fixed length reception”
  • β€’ Implement “DMA transmission”
  • β€’ Clarify the advantages and disadvantages of DMA

The next article (3.5) will discuss “DMA + IDLE β†’ Variable Length Reception (Industrial Level)”

🧩 1. What is DMA?

DMA stands for:

Direct Memory Access

Understanding in simple terms: It acts as a “porter” for the MCU, automatically transferring data from peripherals to memory without CPU involvement.

πŸ”§ Understanding DMA with a Simple Analogy:

Imagine you are the “CPU” holding a bucket (RDR), and someone keeps delivering water to you (data received by UART). Traditional approach:

Every drop of water requires you to run and pour it into the tank (memory buffer) yourself β€” it’s exhausting, as you have to handle every drop.

What DMA does:

You hire a “porter” to handle the pouring of water. You only check and process when the “tank is full” β€” much easier, faster, and safer.

It’s that simple.

🟦 2. Why should UART use DMA? (Addressing the drawbacks of interrupts)

1) Fatal issues with interrupt reception (especially single-byte interrupts):

  • β€’ Every byte received β†’ triggers an interrupt
  • β€’ High byte frequency β†’ interrupt explosion
  • β€’ Any slight delay results in data loss
  • β€’ Callbacks cannot perform complex logic
  • β€’ CPU spends a lot of time “moving data”

If UART sends data at 115200bps:

11520 bytes per second β†’ 11520 interrupts/second β†’ MCU simply cannot handle it

DMA’s solution:

Every received byte β†’ DMA automatically transfers it to the buffer0 interrupts! The CPU doesn’t even know what’s happening

Only after you define the length as “full” does it trigger a callback.

🟧 3. This article only discusses: DMA reception of fixed length data

Applicable scenarios:

  • β€’ Sensors sending a fixed 16 bytes each time
  • β€’ Modules sending a fixed 8 bytes each time
  • β€’ Fixed format communication between MCUs
  • β€’ Need for stable data reception (not concerned about length variations)

Variable length β†’ will be discussed in the next article (DMA + IDLE).

In this article, you will implement:

β€œExecute a callback processing once 10 bytes are received”

🟩 4. CubeMX Configuration (only necessary parts for DMA)

βœ” 1. Enable UART (e.g., USART2)

  • β€’ Mode: Asynchronous
  • β€’ Check RX
  • β€’ Baud rate: 115200

βœ” 2. Configure DMA (key point)

In USART2 β†’ DMA Settings β†’ Add:

  • β€’ Direction: Peripheral to Memory
  • β€’ Mode: Normal (non-circular)
  • β€’ Priority: Medium

Why not use Circular?

Normal mode is the simplest for fixed length reception. Circular mode will be reserved for the next article on variable length.

Introduction to UART + DMA (Fixed Length Reception)

βœ” 3. Enable NVIC Interrupt (used by HAL)

USART2 Global Interrupt β†’ Enable

🟦 5. Code Structure (Fixed Length Reception)

We assume receiving 10 bytes (fixed length frame):

uint8_t dma_rx_buf[10];

β‘  Define the reception buffer

#define UART_RX_LEN 10
uint8_t dma_rx_buf[UART_RX_LEN];

β‘‘ Start DMA reception (execute once in main)

HAL_UART_Receive_DMA(&huart2, dma_rx_buf, UART_RX_LEN);

This means:

β€œUART, I want to use DMA to receive, putting the 10 bytes you receive into dma_rx_buf.”

β‘’ Reception complete callback (automatically called by DMA)

When DMA receives 10 bytes, it will trigger:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART2)
    {
        // Data has been completely written to dma_rx_buf by DMA
        UART_ProcessData(dma_rx_buf, UART_RX_LEN);

        // Restart DMA (otherwise it will only receive once)
        HAL_UART_Receive_DMA(&huart2, dma_rx_buf, UART_RX_LEN);
    }
}

You mustrestart DMA, otherwise reception will only occur once.

Introduction to UART + DMA (Fixed Length Reception)

🟩 6. How to Process Received Data?

Write a simple function:

void UART_ProcessData(uint8_t *data, uint16_t len)
{
    // Example: Echo back
    HAL_UART_Transmit(&huart2, data, len, 100);
}

🟦 7. DMA Transmission (optional but very useful)

HAL also supports DMA transmission:

HAL_UART_Transmit_DMA(&huart2, tx_buf, tx_len);

DMA transmission can:

  • β€’ Not block the CPU
  • β€’ Transfer large amounts of data faster
  • β€’ Avoid timeout issues with HAL_UART_Transmit

Suitable for real-time systems.

πŸŸ₯ 8. Errors and Troubleshooting

❌ 1. Callback not executed

Reasons:

  • β€’ UART interrupt not enabled
  • β€’ Incorrect DMA mode selected (must be Peripheral β†’ Memory)
  • β€’ Buffer length incorrectly specified
  • β€’ HAL_UART_Receive_DMA not called again

❌ 2. Data garbled

Reasons:

  • β€’ Baud rates on both sides do not match (115200 vs 9600)
  • β€’ Clock configuration errors leading to significant baud rate discrepancies

❌ 3. Program hangs

  • β€’ Do not use HAL_Delay in callbacks
  • β€’ Do not perform complex calculations in callbacks

🟩 9. Advantages and Disadvantages of Fixed Length DMA (Engineering Perspective)

Feature Description
Advantages πŸš€ Stable, no byte loss, no interrupt explosion, low CPU usage
Advantages πŸ‘ Suitable for fixed format sensors, protocol frames
Disadvantages ❌ Does not support variable length frames (to be addressed in the next article)
Disadvantages ❌ Does not know when a frame ends (needs to be combined with IDLE)

Therefore:

Fixed length DMA = the simplest and most stable method for large data reception in engineeringDMA + IDLE = industrial level variable length solution (next article)

🟦 10. Complete Minimal Working Template (can be copied directly)

#define UART_RX_LEN 10
uint8_t dma_rx_buf[UART_RX_LEN];

void UART_ProcessData(uint8_t *data, uint16_t len)
{
    // Echo back (for testing)
    HAL_UART_Transmit(&huart2, data, len, 100);
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART2)
    {
        UART_ProcessData(dma_rx_buf, UART_RX_LEN);

        // Restart DMA
        HAL_UART_Receive_DMA(&huart2, dma_rx_buf, UART_RX_LEN);
    }
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_USART2_UART_Init();

    // Start DMA reception
    HAL_UART_Receive_DMA(&huart2, dma_rx_buf, UART_RX_LEN);

    while (1)
    {
        // Main loop does its own thing
    }
}

πŸŽ‰ Summary of this article (can be directly pasted at the end of the article)

β€œDMA = Automatic Porter” It helps you automatically transfer the data received by UART to memory, without the CPU needing to process each time.

  • β€’ Fixed length transmission β†’ requires only DMA
  • β€’ Variable length transmission β†’ DMA + IDLE (next article)
  • β€’ The truly stable and popular method in engineering

πŸ‘‰ Next article (Part 3.5): DMA + IDLE (Idle Interrupt)

Allowing you to receive data frames of any length: sensors, modules, host protocols, industrial Modbus all use this set.

Leave a Comment