使用 DMA 实现高效的 RX 和 TX 传输的 STM32 USART 示例
This application note contains explanations with examples for two distinct topics:
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.
git clone https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx or download the zip package.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.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>
GitHub automatically generates a table of contents, available in the top-left corner of this document.
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:
9600 or lower115200, up to ~921600 baud> 1Mbps) and for low-power applicationsThis 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.)
STM32 UART peripherals can detect when the RX line remains inactive for a certain period of time. This is done using two methods:
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.
This is a real experiment using STM32F4 and the IDLE line event. After the IDLE event is triggered, data is echoed back (loopback mode):
3 bytes, taking approx ~300us at 115200 baud1 frame time (approx ~100us)1 frame timeDMA 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.
0.While a transfer is active, 2 (among others) interrupts may be triggered:
HT): Triggers when DMA has transferred half the elementsTC): Triggers when DMA has transferred all elementsWhen 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.
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:
20 to the relevant DMA register for data lengthHT event (or interrupt) after the first 10 bytes have been transferred from UART to memoryTC event (or interrupt) after 20 bytes have been transferred from UART to memoryThis 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)
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";
strlen(hello_world_arr) or 10TC event (or interrupt) after all bytes have been transmitted from memory to UART via DMAPlease note that the
TCevent is triggered before the last UART byte has been fully transmitted over UART. That is because theTCevent 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.
This section describes 4 possible cases, plus one additional case that explains why HT and TC events are both necessary in the application.
Abbreviations used
暂无开放 Issues,或尚未同步最近议题。