MCPcopy Create free account
hub / github.com/MaJerle/stm32-usart-uart-dma-rx-tx

github.com/MaJerle/stm32-usart-uart-dma-rx-tx @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
63,442 symbols 96,657 edges 3,166 files ⚖ Apache-2.0 60,649 documented · 96% updated 16d ago★ 1,8075 open issues

Browse by type

Functions 62,662 Types & classes 780
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

STM32 UART DMA RX and TX

This application note contains explanations with examples for two distinct topics:

  • Data reception with UART and DMA when the application does not know the number of bytes to receive in advance
  • Data transmission with UART and DMA to avoid CPU stalling and free the CPU for other tasks

How to use this repository

We start with the examples instructions. The most attractive part of the repository.

All examples are developed with CMake build system generation, Ninja build system and GCC compiler. Each example comes with .vscode folder and provides basic set of files for recommended extensions, simple tasks, launch/debug config and C/C++ extension intellisense configuration for CMake data provider.

  1. Clone the repository: git clone https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx or download the zip package.
  2. Install CMake, Ninja and the ARM GCC compiler (arm-none-eabi-gcc) and make sure they are available on your PATH. Either install each of them separately or download and install STM32CubeCLT, CLI tools for STM32 development.
  3. Open the project folder with Visual Studio code or use command line tool to build the project with cmake

To build all examples at once with python helper script:

python3 scripts/build.py [--clean] [--path projects/project-folder]

To manually build selected project with cmake

cd projects/projects-folder
cmake --list-presets
cmake --preset <preset_name>
cmake --build --preset <preset_name>

Table of Contents

GitHub automatically generates a table of contents, available in the top-left corner of this document.

Abbreviations

  • DMA: Direct Memory Access controller in STM32
  • UART: Universal Asynchronous Receiver Transmitter
  • USART: Universal Synchronous Asynchronous Receiver Transmitter
  • TX: Transmit
  • RX: Receive
  • HT: Half-Transfer Complete DMA event/flag
  • TC: Transfer Complete DMA event/flag
  • RTO: Receiver Timeout UART event/flag
  • IRQ: Interrupt

General about UART

STM32 includes peripherals like USART, UART, and LPUART. For the purposes of this example, the specific differences between them aren't important, since the same concept applies to all. In a few words, USART supports synchronous operation on top of asynchronous (UART) and LPUART supports Low-Power operation in STOP mode. When synchronous mode or low-power mode is not used, USART, UART and LPUART can be considered identical. For a complete set of details, check the product's reference manual and datasheet.

For the sake of this application note, we will only use the term UART.

UART in STM32 allows configuration using different transmit (TX) and receive (RX) modes:

  • Polling mode (no DMA, no IRQ)
    • Advantages:
      • The application polls status bits to check whether a character has been transmitted or received, and reads it quickly enough to avoid missing any byte
      • Easy to implement, just a few lines of code
    • Disadvantages:
      • Can easily miss received data in a complex application if the CPU cannot read registers quickly enough
      • Works only for low baudrates, 9600 or lower
  • Interrupt mode (no DMA)
    • Advantages:
      • UART triggers an interrupt and the CPU jumps to a service routine that handles each received byte separately
      • Commonly used approach in embedded applications
      • Works well with common baudrates, 115200, up to ~921600 baud
    • Disadvantages:
      • Interrupt service routine is executed for every received character
      • May decrease system performance if interrupts are triggered for every character at high-speed baudrates
  • DMA mode
    • DMA is used to transfer data from the USART RX data register to user memory at the hardware level. No application interaction is needed at this point, except when the application needs to process the received data
    • Advantages:
      • Transfer from the USART peripheral to memory is done at the hardware level, without CPU interaction
      • Works well with operating systems
      • Optimized for the highest baudrates (> 1Mbps) and for low-power applications
      • For large bursts of data, increasing the buffer size can improve throughput
    • Disadvantages:
      • The number of bytes to transfer must be known by the DMA hardware in advance
      • If communication fails, DMA may not notify the application about all bytes transferred

This guide focuses exclusively on DMA-based RX operation and explains how to handle cases where the data length is unknown.

Every STM32 has at least one UART peripheral and at least one DMA controller built in. This is all we need for successful data transmission. The application uses default features to implement a very efficient DMA-based transmit system.

