Embedded HandbookApps
All sections › III. Peripherals
STM32F103, STM32F407, STM32G474 · level intermediate · 19 min read
Open in the interactive handbook

16а. RS-485: the line, terminators, bias and transmit direction

In section 16.8 the Modbus RTU slave already replies over RS-485: the DE pin on PA1 is raised before the reply and dropped on the TC flag. Section 89.4 added the rule "rate × length ≈ 10⁸", two 120 Ω terminators and a bias of 390 Ω at 3.3 V. Here — the line itself and everything around the DE pin: how to build the bus and where to put the terminators, how to calculate the bias with one and two terminators and with tolerances, why TXE cuts the reply off, how the USART drives DE itself on the G4 and how long the line goes silent between request and reply.

Prerequisites: reference manuals RM0008 (F1: the TXE and TC flags) and RM0440 (G4: DE, DEAT, DEDT, receiver timeout), datasheet DS12288 (the USART2_RTS_DE pin), the CMSIS headers of seven families from the Keil packs, the Zephyr bindings for the STM32 USART and Modbus. The bias, bit fractions and exchange time were computed with a PC model; the code was built with armclang for the F103 and G474, and the F103 code was run on a computer against a model of the USART per RM0008.

Terms used in the documentation

Term Meaning
differential pair (A, B) the two wires A and B
half-duplex only one side transmits at a time
termination resistor a resistor at the end of the line
fail-safe biasing holding a defined level on the idle line
stub a branch from the trunk to a device
unit load the standard measure of receiver loading
driver enable (DE) switching on the RS-485 transmitter
receiver enable (/RE) switching on the receiver, active low
transmission complete (TC) transmission has finished
driver enable assertion / deassertion time (DEAT, DEDT) DE delay before the start bit and after the stop bit
driver enable polarity (DEP) the polarity of DE
receiver timeout (RTOEN, RTOR) silence on RX counted in bits

16а.1 The line and one exchange over it

RS-485 is a pair of wires A and B. The receiver looks only at the difference A − B: interference that is the same on both wires is subtracted. There is one transmitter at a time — the line is half-duplex: while the master talks, everyone else listens, then the other way round. Each device switches the direction itself with the transceiver pins: DE enables the transmitter (active high), /RE the receiver (active low), as the Zephyr binding zephyr,modbus-serial describes them. If DE and /RE are on one pin, a one on it simultaneously enables the transmitter and disables the receiver.

The third wire is the common. The receiver sees the difference A − B only as long as both wires do not move too far from its ground, and the permissible common-mode voltage is in the transceiver datasheet. Devices in different cabinets have different grounds (section 54.1), and without a common wire or isolation the line works until the first potential difference.

One Modbus exchange at 9600 baud looks like this:

One exchange over RS-485 at 9600 baud over time: the master raises DE and sends an 8-byte request in 9167 µs, drops DE on the TC flag after the stop bit of the last byte; if DE is dropped on the TXE of the last byte, 1146 µs are cut off — a CRC byte; then the line is silent for at least 3.5 characters, 4011 µs, and the bias holds a one on it; then the slave raises its DE and replies with 25 bytes in 28 646 µs

The figure shows everything this section is about: who holds the line during the pause (16а.3), when to drop DE (16а.4 and 16а.5) and how long the exchange lasts (16а.6).


16а.2 Topology: bus, stubs, terminators

Devices hang on one trunk in a daisy chain: the cable goes into a device and comes out to the next one. Such a line has two ends, and the terminators go exactly there — one at each physical end (section 89.4). A star of several branches has as many ends as branches, and it cannot be matched with two resistors.

A stub — the piece of wire from the trunk to the transceiver — is itself an unmatched line. By the rule of section 160.1 it is invisible as long as its delay is less than roughly a sixth of the transmitter's edge time. The edge is in the transceiver datasheet: chips with slew-rate limiting have longer edges, and they tolerate longer stubs. Keep the stub short, and put the terminals right at the transceiver.

A terminator in every device is a common assembly mistake: ten 120 Ω resistors in parallel give 12 Ω. A 390 Ω bias at 3.3 V on such a load gives 50 mV — four times below the threshold, and the line picks up noise during pauses again. So the terminator jumper in a device is switched on only at the two end devices.

