Embedded HandbookПриложения
Все разделы › III. Периферия
STM32F103, STM32F407, STM32G474 · уровень средний · 19 мин чтения
Открыть в интерактивном справочнике

16а. RS-485: линия, терминаторы, смещение и направление передачи

В разделе 16.8 ведомый Modbus RTU уже отвечает по RS-485: вывод DE на PA1 поднимается перед ответом и снимается по флагу TC. Раздел 89.4 добавил правило «скорость × длина ≈ 10⁸», два терминатора по 120 Ом и смещение 390 Ом при 3,3 В. Здесь — сама линия и всё, что вокруг вывода DE: как строить шину и где ставить терминаторы, как считать смещение при одном и двух терминаторах и с допусками, почему TXE обрезает ответ, как DE ведёт сам USART у G4 и на сколько линия замолкает между запросом и ответом.

Опоры: справочные руководства RM0008 (F1: флаги TXE и TC) и RM0440 (G4: DE, DEAT, DEDT, таймаут приёма), даташит DS12288 (вывод USART2_RTS_DE), заголовки CMSIS семи семейств из паков Keil, привязки Zephyr для USART STM32 и Modbus. Смещение, доли бита и время обмена посчитаны моделью на ПК; код собран armclang для F103 и G474, а код F103 прогнан на компьютере против модели USART по RM0008.

Как это называется в документации

Оригинал Перевод
differential pair (A, B) дифференциальная пара: провода A и B
half-duplex полудуплекс
termination resistor терминатор: резистор на конце линии
fail-safe biasing смещение линии в покое
stub отвод от магистрали к прибору
unit load единица нагрузки
driver enable (DE) включение передатчика RS-485
receiver enable (/RE) включение приёмника, активный низкий
transmission complete (TC) передача завершена
driver enable assertion / deassertion time (DEAT, DEDT) задержка DE до старт-бита и после стоп-бита
driver enable polarity (DEP) полярность DE
receiver timeout (RTOEN, RTOR) таймаут приёма: тишина на RX в битах

16а.1 Линия и один обмен по ней

RS-485 — это пара проводов A и B. Приёмник смотрит только на разность A − B: наводка, одинаковая на обоих проводах, вычитается. Передатчик в один момент один — линия полудуплексная: пока ведущий говорит, все остальные слушают, потом наоборот. Направление каждый прибор переключает сам выводами приёмопередатчика: DE включает передатчик (активный высокий), /RE — приёмник (активный низкий), так их описывает привязка Zephyr zephyr,modbus-serial. Если DE и /RE сидят на одном выводе, единица на нём одновременно включает передатчик и выключает приёмник.

Третий провод — общий. Разность A − B приёмник видит, только пока оба провода не уходят слишком далеко от его земли, а допустимое синфазное напряжение — в даташите приёмопередатчика. У приборов в разных шкафах земли разные (раздел 54.1), и без общего провода или изоляции линия работает до первой разницы потенциалов.

Один обмен Modbus на 9600 бод выглядит так:

Один обмен по RS-485 на 9600 бод во времени: ведущий поднимает DE и передаёт запрос 8 байт за 9167 мкс, снимает DE по флагу TC после стоп-бита последнего байта; если снять DE по TXE последнего байта, обрезается 1146 мкс — байт CRC; дальше линия молчит не меньше 3,5 символа, 4011 мкс, и единицу в ней держит смещение; затем ведомый поднимает свой DE и отвечает 25 байтами за 28 646 мкс

Из рисунка видно всё, о чём раздел: кто держит линию в паузе (16а.3), когда снимать DE (16а.4 и 16а.5) и сколько длится обмен (16а.6).


16а.2 Топология: шина, отводы, терминаторы

Приборы вешают на одну магистраль цепочкой: кабель заходит в прибор и выходит к следующему. У такой линии два конца, и терминаторы стоят ровно там — по одному на каждом физическом конце (раздел 89.4). Звезда из нескольких лучей даёт столько концов, сколько лучей, и согласовать её двумя резисторами нельзя.

Отвод — кусок провода от магистрали до приёмопередатчика — сам по себе несогласованная линия. По правилу раздела 160.1 он незаметен, пока его задержка меньше примерно шестой части фронта передатчика. Фронт — в даташите приёмопередатчика: у микросхем с ограниченной скоростью нарастания он длиннее, и отводы они терпят длиннее. Отвод держат коротким, а клеммы ставят прямо у приёмопередатчика.