The implementation for TX operation is fairly straightforward (set the pointer to the data, define its length, and go), but this may not be the case for receive. When implementing DMA receive, the application needs to know the number of received bytes to be processed by DMA before it is considered done. However, the UART protocol does not offer such information. (A higher-level protocol could provide it, but that is a separate topic not covered here — we assume we need to implement a very reliable low-level communication protocol.)

Idle Line or Receiver Timeout events

STM32 UART peripherals can detect when the RX line remains inactive for a certain period of time. This is done using two methods: - IDLE line event: Triggered when the RX line has been in idle state (normally high) for one frame time after the last received byte. Frame time is based on the baudrate — a higher baudrate means a shorter frame time for a single byte. - RTO (Receiver Timeout) event: Triggered when the line has been idle for a programmable time. It is fully configurable by firmware.

Both events can trigger an interrupt, which is an essential feature for effective receive operation.

Not all STM32 have IDLE line or RTO features available. When not available, examples concerning these features may not be used.

An example: transmitting 1 byte at 115200 baud takes approximately ~100us; 3 bytes would take ~300us in total. The IDLE line event triggers an interrupt when the line has been idle for 1 frame time (in this case ~100us) after the third byte has been received.

IDLE LINE DEMO

This is a real experiment using STM32F4 and the IDLE line event. After the IDLE event is triggered, data is echoed back (loopback mode):

  • The application receives 3 bytes, taking approx ~300us at 115200 baud
  • RX goes to high state (yellow rectangle) and UART RX detects it has been idle for at least 1 frame time (approx ~100us)
    • The width of the yellow rectangle represents 1 frame time
  • The IDLE line interrupt is triggered at the green arrow
  • The application echoes the data back from interrupt context

General about DMA

DMA in STM32 can be configured in normal or circular mode. For each mode, DMA requires the number of elements to transfer before its events (half-transfer complete, transfer complete) are triggered.

  • Normal mode: DMA starts the data transfer; once it has transferred all elements, it stops and sets the enable bit to 0.
    • The application uses this mode when transmitting data
  • Circular mode: DMA starts the transfer; once it has transferred all elements (as written in the corresponding length register), it starts again from the beginning of memory and transfers more
    • The application uses this mode when receiving data

While a transfer is active, 2 (among others) interrupts may be triggered:

  • Half-Transfer complete (HT): Triggers when DMA has transferred half the elements
  • Transfer-Complete (TC): Triggers when DMA has transferred all elements

When DMA operates in circular mode, these interrupts are triggered periodically.

The number of elements to transfer must be written to the relevant DMA register before the start of the transfer.

Combine UART + DMA for data reception

Now it is time to understand which features to use to receive data with UART and DMA to offload the CPU. For the sake of this example, we use a memory buffer array of 20 bytes. DMA will transfer data received from UART to this buffer.

Listed are the steps to begin. The initial assumption is that UART has been initialized prior to reaching this step, and the same for basic DMA setup:

  • The application writes 20 to the relevant DMA register for data length
  • The application writes the memory and peripheral addresses to the relevant DMA registers
  • The application sets the DMA direction to peripheral-to-memory mode
  • The application puts DMA into circular mode. This ensures DMA does not stop transferring data after it reaches the end of memory. Instead, it rolls over and continues transferring any further data from UART to memory
  • The application enables DMA and UART in reception mode. Reception cannot start until DMA waits for UART to receive the first character and transmit it to the array; this happens for every received byte
  • The application is notified by the DMA HT event (or interrupt) after the first 10 bytes have been transferred from UART to memory
  • The application is notified by the DMA TC event (or interrupt) after 20 bytes have been transferred from UART to memory
  • The application is notified by the UART IDLE line (or RTO) event in case of IDLE line detection or a timeout on the RX line
  • The application needs to rely on all of these events for the most efficient reception

This configuration is important, as we do not know the length in advance. The application must assume it may receive an endless number of bytes, so DMA must operate endlessly.

We used a 20-byte-long array for demonstration purposes. In a real application, this size may need to be increased. It depends on the UART baudrate (a higher speed means more data may be received in a fixed window) and on how fast the application can process the received data (using interrupt notification, RTOS, or polling mode)

Combine UART + DMA for data transmission

