Перейти к содержанию

Почему HAL_UART_Receive_IT принимает данные только один раз?

Обновлено Редакция GAW

HAL_UART_Receive_IT(&huart1, buf, Size) в драйвере HAL — не «включить приём по прерываниям навсегда», а «принять ровно Size байт в фоне». Это видно в исходнике stm32f1xx_hal_uart.c: после последнего байта драйвер сам выключает прерывание RXNE и сообщает о завершении через обработчик.

Где обрывается приём

СобытиеЧто делает HALЧто нужно от программы
принят последний из Size байтвыключает RXNE, PE, ERR; RxState = READY; HAL_UART_RxCpltCallbackснова вызвать HAL_UART_Receive_IT
переполнение OREзавершает приём (UART_EndRxTransfer); HAL_UART_ErrorCallbackперезапустить приём в ErrorCallback
повторный вызов, пока приём идётвозвращает HAL_BUSY, ничего не меняетпроверять код возврата
Size = 0 или pData = NULLвозвращает HAL_ERROR—

Переполнение возникает, когда новый символ пришёл, а предыдущий ещё не прочитан из DR (RM0008, 27.6.1) — например, пока ядро стоит на точке останова. Подробности — в статье об ошибке переполнения.

Как сделать непрерывный приём

static uint8_t rx_byte;

void start_rx(void) { HAL_UART_Receive_IT(&huart1, &rx_byte, 1); }

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart == &huart1) {
        ring_put(rx_byte);      /* store, parse later in the main loop */
        start_rx();             /* re-arm reception */
    }
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
    if (huart == &huart1) start_rx();
}

Приём по одному байту прост, но на каждый байт выполняется обработчик HAL. Для высоких скоростей разумнее свой обработчик на регистрах с кольцевым буфером — его разбирает статья «Кольцевой буфер для UART», а разницу между HAL, LL и регистрами — сравнение HAL, LL и регистров CMSIS. Настройка самого USART описана на странице UART на STM32.

Похожие вопросы

Можно ли вызвать HAL_UART_Receive_IT прямо в обработчике RxCpltCallback?

Да, это обычный способ: к моменту вызова обработчика драйвер уже вернул состояние приёма в READY, и новый вызов не получит HAL_BUSY. Обработчик выполняется в прерывании, поэтому в нём только кладут байт в буфер и перезапускают приём, а разбирают данные в основном цикле.

Почему приём ломается, когда я ставлю точку останова?

Пока ядро стоит, байты продолжают приходить, регистр данных не читается, и взводится флаг ORE. HAL считает переполнение блокирующей ошибкой и завершает приём. Перезапуск в HAL_UART_ErrorCallback возвращает работу после отладки.

Как принимать строки неизвестной длины?

Либо по одному байту с перезапуском и сборкой строки в кольцевом буфере, либо функцией HAL_UARTEx_ReceiveToIdle_IT, которая завершает приём при паузе на линии и сообщает, сколько байт пришло. Для больших потоков удобнее DMA в циклическом режиме.

Источники

  1. STMicroelectronics. STM32F1xx HAL Driver v1.1.10: Src/stm32f1xx_hal_uart.c — HAL_UART_Receive_IT() — если RxState не READY, возвращает HAL_BUSY, при Size = 0 — HAL_ERROR; UART_Receive_IT() — после последнего байта (--RxXferCount == 0) выключает прерывания RXNE, PE и ERR, возвращает RxState = READY и вызывает HAL_UART_RxCpltCallback(); HAL_UART_IRQHandler() — ошибка ORE при включённом RXNEIE считается блокирующей: UART_EndRxTransfer() и HAL_UART_ErrorCallback(); HAL_UARTEx_ReceiveToIdle_IT() — приём до паузы на линии
  2. STMicroelectronics. RM0008 Reference Manual STM32F101/102/103/105/107, Rev 21, 2021 — 27.6.1 USART_SR — RXNE сбрасывается чтением DR; ORE взводится, когда новый символ готов, а RXNE ещё 1, при RXNEIE = 1 вызывает прерывание, сбрасывается чтением SR и затем DR

Нашли ошибку?