Почему 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 в циклическом режиме.
Источники
- 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() — приём до паузы на линии
- 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
Нашли ошибку?
Смотрите также
- Кольцевой буфер UARTБуфер SPSC с маской, ISR приёма и передачи на AVR и STM32, ORE и DOR, размер
- UART STM32BRR и OVER8, допуск по частоте, флаги, прерывания, DMA с IDLE, полудуплекс, RS-485
- Ошибка переполнения приёмникаНовый символ при непрочитанном буфере: ORE, DOR0, OE
- HAL, LL или регистрыУровни STM32Cube, что делает HAL, правила совмещения, один код тремя способами