The load from receiver inputs is counted by the standard in unit loads. How many units (or what fraction of a unit) a transceiver takes and how many devices can hang on the line is written in its datasheet. There are no numbers here: the text of the standard is not on disk, and they differ between chips — take them from the datasheet of yours.


16а.3 Bias: calculating for the threshold with one and two terminators

The bias is a resistor Rb from the supply to A and an identical one from B to ground. At rest current flows through them and the terminators, and between A and B you get

VAB = Vcc × Rt / (2 × Rb + Rt),

where Rt is the terminators in parallel: 120 Ω if there is one, and 60 Ω if there are two. A above B — the receiver outputs a one, that is, the very idle state of the UART. We take the receiver threshold as 200 mV — the limit from section 89.4: from it upwards the receiver must output a one; yours is in its datasheet. The model does not account for the receiver inputs: with them VAB is a little lower still.

The nominal is not enough: resistors and the supply vary. A PC model looks for Rb so that 200 mV is reached even in the worst case — the supply 5 % low, Rb 1 % high, Rt 1 % low — and takes the nearest lower value of the E12 series:

Supply Terminators Rb at most With tolerances E12 series VAB Worst case Current
5 V 1 1440 Ω 1338 Ω 1200 Ω 238 mV 222 mV 1.98 mA
5 V 2 720 Ω 669 Ω 560 Ω 254 mV 237 mV 4.24 mA
3.3 V 1 930 Ω 863 Ω 820 Ω 225 mV 210 mV 1.88 mA
3.3 V 2 465 Ω 432 Ω 390 Ω 236 mV 220 mV 3.93 mA

What follows from the table:

390 Ω sets at 3.3 V VAB Current Transmitter load
1 236 mV 3.93 mA 55.7 Ω
2 440 mV 7.33 mA 52.0 Ω
4 776 mV 12.94 mA 45.9 Ω

The threshold margin grows, but so do the current and the transmitter load: for the signal the supply is the same as ground, and each set hangs on the line as 2 × Rb. The budget of the isolated port from section 54.4 allows 10 mA for the line pull resistors: one set fits there, four no longer do. What load the transmitter can drive is stated in its datasheet.

Many newer transceivers have the threshold shifted so that silence reads as a one even without bias (section 89.4). This is visible in the datasheet from the receiver threshold; then the bias resistors are not needed.


16а.4 DE by hand on the F103: by TC, not by TXE

The USART of the F1 and F407 has no hardware DE (16а.5), and the DE pin is an ordinary output. In section 16.8 it is dropped by polling TC in a loop. For a master that has no time to wait — the same with interrupts. RM0008 (27.3.2) on the flags: TXE is set when a byte has been moved from the data register to the shift register, that is, at the moment it has only started going out on the line; TC when the frame has gone out completely and there is no new data. Hence the PC model: for an 8-byte request at 9600 baud the TXE of the last byte comes at 8021 µs, TC at 9167 µs; DE dropped on TXE cuts off 1146 µs — the last byte, the high byte of the CRC. With a one-byte message it is worse: while the transmitter is free, a write to DR goes straight into the shift register, and TXE stays set — DE falls before the byte has started.

The second trap is reset: USART_SR after reset equals 0x00C0, that is, TC is already set. It is cleared by the pair "read SR — write DR". Enable TCIE before the first byte — and the very first interrupt comes from the old TC. So TCIE is enabled only after the last byte has been written:

void rs485_send(const uint8_t *p, uint32_t n)
{
    if (n == 0)
        return;
    while (tx_busy) {
    }
    tx_p = p;
    tx_n = n;
    tx_busy = 1;
    USART2->CR1 &= ~USART_CR1_RE;               /* своё эхо не принимать */
    GPIOA->BSRR = 1u << 1;                      /* DE = 1: передатчик на линии */
    USART2->CR1 |= USART_CR1_TXEIE;             /* байты кладёт прерывание; TCIE пока нельзя: TC = 1 с самого сброса */
}