Терминатор в каждом приборе — частая ошибка сборки: десять резисторов по 120 Ом параллельно дают 12 Ом. Смещение 390 Ом при 3,3 В на такой нагрузке даёт 50 мВ — вчетверо ниже порога, и линия в паузе снова ловит шум. Поэтому перемычку терминатора в приборе включают только на двух крайних.

Нагрузку от входов приёмников стандарт считает в единицах нагрузки. Сколько единиц (или какую долю единицы) занимает приёмопередатчик и сколько приборов можно повесить на линию, пишут в его даташите. Чисел здесь нет: текста стандарта на диске нет, а у разных микросхем они разные — берите их из даташита своей.


16а.3 Смещение: расчёт под порог при одном и двух терминаторах

Смещение — резистор Rb от питания к A и такой же от B к земле. В покое через них и терминаторы течёт ток, и между A и B получается

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

где Rt — терминаторы параллельно: 120 Ом, если он один, и 60 Ом, если их два. A выше B — приёмник выдаёт единицу, то есть тот самый покой UART. Порог приёмника берём 200 мВ — это граница из раздела 89.4: от неё и выше приёмник обязан выдать единицу; свой порог — в даташите. Входы приёмников модель не учитывает: с ними VAB ещё немного меньше.

Номинал мало: резисторы и питание гуляют. Модель на ПК ищет Rb так, чтобы 200 мВ набиралось и в худшем случае — питание на 5 % ниже, Rb на 1 % больше, Rt на 1 % меньше, — и берёт ближайший меньший номинал ряда E12:

Питание Терминаторов Rb не больше С допусками Ряд E12 VAB Худший случай Ток
5 В 1 1440 Ом 1338 Ом 1200 Ом 238 мВ 222 мВ 1,98 мА
5 В 2 720 Ом 669 Ом 560 Ом 254 мВ 237 мВ 4,24 мА
3,3 В 1 930 Ом 863 Ом 820 Ом 225 мВ 210 мВ 1,88 мА
3,3 В 2 465 Ом 432 Ом 390 Ом 236 мВ 220 мВ 3,93 мА

Что из таблицы следует:

Комплектов 390 Ом при 3,3 В VAB Ток Нагрузка передатчика
1 236 мВ 3,93 мА 55,7 Ом
2 440 мВ 7,33 мА 52,0 Ом
4 776 мВ 12,94 мА 45,9 Ом

Запас по порогу растёт, но растёт и ток, и нагрузка передатчика: по сигналу питание — та же земля, и каждый комплект висит на линии как 2 × Rb. В бюджете изолированного порта из раздела 54.4 на подтяжки линии заложено 10 мА: один комплект туда помещается, четыре — уже нет. Какую нагрузку выдерживает передатчик, пишет его даташит.

У многих новых приёмопередатчиков порог сдвинут так, что тишина читается единицей и без смещения (раздел 89.4). Это видно в даташите по порогу приёмника; тогда резисторы смещения не нужны.


16а.4 DE вручную на F103: по TC, а не по TXE

У USART F1 и F407 аппаратного DE нет (16а.5), и вывод DE — обычный выход. В разделе 16.8 его снимают опросом TC в цикле. Для ведущего, которому некогда ждать, — то же на прерываниях. RM0008 (27.3.2) о флагах: TXE взводится, когда байт переложен из регистра данных в сдвиговый, то есть в момент, когда он только начал уходить в линию; TC — когда кадр ушёл целиком и новых данных нет. Отсюда модель на ПК: у запроса из 8 байт на 9600 бод TXE последнего байта приходит на 8021 мкс, TC — на 9167 мкс; DE, снятый по TXE, обрезает 1146 мкс — последний байт, старший байт CRC. У посылки из одного байта хуже: пока передатчик свободен, запись в DR сразу уходит в сдвиговый регистр, и TXE остаётся взведённым — DE падает раньше, чем байт начался.

Вторая ловушка — сброс: USART_SR после сброса равен 0x00C0, то есть TC уже взведён. Сбрасывает его пара «чтение SR — запись DR». Разрешите TCIE до первого байта — и первое же прерывание придёт по старому TC. Поэтому TCIE включают только после записи последнего байта:

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);
}

Приёмник USART (RE) на время передачи выключен: так своё эхо не попадёт в приём, как бы ни был подключён /RE — вместе с DE или на землю. RX подтянут к питанию: если выход приёмника при выключении отпускает линию (у многих микросхем так, смотрите даташит), без подтяжки вывод плавал бы.

