📘 STM32 串口中断接收方式笔记:HAL_UART_Receive_IT
vs __HAL_UART_ENABLE_IT
🧠 1. 两者作用简述
函数/宏 | 作用 | 是否配置HAL状态 | 是否调用Rx回调 |
---|---|---|---|
HAL_UART_Receive_IT() |
启动一次基于中断的串口接收任务 | ✅ 是 | ✅ 是 |
__HAL_UART_ENABLE_IT() |
手动使能某个串口中断(如RXNE) | ❌ 否 | ❌ 否(除非配合HAL函数) |
🔧 2. HAL_UART_Receive_IT()
的工作机制
c
HAL_UART_Receive_IT(&huart1, rx_buf, 10);
-
作用:启动一个"异步接收任务",要求 HAL 接收 10 个字节 存入
rx_buf
中。 -
内部做了以下几件事:
- 设置接收缓冲区指针
huart->pRxBuffPtr
- 设置接收长度
huart->RxXferSize
- 状态切换为
BUSY_RX
- 使能
RXNE
中断
- 设置接收缓冲区指针
-
当 HAL 中断服务函数
HAL_UART_IRQHandler()
检测到接收到的数据后,会自动读取数据放入缓冲区,直到接收满为止。 -
满足接收条件后,会自动调用:
cvoid HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
注意:接收完成后,不会自动重新启动接收 ,需要你手动再次调用
HAL_UART_Receive_IT()
。
⚠️ 3. 使用 __HAL_UART_ENABLE_IT()
的注意事项
c
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
- 只是打开了
USART1
的接收中断RXNE
。 - 并不会设置 HAL 的接收状态、缓冲区等内部变量。
- 如果你还调用了
HAL_UART_IRQHandler()
,会因 HAL 状态未准备好,导致:- 中断进来了
RXNE
置位- HAL 不消费数据
RXNE
不会被清除- 中断一直触发 → 卡死!
✅ 正确用法对比
✅ 【方式1】HAL自动管理方式(推荐新手/结构清晰)
c
// 初始化接收任务
HAL_UART_Receive_IT(&huart1, rx_buf, 1);
// 实现接收完成回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) {
// 处理数据 rx_buf[0]
HAL_UART_Receive_IT(&huart1, rx_buf, 1); // 继续接收
}
}
✅ 【方式2】裸中断方式(适合自定义接收流程)
c
// 手动使能接收中断
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
void USART1_IRQHandler(void)
{
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)) {
uint8_t ch = (uint8_t)(huart1.Instance->RDR);
// 自定义缓冲区存储、协议解析等
}
}
🔄 4. 总结对比表
项目 | HAL_UART_Receive_IT() |
__HAL_UART_ENABLE_IT() + 手动处理 |
---|---|---|
接收管理 | HAL帮你管理接收缓冲 | 你自己处理 |
接收长度 | 固定长度 | 可灵活按字节 |
回调机制 | 自动调用 RxCpltCallback |
你自己写中断函数 |
中断使能 | 内部自动配置 | 你自己配置 |
出错风险 | 小 | 大(忘记清标志位或缓冲区会卡死) |
场景推荐 | 简单数据帧、串口透传 | 协议解析、环形缓冲、实时性要求高 |
🧪 5. 常见问题
问题 | 原因 |
---|---|
程序卡在中断里出不来 | 只使能了 RXNE 中断,没有初始化接收任务,RXNE 一直为 1 |
接收不到数据 | 没有调用 HAL_UART_Receive_IT() ,或中断未使能 |
回调函数不触发 | 没有使用 HAL 接收函数,或 HAL 状态机异常 |
中断乱跳、多次进中断 | 收数据没及时读 RDR ,或 RXNE 没清除 |