void USART2_IRQHandler(void)
{
    uint32_t sr = USART2->SR;
    uint32_t cr1 = USART2->CR1;
    if ((cr1 & USART_CR1_TXEIE) && (sr & USART_SR_TXE)) {
        USART2->DR = *tx_p++;                   /* чтение SR, затем запись DR сбрасывают TC */
        if (--tx_n == 0)                        /* последний байт только начал уходить: ждать TC, а не TXE */
            USART2->CR1 = (uint16_t)((cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE);
    } else if ((cr1 & USART_CR1_TCIE) && (sr & USART_SR_TC)) {
        GPIOA->BSRR = 1u << (1 + 16);           /* стоп-бит последнего байта в линии: DE = 0 */
        USART2->CR1 = (uint16_t)((cr1 & ~USART_CR1_TCIE) | USART_CR1_RE);
        tx_busy = 0;
    }
    if (sr & USART_SR_RXNE)
        rs485_rx_byte((uint8_t)USART2->DR);
}

The USART receiver (RE) is off during transmission: then your own echo does not get into reception, however /RE is connected — together with DE or to ground. RX is pulled up to the supply: if the receiver output releases the line when disabled (on many chips it does, see the datasheet), without the pull-up the pin would float.

The same file was built on a computer together with a model of the USART per RM0008 — time in bits, TXE and TC behave as in the description above:

Variant Bytes Bits on the line Bits with DE = 0
by TC — the code above 8 88 0
by TC — the code above 1 11 0
by the TXE of the last byte 8 88 11
by the TXE of the last byte 1 11 11
TCIE at once, the handler checks TC first 8 88 88

With TC, DE falls exactly at the boundary of the last stop bit. Between raising DE and the start bit there is only the interrupt entry. If the transceiver needs time to switch on (it is in the datasheet), wait it out before the first write to DR — on the G4 the DEAT field does this.


16а.5 Hardware DE: the G474 and which families can do it

In the new USART the DE pin is driven by the block itself. RM0440 (37.5.20): the DEM bit in USART_CR3 outputs the DE signal on the RTS pin; DEP selects the polarity (0 — active high); DEAT in USART_CR1 is the time from raising DE to the start of the start bit, DEDT from the end of the last stop bit to dropping DE. Both fields are 5 bits and count in fractions of a bit: 1/16 with OVER8 = 0 and 1/8 with OVER8 = 1. They can only be written with UE = 0. The Zephyr binding st,stm32-uart calls them de-assert-time and de-deassert-time — "in sixteenths of a bit, from 0 to 31", in HAL the same is done by HAL_RS485Ex_Init.

Which families can do this — from the CMSIS headers in the Keil packs:

Family Header DEM, DEP DEAT, DEDT RTOEN
F0 stm32f072xb.h yes yes yes
F1 stm32f10x.h no no no
F3 stm32f303xc.h yes yes yes
F4 stm32f407xx.h no no no
F7 stm32f767xx.h yes yes yes
G4 stm32g474xx.h yes yes yes
L4 stm32l476xx.h yes yes yes

How much that is in time:

Rate Fraction with OVER8 = 0 Maximum, 31 fractions The same with OVER8 = 1
9600 baud 6.5104 µs 201.82 µs 403.65 µs
115 200 baud 0.5425 µs 16.82 µs 33.64 µs
1 Mbit/s 0.0625 µs 1.94 µs 3.88 µs

31 fractions are 1.9375 bits with OVER8 = 0 and 3.875 bits with OVER8 = 1. DEAT is chosen like this: the transmitter enable time from the datasheet × rate × 16, rounded up. Suppose the transceiver needs 3 µs: at 115 200 baud this is DEAT = 6, and at 1 Mbit/s 48 fractions are needed — the field cannot hold that many, and OVER8 = 1 remains (24 fractions of 1/8).

Code for the G474 — USART2 on the same PA1, PA2 and PA3 as on the F103 in section 16.8; according to datasheet DS12288 PA1 is USART2_RTS_DE on AF7, with TX and RX next to it on the same AF7:

#define DE_TIME   16u       /* DEAT и DEDT: 16 долей по 1/16 бита = 1 бит (OVER8 = 0) */
#define RTO_BITS  39u       /* 3,5 символа по 11 бит = 38,5 бита -> 39 */

#define CR1_RS485 ((DE_TIME << USART_CR1_DEAT_Pos) | (DE_TIME << USART_CR1_DEDT_Pos) | \
                   USART_CR1_M0 | USART_CR1_PCE | USART_CR1_RTOIE | USART_CR1_TE | USART_CR1_RE)