Этот же файл собран на компьютере вместе с моделью USART по RM0008 — время в битах, TXE и TC ведут себя как в описании выше:

Вариант Байт Бит в линию Бит при DE = 0
по TC — код выше 8 88 0
по TC — код выше 1 11 0
по TXE последнего байта 8 88 11
по TXE последнего байта 1 11 11
TCIE сразу, обработчик смотрит TC первым 8 88 88

По TC DE падает ровно на границе последнего стоп-бита. Между подъёмом DE и старт-битом — только вход в прерывание. Если приёмопередатчику нужно время на включение (оно в даташите), выдержите его до первой записи в DR — на G4 это делает поле DEAT.


16а.5 Аппаратный DE: G474 и какие семейства это умеют

У нового USART вывод DE ведёт сам блок. RM0440 (37.5.20): бит DEM в USART_CR3 выводит сигнал DE на ножку RTS; DEP выбирает полярность (0 — активный высокий); DEAT в USART_CR1 — время от подъёма DE до начала старт-бита, DEDT — от конца последнего стоп-бита до снятия DE. Оба поля по 5 бит и считаются в долях бита: 1/16 при OVER8 = 0 и 1/8 при OVER8 = 1. Писать их можно только при UE = 0. Привязка Zephyr st,stm32-uart называет их de-assert-time и de-deassert-time — «в шестнадцатых долях бита, от 0 до 31», в HAL то же делает HAL_RS485Ex_Init.

Какие семейства это умеют — по заголовкам CMSIS из паков Keil:

Семейство Заголовок DEM, DEP DEAT, DEDT RTOEN
F0 stm32f072xb.h есть есть есть
F1 stm32f10x.h нет нет нет
F3 stm32f303xc.h есть есть есть
F4 stm32f407xx.h нет нет нет
F7 stm32f767xx.h есть есть есть
G4 stm32g474xx.h есть есть есть
L4 stm32l476xx.h есть есть есть

Сколько это во времени:

Скорость Доля при OVER8 = 0 Наибольшее, 31 доля То же при OVER8 = 1
9600 бод 6,5104 мкс 201,82 мкс 403,65 мкс
115 200 бод 0,5425 мкс 16,82 мкс 33,64 мкс
1 Мбит/с 0,0625 мкс 1,94 мкс 3,88 мкс

31 доля — это 1,9375 бита при OVER8 = 0 и 3,875 бита при OVER8 = 1. DEAT берут так: время включения передатчика из даташита × скорость × 16, округлить вверх. Допустим, приёмопередатчику нужно 3 мкс: на 115 200 бод это DEAT = 6, а на 1 Мбит/с нужно 48 долей — поле столько не вмещает, и остаётся OVER8 = 1 (24 доли по 1/8).

Код для G474 — USART2 на тех же PA1, PA2 и PA3, что у F103 в разделе 16.8; по даташиту DS12288 PA1 — это USART2_RTS_DE на AF7, рядом TX и RX на том же 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 МГц / 9600 = 17708: скорость 9600,18 бод, ошибка +0,0019 %. DEAT = DEDT = 16 — один бит, на 9600 бод 104,2 мкс. Статические утверждения в файле держат номера битов по RM0440: DEM и DEP — биты 14 и 15 CR3, DEAT — 25:21, DEDT — 20:16 CR1. armclang сводит настройку USART2 (адрес 0x4000 4400) к шести записям: BRR = 0x452C, RTOR = 0x27, CR2 = 0x800000, CR3 = 0x4000, CR1 = 0x0610140C и затем 0x0610140D — тот же CR1 с UE. Передача после этого — просто запись в TDR: DE поднимется за DEAT до старт-бита и упадёт через DEDT после стоп-бита без участия прошивки.

Цена: DE выходит на ножку RTS, так что аппаратное управление потоком RTS на этом USART уже не занять.


16а.6 Время кадра, оборот линии и 3,5 символа

Символ Modbus RTU — 11 бит (раздел 16.8). Запрос «прочитать 10 регистров» — 8 байт, ответ на него — 25: адрес, функция, счётчик, 20 байт данных и CRC. Между кадрами линия молчит не меньше 3,5 символа, выше 19 200 бод — постоянные 1750 мкс (раздел 198в.3). Сколько занимает один обмен без времени на обработку в ведомом:

Скорость Символ Запрос Пауза Ответ Обмен Обменов в секунду, не больше
9600 бод 1145,8 мкс 9167 мкс 4011 мкс 28 646 мкс 45 834 мкс 21,8
19 200 бод 572,9 мкс 4583 мкс 2006 мкс 14 323 мкс 22 918 мкс 43,6
115 200 бод 95,5 мкс 764 мкс 1750 мкс 2387 мкс 6651 мкс 150,4

На 115 200 бод две паузы по 1750 мкс — больше половины обмена: поднимать скорость дальше почти бесполезно, а длину линии она сокращает по правилу 89.4.

Оборот линии — момент, когда ведущий отпускает её, а ведомый захватывает. Ведомый начинает отвечать не раньше, чем кончится тишина 3,5 символа: так он узнаёт конец запроса. Значит, у ведущего на снятие DE — 4011 мкс на 9600 бод. DEDT = 16 занимает 104,2 мкс, вход в прерывание на F103 — единицы микросекунд: запас огромный. Опасен не Modbus, а свои протоколы, где ведомый отвечает сразу: тогда задержка снятия DE у ведущего должна быть меньше самой короткой задержки ответа.

Конец кадра на G4 без таймера. В разделах 16.8 и 198в тишину считает таймер. У нового USART для этого есть таймаут приёма: RTOEN в CR2 и число бит тишины в RTOR; RTOF взводится, если пауза длиннее значения плюс 2 доли бита (RM0440). 3,5 символа — это 38,5 бита:

RTO RTOF после Нужно не меньше Итог
38 38,125 бита 38,5 бита рано: кадр режется
39 39,125 бита 38,5 бита годится

Отсюда RTO_BITS = 39 в коде выше. Разрыв внутри кадра до 1,5 символа (16,5 бита) протокол разрешает — он счётчик не доведёт. RM0440 в разделе про Modbus берёт для примера 2 символа, 22 бита; протоколу нужно 3,5. Постоянные 1750 мкс для скоростей выше 19 200 бод на 115 200 бод — это 202 бита.

Изоляция и защита. Если приборы питаются от разных земель — цифровой изолятор и изолированный преобразователь (разделы 54.4 и 54.6). Всё, что выходит на клеммы, получает ограничитель у разъёма (разделы 62.9 и 203.4), от синфазной помехи на кабеле — синфазный дроссель (раздел 168).


16а.7 Грабли

Связь есть на столе и пропадает в шкафу. Смещение считали под один терминатор, а на объекте их два: 820 Ом при 3,3 В дают 116 мВ вместо нужных 200. Считать под два — 390 Ом.

Шум в паузах, ложные старт-биты. Терминатор включён в каждом приборе: десять по 120 Ом — это 12 Ом, и смещение даёт 50 мВ. Терминаторы — только на двух концах.

Работает, но чуть что — сбои. 680 Ом при 5 В: номинал 211 мВ, но с допусками питания и резисторов 197 мВ. Брать 560.

Ведущий видит тайм-аут, хотя ответ ушёл. DE сняли по TXE: последний байт CRC обрезан. У посылки из одного байта не уходит вообще ничего.

DE падает, не успев подняться. TCIE разрешили до первого байта, а TC взведён ещё со сброса (USART_SR = 0x00C0).

Своё эхо в приёме. /RE на земле, приёмник слушает собственную передачу. Выключать RE в CR1 на время передачи — или выбрасывать эхо.

На G4 DEAT и DEDT не меняются. Поля пишут только при UE = 0 — сначала CR1 без UE, потом с ним.

Таймаут приёма режет кадр. RTO = 38: RTOF приходит после 38,125 бита, раньше 3,5 символа. Нужно 39.

Звезда вместо цепочки. У звезды больше двух концов — два терминатора её не согласуют. Магистраль — цепочкой, отводы короткие.


16а.8 Итоги

Полные исходники

rs485_f103.c — RS-485 на STM32F103: передача по прерываниям, DE снимается по флагу TC.

/* 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 на STM32G474: DE ведёт сам USART, конец кадра ловит таймаут приёма.

/* 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++;
    }
}

Дальше: раздел 16б «Приём UART без потерь: DMA в кольцо, IDLE и половина буфера» — кольцевой DMA, позиция по NDTR, разбор кадров без копирования и размер кольца от скорости и задержки.

Открыть в интерактивном справочнике