使用 HAL_UART_Receive_IT() 和 HAL_UART_RxCpltCallback() 时会丢失字节吗?
Posted
技术标签:
【中文标题】使用 HAL_UART_Receive_IT() 和 HAL_UART_RxCpltCallback() 时会丢失字节吗?【英文标题】:Can bytes be lost when using HAL_UART_Receive_IT() and HAL_UART_RxCpltCallback()? 【发布时间】:2020-04-30 03:21:25 【问题描述】:我有一些(主要是 CubeMX 生成的)代码:
volatile uint8_t buf[4];
int main(void)
...
HAL_UART_Receive_IT(&huart3, buf, sizeof(buf));
while (1)
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
if (huart->Instance == USART3)
HAL_UART_Transmit(&huart3, buf, sizeof(buf), 0xFFFF);
HAL_UART_Receive_IT(&huart3, buf, sizeof(buf));
这成功地回显了 USART3 上接收到的所有内容。 (此代码只是了解串行端口的一次性示例。)
我担心HAL_UART_RxCpltCallback()
被调用和HAL_UART_Receive_IT()
设置下一次接收之间的时间。
STM32F103有什么特性可以保证这个时间间隔内数据不会丢失吗?我没有发现任何证据表明 USART 上有超过两个字节的接收缓冲区。
我特别担心某些更高优先级的 USB 设备活动可能会延迟HAL_UART_RxCpltCallback()
的调用,因此可能会丢失一个或多个字符。
【问题讨论】:
如果在读取 UART 寄存器的当前内容之前在 UART 上接收到更多数据,则将设置溢出错误 (ORE) 位。我不熟悉 UART 的 HAL 接口,但是 LL(Low-Level)接口提供了读取该位的方法。 一般来说...使用LL_USART_xxx
例程提供了比HAL_UART_xxx
对应的更多灵活性。 IIRC 在使用 HAL 例程进行基于中断的 UART 接收时,需要提前知道传入字符的数量(这使得在许多情况下使用 HAL UART 驱动程序是不可行的。)
谢谢两位,我会看看LL_USART
函数。
【参考方案1】:
快速回答:
HAL_UART_Transmit 是一个阻塞函数,所以在它完成之前不会调用 HAL_UART_Receive_IT 并且 uart 肯定会溢出。如果您担心任何其他中断会抢占您的执行,请在发出命令时使用 dma 或禁用麻烦的中断。您也可以提高 UART 中断优先级,但是......
提示:
....您不应该在 HAL_UART_RxCpltCallback 中调用 HAL_UART_Transmit 和 HAL_UART_Receive_IT,因为您处于 ISR 模式。这会导致 HAL 框架的某些版本出现一些附带问题。最好设置一个标志并从主代码中检查它。
你也应该避免使用 volatile,如果你使用像 gcc __asm volatile("" ::: "memory")
这样的编译器屏障会更好
【讨论】:
我打算将volatile
指示编译器,当处理中断时,buf
中的值可能会独立于当前挂起的代码而改变。我是否需要在对buf
的所有访问周围使用障碍?
你可以在主循环解析buf之前放一个。
我刚刚注意到您也在同一个缓冲区上进行传输和接收,这是故意的吗?你想用这个 HAL_UART_Transmit 实现什么?
是的,这是故意的 - 它是一个回显程序,用于测试串行端口。
那么短/坏的方法是一次接收/发送一个字符。如果你想要一个更好的实现,你应该使用接收回调来提供一个循环缓冲区和主循环来继续检查其中的新数据,并在数据可用时编写一个新的传输。在访问头/尾指针之前使用 volatile/barrier 应该可以避免任何不需要的优化。以上是关于使用 HAL_UART_Receive_IT() 和 HAL_UART_RxCpltCallback() 时会丢失字节吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何在收到数据时自动调用UART_Receive_IT()?
如何重置 STM32 HAL UART 驱动程序 (HAL) 状态?