Everything gets simpler when the application transmits data: the length of the data is known in advance, and the memory to transmit is ready. For the sake of this example, we use memory for the HelloWorld message. In C language it would be:

const char
hello_world_arr[] = "HelloWorld";
  • The application writes the number of bytes to transmit to the relevant DMA register, that would be strlen(hello_world_arr) or 10
  • The application writes the memory and peripheral addresses to the relevant DMA registers
  • The application sets the DMA direction to memory-to-peripheral mode
  • The application sets DMA to normal mode. This effectively disables DMA once all the bytes are successfully transferred
  • The application enables DMA and UART in transmitter mode. Transmission starts immediately when UART requests the first byte via DMA to be shifted into the UART TX register
  • The application is notified by the TC event (or interrupt) after all bytes have been transmitted from memory to UART via DMA
  • DMA is stopped and the application may prepare the next transfer immediately

Please note that the TC event is triggered before the last UART byte has been fully transmitted over UART. That is because the TC event is part of DMA and not part of UART. It is triggered when DMA transfers all the bytes from point A to point B. That is, point A for DMA is memory, and point B is the UART data register. It is then up to UART to clock the byte out to the GPIO pin.

DMA HT/TC and UART IDLE combination details

This section describes 4 possible cases, plus one additional case that explains why HT and TC events are both necessary in the application.

DMA events

Abbreviations used for the image: - R: Read pointer, used by the application to read data from memory. Later also used as old_ptr - W: Write pointer, used by the DMA to write next byte to. Increased every time DMA writes new byte. Later also used as new_ptr - HT: Half-Transfer Complete event triggered by DMA - TC: Transfer-Complete event - triggered by DMA - I: IDLE line event - triggered by USART

DMA configuration: - Circular mode - 20 bytes data length - Consequently HT event gets triggered at 10 bytes being transmitted - Consequently TC event gets triggered at 20 bytes being transmitted

Possible cases during real-life execution: - Case A: DMA transfers 10 bytes. The application receives a notification via the HT event and may process the received data - Case B: DMA transfers the next 10 bytes. The application receives a notification via the TC event. Processing now starts from the last known position to the end of memory - DMA is in circular mode, so it continues right from the beginning of the buffer, at the top of the picture - Case C: DMA transfers 10 bytes, but not aligned with HT or TC events - The application gets notified with the HT event when the first 6 bytes are transferred. Processing may start from the last known read location - The application receives the IDLE line event after the next 4 bytes are successfully transferred to memory - Case D: DMA transfers 10 bytes in overflow mode and not aligned with HT or TC events - The application receives a notification via the TC event when the first 4 bytes are transferred. Processing may start from the last known read location - The application receives a notification via the IDLE event after the next 6 bytes are transferred. Processing may start

Core symbols most depended-on inside this repo

browse all functions →

Shape

Function 62,662
Class 780

Languages

C++55%
C45%
Python1%

Modules by API surface

drivers/STM32G4xx_HAL_Driver/Inc/stm32g4xx_ll_hrtim.h408 symbols
drivers/STM32F3xx_HAL_Driver/Inc/stm32f3xx_ll_hrtim.h343 symbols
drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_ll_hrtim.h336 symbols
drivers/STM32G4xx_HAL_Driver/Inc/stm32g4xx_ll_rtc.h332 symbols
drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_ll_rcc.h323 symbols
drivers/STM32U5xx_HAL_Driver/Inc/stm32u5xx_ll_rcc.h316 symbols
drivers/STM32L5xx_HAL_Driver/Inc/stm32l5xx_ll_rtc.h306 symbols
drivers/STM32G4xx_HAL_Driver/Inc/stm32g4xx_ll_tim.h261 symbols
drivers/STM32L4xx_HAL_Driver/Inc/stm32l4xx_ll_usart.h260 symbols
drivers/STM32WLxx_HAL_Driver/Inc/stm32wlxx_ll_rtc.h256 symbols
drivers/STM32U5xx_HAL_Driver/Inc/stm32u5xx_ll_usart.h254 symbols
drivers/STM32WLxx_HAL_Driver/Inc/stm32wlxx_ll_usart.h249 symbols

For agents

$ claude mcp add stm32-usart-uart-dma-rx-tx \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page