1. Тема дня

UART/USART, RX через interrupt/DMA/ring buffer, RS-485 DE/RE, half-duplex, Modbus RTU timing. Главная мысль: UART - поток байтов; RS-485 добавляет управление направлением передачи.

2. Зачем это нужно в проектах

CLI, HIL через Raspberry Pi, EC25-like parser, RS-485/Modbus, промышленные модули, DMA/IDLE, watchdog-friendly архитектура.

3. Теория

Разделяй уровни:

text
UART/USART - периферия MCU.
RS-485 - физический дифференциальный интерфейс.
Modbus RTU - протокол поверх UART/RS-485.

Polling-приём плох для production:

c
if (HAL_UART_Receive(&huart1, &b, 1, 10) == HAL_OK) {
    parser_feed_byte(b);
}

Лучше:

text
CLI/низкая скорость: interrupt per byte -> ring buffer
поток/Modbus: DMA circular RX + IDLE/timer -> parser

Interrupt per byte:

c
static uint8_t s_rx_byte;
void uart_rx_start(void)
{
    HAL_UART_Receive_IT(&huart1, &s_rx_byte, 1);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1) {
        ringbuf_put_from_isr(&uart1_rx_rb, s_rx_byte);
        HAL_UART_Receive_IT(&huart1, &s_rx_byte, 1);
    }
}

RS-485 transceiver:

text
DI  - MCU TX
RO  - MCU RX
DE  - driver enable
/RE - receiver enable
A/B - differential bus

DE нельзя выключать до последнего stop bit и нельзя держать слишком долго. Если MCU поддерживает аппаратный DE USART - лучше использовать его. Modbus RTU использует паузы:

text
t1.5 - пауза внутри кадра, кадр неполный
t3.5 - конец кадра

Для baud > 19200 часто используют 750 us и 1.750 ms. Архитектура:

text
rs485_uart_driver -> rs485_link_service -> modbus_rtu_parser -> application

4. Типичные ошибки

  1. Выключать DE сразу после записи байтов.
  2. Оставлять DE включённым слишком долго.
  3. Не обрабатывать ORE/FE/NE/PE.
  4. Делать Modbus RTU без таймеров.
  5. Давать application напрямую вызывать HAL_UART на bus UART.
  6. Игнорировать termination/bias/ESD/topology.

5. Практическое задание

Создай STM32_UART_RS485_POLICY.md.

c
typedef enum {
    RS485_STATE_IDLE_RX = 0,
    RS485_STATE_TX_ASSERT_DE,
    RS485_STATE_TX_ACTIVE,
    RS485_STATE_TX_WAIT_TC,
    RS485_STATE_TURNAROUND,
    RS485_STATE_WAIT_REPLY,
    RS485_STATE_ERROR,
} rs485_state_t;
typedef struct {
    uint32_t tx_frames;
    uint32_t rx_frames;
    uint32_t crc_errors;
    uint32_t frame_timeouts;
    uint32_t uart_ore;
    uint32_t uart_fe;
    uint32_t uart_ne;
    uint32_t uart_pe;
    uint32_t de_late_disable;
    uint32_t rx_overflows;
    uint32_t modbus_t15_violations;
    uint32_t modbus_t35_detected;
} rs485_diag_t;

6. Что почитать или попробовать дальше

STM32 HAL UART/USART, ST AN4113 RS-485 hardware DE, Modbus over Serial Line, DMA circular RX + IDLE examples.

DMA завершил запись последнего байта в UART data register. Когда можно выключить DE?

Задание

При 9600 baud и формате 8E1 сколько длится символ? Вычислите 1.5 и 3.5 character times. Почему событие UART IDLE само по себе не доказывает конец Modbus RTU frame?

Критерии самопроверки: Покажите 11 bits, все три интервала и различие IDLE/RTU framing.

Показать ответ автора

8E1 содержит 1 start + 8 data + 1 parity + 1 stop = 11 bits. Tchar=11/9600≈1.146 ms; t1.5≈1.719 ms; t3.5≈4.010 ms. IDLE может обнаруживать более короткую паузу; для RTU нужен отдельный контроль требуемого silence interval.