void rs485_g4_init(void)
{
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
    RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;
    GPIOA->AFR[0] = (GPIOA->AFR[0] & ~0xFFF0u) | 0x7770u;   /* PA1, PA2, PA3 — AF7 */
    GPIOA->MODER = (GPIOA->MODER & ~0xFCu) | 0xA8u;         /* все три — альтернативная функция */
    USART2->BRR = 17708u;
    USART2->RTOR = RTO_BITS;
    USART2->CR2 = USART_CR2_RTOEN;
    USART2->CR3 = USART_CR3_DEM;                            /* DEP = 0: DE активен высоким уровнем */
    USART2->CR1 = CR1_RS485;                                /* DEAT, DEDT и формат пишут при UE = 0 */
    USART2->CR1 = CR1_RS485 | USART_CR1_UE;
}

BRR = 170 MHz / 9600 = 17708: a rate of 9600.18 baud, an error of +0.0019 %. DEAT = DEDT = 16 — one bit, 104.2 µs at 9600 baud. Static assertions in the file pin the bit numbers per RM0440: DEM and DEP are bits 14 and 15 of CR3, DEAT 25:21, DEDT 20:16 of CR1. armclang reduces the USART2 setup (address 0x4000 4400) to six writes: BRR = 0x452C, RTOR = 0x27, CR2 = 0x800000, CR3 = 0x4000, CR1 = 0x0610140C and then 0x0610140D — the same CR1 with UE. Transmission after that is simply a write to TDR: DE rises DEAT before the start bit and falls DEDT after the stop bit without the firmware taking part.

The price: DE comes out on the RTS pin, so hardware RTS flow control can no longer be used on this USART.


16а.6 Frame time, line turnaround and 3.5 characters

A Modbus RTU character is 11 bits (section 16.8). The request "read 10 registers" is 8 bytes, the reply to it 25: address, function, byte count, 20 bytes of data and CRC. Between frames the line is silent for at least 3.5 characters, above 19 200 baud a constant 1750 µs (section 198в.3). How long one exchange takes, without the processing time in the slave:

Rate Character Request Pause Reply Exchange Exchanges per second, at most
9600 baud 1145.8 µs 9167 µs 4011 µs 28 646 µs 45 834 µs 21.8
19 200 baud 572.9 µs 4583 µs 2006 µs 14 323 µs 22 918 µs 43.6
115 200 baud 95.5 µs 764 µs 1750 µs 2387 µs 6651 µs 150.4

At 115 200 baud the two 1750 µs pauses are more than half the exchange: raising the rate further is almost useless, and it shortens the line length by the rule of 89.4.

Line turnaround is the moment when the master releases the line and the slave takes it. The slave starts replying no earlier than the end of the 3.5-character silence: that is how it recognises the end of the request. So the master has 4011 µs at 9600 baud to drop DE. DEDT = 16 takes 104.2 µs, interrupt entry on the F103 a few microseconds: the margin is huge. The danger is not Modbus but home-grown protocols where the slave replies at once: then the master's delay in dropping DE must be shorter than the shortest reply delay.

End of frame on the G4 without a timer. In sections 16.8 and 198в the silence is counted by a timer. The new USART has a receiver timeout for this: RTOEN in CR2 and the number of bits of silence in RTOR; RTOF is set if the pause is longer than the value plus 2 fractions of a bit (RM0440). 3.5 characters are 38.5 bits:

RTO RTOF after Needed at least Result
38 38.125 bits 38.5 bits too early: the frame is cut
39 39.125 bits 38.5 bits suitable

Hence RTO_BITS = 39 in the code above. A gap inside a frame of up to 1.5 characters (16.5 bits) is allowed by the protocol — it will not run the counter out. RM0440 in the section on Modbus takes 2 characters, 22 bits, as an example; the protocol needs 3.5. The constant 1750 µs for rates above 19 200 baud is, at 115 200 baud, 202 bits.

Isolation and protection. If devices are powered from different grounds — a digital isolator and an isolated converter (sections 54.4 and 54.6). Everything that goes out to the terminals gets a suppressor at the connector (sections 62.9 and 203.4), and against common-mode interference on the cable a common-mode choke (section 168).


