This is an application note and contains list of examples about 2
distinct topics:
STM32 has peripherals such as USART and UART. Difference between them is that USART includes advance feature such as optional synchronous communication, not available in UART. If these features are not used, USART and UART peripherals can be considered identical. For the sake of this application note, we will use term UART only, while exactly same applies to USART peripherals aswell.
UART in STM32 allows customers to configure it using different transmit(TX
)/receive(RX
) modes:
9600
or lower115200
, up to ~921600
bauds> 1Mbps
and low-power applicationsFor RX mode, this article focuses only on DMA mode, to receive unknown number of bytes
Every STM32 has at least one (1
) UART IP and at least one (1
) DMA controller available in its DNA.
For data transmission, no special features (on top of essential one) are necessary, except DMA availability for UART.
Application uses use default features to implement very efficient transmit system using DMA.
While it is straight-forward for TX, this is not the case for receive operation.
When implementing DMA receive, application would need to understand number of received bytes to process by DMA before finishing transfer.
This is especially true when UART is used for system communication where it has to react in short time after all bytes have been received.
STM32s have capability in UART to detect when RX line has not been active for period of time. This is achieved using one of 2
available methods:
1
frame time, after last received byte. Frame time is based on baudrate. Higher baudrate means lower frame time for single byte.Both events can trigger an interrupt which is an essential feature to allow 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) ~100us
; for 3 bytes
it would be ~300us
in total.
IDLE line event triggers an interrupt for application when line has been in idle state for 1
frame time (in this case 100us
) after third byte has been received.
This is a real experiment demo using STM32F4 and IDLE LINE event. After IDLE event is triggered, data are echoed back (loopback mode):
3
bytes, takes approx ~300us
at 115200
bauds1
frame time (approx 100us
)
1
frame timeDMA 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.
0
.
While transfer is active, 2
(among others) interrupts may be triggered:
HT
: Triggers when DMA transfers half count of elementsTC
: Triggers when DMA transfers all elementsWhen 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
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 reaching this step, same for basic DMA setup, the rest:
20
to relevant DMA register for data lengthHT
event (or interrupt) after first 10
have been transferred from UART to memoryTC
event (or interrupt) after 20
bytes are transferred from UART to memoryThis 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)
Everything gets simplier when application transmits data, length of data is known in advance and 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";
strlen(hello_world_arr)
or 10
TC
event (or interrupt) after all bytes have been transmitted from memory to UART via DMAPlease note that
TC
event is triggered before last UART byte has been fully transmitted over UART. That's becauseTC
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
This section describes 4
possible cases and one additional which explains why HT/TC events are necessary by application
Abbrevations used on image:
R
: R
ead pointer, used by application to read data from memory. Later also used as old_ptr
W
: W
rite pointer, used by DMA to write next byte to. Increased every time DMA writes new byte. Later also used as new_ptr
HT
: H
alf-T
ransfer Complete event triggered by DMATC
: T
ransfer-C
omplete event triggered by DMAI
: I
DLE line detection event triggered by USARTDMA configuration:
20
bytes length memory
HT
event triggers at 10
bytesTC
event triggers at 20
bytesPossible cases:
10
bytes. Application gets notification by HT
event and may process received data10
bytes. Application gets notification by TC
event. Processing now starts from last known position until the end of memory
10
bytes, but not aligned with HT
nor TC
events
HT
event when first 6
bytes are transfered. Processing may start from last known read locationIDLE
event after next 4
bytes are successfully transfered10
bytes in overflow mode and but not aligned with HT
nor TC
events
TC
event when first 4
bytes are transfered. Processing may start from last known read locationIDLE
event after next 6
bytes are transfered. Processing may start from beginning of bufferIDLE
event
30
bytes in burst, 10
bytes get overwritten by DMA as application did not process it quickly enoughIDLE
line event once there is steady RX line for 1
byte timeframe10
received bytes from burst which were overwritten by last 10
bytes in burst20
bytes take; or by using TC
and HT
eventsExample code to read data from memory and process it, for cases A-D
/**
* \brief Check for new data received with DMA
* \note This function must be called from DMA HT/TC and USART IDLE events
* \note Full source code is available in examples
*/
void
usart_rx_check(void) {
static size_t old_pos;
size_t pos;
/* Calculate current position in buffer */
/* usart_rx_dma_buffer is an array of uint8_t */
pos = sizeof(usart_rx_dma_buffer) - LL_DMA_GetDataLength(DMA1, LL_DMA_STREAM_1);
if (pos != old_pos) { /* Check change in received data */
if (pos > old_pos) { /* Current position is over previous one */
/* We are in "linear" mode, case P1, P2, P3 */
/* Process data directly by subtracting "pointers" */
usart_process_data(&usart_rx_dma_buffer[old_pos], pos - old_pos);
} else {
/* We are in "overflow" mode, case P4 */
/* First process data to the end of buffer */
usart_process_data(&usart_rx_dma_buffer[old_pos], sizeof(usart_rx_dma_buffer) - old_pos);
/* Check and continue with beginning of buffer */
if (pos > 0) {
usart_process_data(&usart_rx_dma_buffer[0], pos);
}
}
old_pos = pos; /* Save current position as old */
}
}
Examples provide reference code to implement RX and TX functionality using DMA transfers. There are 2 sets of examples:
projects
folder with usart_rx_
prefixprojects
folder with usart_tx_
prefixCommon for all examples:
115200
bauds, 1
stop bit, no-parityTC
and HT
events enabledTC
event enabledSTM32 family | Board name | USART | STM32 TX | STM32 RX | RX DMA settings | TX DMA settings |
---|---|---|---|---|---|---|
STM32F1xx | BluePill-F103C8 |
USART1 |
PA9 |
PA10 |
DMA1 , Channel 5 |
|
STM32F4xx | NUCLEO-F413ZH |
USART3 |
PD8 |
PD9 |
DMA1 , Stream 1 , Channel 4 |
DMA1 , Stream 3 , Channel 4 |
STM32G0xx | NUCLEO-G071RB |
USART2 |
PA2 |
PA3 |
DMA1 , Channel 1 |
|
STM32G4xx | NUCLEO-G474RE |
LPUART1 |
PA2 |
PA3 |
DMA1 , Channel 1 |
|
STM32L4xx | NUCLEO-L432KC |
USART2 |
PA2 |
PA15 |
DMA1 , Channel 6 , Request 2 |
|
STM32H7xx | NUCLEO-H743ZI2* |
USART3 |
PD8 |
PD9 |
DMA1 , Stream 0 |
DMA1 , Stream 1 |
- It is possible to run H743 (single-core) examples on dual-core STM32H7 Nucleo boards, NUCLEO-H745 or NUCLEO-H755. Special care needs to be taken as dual-core H7 Nucleo boards use DCDC for MCU power hence application must check clock configuration in main file and uncomment code to enable SMPS.
Examples demonstrate different use cases for RX only or RX&TX combined.
3
interruptsProcessing of incoming data is from 2 interrupt vectors, hence it is important that they do not preempt each-other. Set both to the same preemption priority!
This is the most preferred way to use and process UART received character
This is a demo application available in projects
folder.
Its purpose is to show how can application implement output of debug messages without drastically affect CPU performance.
It is using DMA to transfer data (no CPU to wait for UART flags) and can achieve very high or very low data rates
As a result of this demo application for STM32F413-Nucleo board, observations are as following:
1581
bytes every second at 115200
bauds, which is approx 142ms
.14%
, in-line with time to transmit the data0%
USE_DMA_TX
macro configuration in main.c
git clone --recurse-submodules https://github.com/MaJerle/stm32-usart-dma-rx-tx
to clone repository including submodulesprojects
directory using STM32CubeIDE IDE此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。