MCPcopy Index your code
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 60,649 documented · 96% updated 8d ago★ 1,7956 open issues
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 2 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 use CPU for other purposes

Table of Contents

GitHub supports ToC by default. It is 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 term UART.

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

  • Polling mode (no DMA, no IRQ)
    • P: Application is polling for status bits to check if any character has been transmitted/received and read it fast enough in order not to miss any byte
    • P: Easy to implement, simply a few lines of code
    • C: Can easily miss received data in complex application if CPU cannot read registers quickly enough
    • C: Works only for low baudrates, 9600 or lower
  • Interrupt mode (no DMA)
    • P: UART triggers interrupt and CPU jumps to service routine to handle each received byte separately
    • P: Commonly used approach in embedded applications
    • P: Works well with common baudrates, 115200, up to ~921600 bauds
    • C: Interrupt service routine is executed for every received character
    • C: May decrease system performance if interrupts are triggered for every character for 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 for processing received data by the application when necessary
    • P: Transfer from USART peripheral to memory is done on hardware level without CPU interaction
    • P: Works well with operating systems
    • P: Optimized for highest baudrates > 1Mbps and low-power applications
    • P: In case of big bursts of data, increasing data buffer size can improve functionality
    • C: Number of bytes to transfer must be known in advance by DMA hardware
    • C: If communication fails, DMA may not notify application about all bytes transferred

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

Every STM32 has at least one (1) UART IP and at least one (1) DMA controller available in its DNA. This is all we need for successful data transmission. The application uses default features to implement very efficient transmit system using DMA.

While the implementation is fairly straightforward for TX (set the pointer to data, define its length, and go) operation, 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, UART protocol does not offer such information (it could work with higher-level protocol, but that is another story entirely that we will not cover here. We assume we have 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 2 methods: - IDLE LINE event: Triggered when RX line has been in idle state (normally high state) for 1 frame time, after the last received byte. Frame time is based on baudrate. Higher baudrate means lower frame time for single byte. - RTO (Receiver Timeout) event: Triggered when line has been in idle state for 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: To transmit 1 byte at 115200 bauds, it takes approximately (for easier estimation purposes) ~100us; for 3 bytes it would be ~300us in total. IDLE line event triggers an interrupt when line has been in idle state for 1 frame time (in this case 100us), after third byte has been received.

IDLE LINE DEMO

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

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

General about DMA

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

  • Normal mode: DMA starts with data transfer, once it transfers all elements, it stops and sets enable bit to 0.
    • Application is using this mode when transmitting data
  • Circular mode: DMA starts with transfer, once it transfers all elements (as written in corresponding length register), it starts from beginning of memory and transfers more
    • Application is using this mode when receiving data

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

  • Half-Transfer complete HT: Triggers when DMA transfers half count of elements
  • Transfer-Complete TC: Triggers when DMA transfers all elements

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

Number of elements to transfer by DMA hardware must be written to relevant DMA register before start of 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 CPU. As for the sake of this example, we use memory buffer array of 20 bytes. DMA will transfer data received from UART to this buffer.

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

  • The application writes 20 to relevant DMA register for data length
  • The application writes memory & peripheral addresses to relevant DMA registers
  • Application sets DMA direction to peripheral-to-memory mode
  • Application puts DMA to circular mode. This is to assure DMA does not stop transferring data after it reaches end of memory. Instead, it will roll over and continue with transferring possible more data from UART to memory
  • The application enables DMA & UART in reception mode. Reception cannot start & DMA will wait for UART to receive first character and transmit it to array. This is done for every received byte
  • The application is notified by DMA HT event (or interrupt) after the first 10 bytes have been transferred from UART to memory
  • The application is notified by DMA TC event (or interrupt) after 20 bytes are transferred from UART to memory
  • The application is notified by UART IDLE line (or RTO) in case of IDLE line detection or timeout on the RX line
  • The application needs to rely on all of these events for most efficient receive

This configuration is important as we do not know length in advance. Application needs to assume it may be endless number of bytes received, therefore DMA must be operational endlessly.

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

Combine UART + DMA for data transmission

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

const char
hello_world_arr[] = "HelloWorld";
  • The application writes number of bytes to transmit to relevant DMA register, that would be strlen(hello_world_arr) or 10
  • The application writes memory & peripheral addresses to relevant DMA registers
  • Application sets DMA direction to memory-to-peripheral mode
  • Application sets DMA to normal mode. This effectively disables DMA once all the bytes are successfully transferred
  • The application enables DMA & UART in transmitter mode. Transmit starts immediately when UART requests first byte via DMA to be shifted to UART TX register
  • The application is notified by TC event (or interrupt) after all bytes have been transmitted from memory to UART via DMA
  • DMA is stopped and application may prepare 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, point B is UART data register. Now it is up to UART to clock out byte to GPIO pin

DMA HT/TC and UART IDLE combination details

This section describes 4 possible cases and one additional which 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 received data - Case B: DMA transfers next 10 bytes. The application receives a notification thanks to the TC event. Processing now starts from the last known position to the end of memory - DMA is in circular mode, thus it will continue right from beginning of the buffer, on top of the picture - Case C: DMA transfers 10 bytes, but not aligned with HT or TC events - 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 by the TC event when the first 4 bytes are transferred. Processing may start from the last known read location - The application receives a notification by the IDLE event after the next 6 bytes are transferred. Processing may start from beginning of buffer - Case E: Example of what may happen when the application relies only on the IDLE event - If application receives 30 bytes in burst, 10 bytes get overwritten by DMA as application did not process it quickly enough - The application gets the IDLE line event once the RX line is steady for 1 byte timeframe - Red part of data represents first 10 received bytes from burst which were overwritten by last 10 bytes in burst - An option to avoid such a scenario is to poll for DMA changes quicker than a burst of 20 bytes takes; or by using TC and HT events

Example code to read data from memory and process it, for cases A-D

```c / * \brief Check for new data received with DMA * * User must select context to call this function from: * - Only interrupts (DMA HT, DMA TC, UART IDLE) with same preemption priority level * - Only thread context (outside interrupts) * * If called from both contexts, exclusive access protection must be implemented * This mode is not advised as it usually means architecture design problems * * When IDLE interrupt is not present, application must rely only on thread context, * by manually calling function as quickly as possible, to make sure * data are read from raw buffer and processed. * * Not doing reads fast enough may cause DMA to overflow unread received bytes, * hence the application will lose useful data. * * Solutions to this are: * - Improve architecture design to achieve faster reads * - Increase raw buffer size and allow DMA to write more data before this function is called / void usart_rx_check(void) { / * Set

Core symbols most depended-on inside this repo

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