16а.7 Pitfalls

Communication works on the desk and fails in the cabinet. The bias was calculated for one terminator, but on site there are two: 820 Ω at 3.3 V gives 116 mV instead of the required 200. Calculate for two — 390 Ω.

Noise in pauses, false start bits. A terminator is switched on in every device: ten of 120 Ω are 12 Ω, and the bias gives 50 mV. Terminators only at the two ends.

It works, but fails at the slightest thing. 680 Ω at 5 V: nominally 211 mV, but with supply and resistor tolerances 197 mV. Take 560.

The master sees a timeout although the reply went out. DE was dropped on TXE: the last CRC byte is cut off. With a one-byte message nothing at all goes out.

DE falls before it has risen. TCIE was enabled before the first byte, and TC has been set since reset (USART_SR = 0x00C0).

Your own echo in reception. /RE is grounded, and the receiver hears its own transmission. Switch off RE in CR1 during transmission — or discard the echo.

On the G4 DEAT and DEDT do not change. The fields are written only with UE = 0 — first CR1 without UE, then with it.

The receiver timeout cuts the frame. RTO = 38: RTOF comes after 38.125 bits, earlier than 3.5 characters. 39 is needed.

A star instead of a chain. A star has more than two ends — two terminators cannot match it. The trunk is a daisy chain, the stubs are short.


16а.8 Summary

Full sources

rs485_f103.c — RS-485 on the STM32F103: interrupt-driven transmission, DE dropped on the TC flag.

/* 16а: RS-485 на STM32F103 — передача по прерываниям, вывод DE снимается по флагу TC.
   USART2: PA2 — TX, PA3 — RX, PA1 — DE и /RE приёмопередатчика (как в разделе 16.8), 9600 8E1.
   Собирается armclang: -DSTM32F10X_MD -include stm32f10x.h. */
#include <stddef.h>
#include <stdint.h>

#ifdef USART_CR3_DEM
#error "у USART F1 аппаратного DE нет: такого бита в заголовке быть не должно"
#endif
_Static_assert(USART_SR_TXE == 0x80 && USART_SR_TC == 0x40, "TXE и TC — биты 7 и 6 USART_SR (RM0008)");
_Static_assert(USART_CR1_TXEIE == 0x80 && USART_CR1_TCIE == 0x40, "их прерывания — те же биты в CR1");
_Static_assert(36000000u / 9600u == 3750u && 36000000u % 9600u == 0u, "9600 бод от APB1 36 МГц без погрешности");

extern void rs485_rx_byte(uint8_t b);           /* приём кадра — раздел 16.8 */

static const uint8_t *tx_p;
static volatile uint32_t tx_n;
static volatile uint8_t tx_busy;

void rs485_init(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
    GPIOA->BSRR = (1u << (1 + 16)) | (1u << 3); /* DE = 0 — слушаем; подтяжка RX вверх */
    GPIOA->CRL = (GPIOA->CRL & ~0xFFF0u)
               | (0x2u << 4)                    /* PA1: выход 2 МГц — DE и /RE */
               | (0xAu << 8)                    /* PA2: альтернативная функция — TX */
               | (0x8u << 12);                  /* PA3: вход с подтяжкой — RX */
    USART2->BRR = 3750u;
    USART2->CR1 = USART_CR1_UE | USART_CR1_M | USART_CR1_PCE | USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
    NVIC_EnableIRQ(USART2_IRQn);
}

void rs485_send(const uint8_t *p, uint32_t n)
{
    if (n == 0)
        return;
    while (tx_busy) {
    }
    tx_p = p;
    tx_n = n;
    tx_busy = 1;
    USART2->CR1 &= ~USART_CR1_RE;               /* своё эхо не принимать */
    GPIOA->BSRR = 1u << 1;                      /* DE = 1: передатчик на линии */
    USART2->CR1 |= USART_CR1_TXEIE;             /* байты кладёт прерывание; TCIE пока нельзя: TC = 1 с самого сброса */
}

void USART2_IRQHandler(void)
{
    uint32_t sr = USART2->SR;
    uint32_t cr1 = USART2->CR1;
    if ((cr1 & USART_CR1_TXEIE) && (sr & USART_SR_TXE)) {
        USART2->DR = *tx_p++;                   /* чтение SR, затем запись DR сбрасывают TC */
        if (--tx_n == 0)                        /* последний байт только начал уходить: ждать TC, а не TXE */
            USART2->CR1 = (uint16_t)((cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE);
    } else if ((cr1 & USART_CR1_TCIE) && (sr & USART_SR_TC)) {
        GPIOA->BSRR = 1u << (1 + 16);           /* стоп-бит последнего байта в линии: DE = 0 */
        USART2->CR1 = (uint16_t)((cr1 & ~USART_CR1_TCIE) | USART_CR1_RE);
        tx_busy = 0;
    }
    if (sr & USART_SR_RXNE)
        rs485_rx_byte((uint8_t)USART2->DR);
}

rs485_g474.c — RS-485 on the STM32G474: the USART drives DE itself, the receiver timeout catches the end of a frame.

/* 16а: RS-485 на STM32G474 — вывод DE ведёт сам USART2 (DEM), конец кадра Modbus ловит таймаут приёма.
   USART2: PA1 — DE (USART2_RTS_DE), PA2 — TX, PA3 — RX, все AF7; 9600 8E1 от PCLK1 170 МГц.
   Собирается armclang: -DSTM32G474xx -include stm32g4xx.h. */
#include <stdint.h>

#define DE_TIME   16u       /* DEAT и DEDT: 16 долей по 1/16 бита = 1 бит (OVER8 = 0) */
#define RTO_BITS  39u       /* 3,5 символа по 11 бит = 38,5 бита -> 39 */

#define CR1_RS485 ((DE_TIME << USART_CR1_DEAT_Pos) | (DE_TIME << USART_CR1_DEDT_Pos) | \
                   USART_CR1_M0 | USART_CR1_PCE | USART_CR1_RTOIE | USART_CR1_TE | USART_CR1_RE)

_Static_assert(USART_CR3_DEM == (1u << 14) && USART_CR3_DEP == (1u << 15), "DEM и DEP — биты 14 и 15 USART_CR3 (RM0440)");
_Static_assert(USART_CR1_DEAT_Pos == 21 && USART_CR1_DEAT_Msk == 0x03E00000u, "DEAT[4:0] — биты 25:21 CR1");
_Static_assert(USART_CR1_DEDT_Pos == 16 && USART_CR1_DEDT_Msk == 0x001F0000u, "DEDT[4:0] — биты 20:16 CR1");
_Static_assert(DE_TIME <= 31u && RTO_BITS <= USART_RTOR_RTO_Msk, "поля DEAT, DEDT — 5 бит, RTO — 24 бита");
_Static_assert(CR1_RS485 == 0x0610140Cu, "CR1: DEAT = DEDT = 16, 8E1, RTOIE, TE, RE");
_Static_assert((170000000u + 4800u) / 9600u == 17708u, "BRR для 9600 бод от 170 МГц");

void rs485_g4_init(void)
{
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
    RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;
    GPIOA->AFR[0] = (GPIOA->AFR[0] & ~0xFFF0u) | 0x7770u;   /* PA1, PA2, PA3 — AF7 */
    GPIOA->MODER = (GPIOA->MODER & ~0xFCu) | 0xA8u;         /* все три — альтернативная функция */
    USART2->BRR = 17708u;
    USART2->RTOR = RTO_BITS;
    USART2->CR2 = USART_CR2_RTOEN;
    USART2->CR3 = USART_CR3_DEM;                            /* DEP = 0: DE активен высоким уровнем */
    USART2->CR1 = CR1_RS485;                                /* DEAT, DEDT и формат пишут при UE = 0 */
    USART2->CR1 = CR1_RS485 | USART_CR1_UE;
}

/* Передача: DE поднимется за DEAT до старт-бита и упадёт через DEDT после стоп-бита сам */
void rs485_g4_send(const uint8_t *p, uint32_t n)
{
    while (n--) {
        while (!(USART2->ISR & USART_ISR_TXE)) {
        }
        USART2->TDR = *p++;
    }
}

Next: section 16б "Lossless UART reception: DMA into a ring, IDLE and the half buffer" — circular DMA, the position from NDTR, parsing frames without copying and the ring size from the baud rate and latency.

Open in the interactive handbook