目录
- 一、前言
- [二、CubeMX 串口中断相关配置](#二、CubeMX 串口中断相关配置)
- 三、串口中断核心函数说明
- 四、中断收发回调与等待函数开发
- [五、FreeRTOS 收发任务函数修改](#五、FreeRTOS 收发任务函数修改)
- 六、运行现象与功能补充说明
- 七、总结
- 八、结尾
一、前言
本篇笔记将承接上一篇的内容,对上一篇中查询方式 实现的串口 2 与串口 4 数据收发功能做优化升级,改用中断方式完成两路串口的 RS485 数据收发逻辑。相较于查询方式,中断方式能极大减少 CPU 的资源占用,无需循环轮询串口状态,只有在实际有收发数据时才触发相应逻辑,是嵌入式串口开发中更优的实现方案,本次功能依旧全程基于 FreeRTOS 多任务完成,保持串口发送、串口接收、LCD 显示的独立并行运行。
二、CubeMX 串口中断相关配置
在原有串口基础配置的前提下,实现中断方式的串口通信,核心只需完成一个关键操作:在 CubeMX 中对对应的串口开启 NVIC 中断使能,配置截图如下:


三、串口中断核心函数说明
使用中断方式实现串口的收发功能,需要用到 HAL 库为串口中断提供的专用函数与回调函数,不同功能对应不同的函数组合,各类函数的作用整理如下,也是本次开发的核心函数:
| 作用 | 中断方式对应的函数组合 |
|---|---|
| 数据发送 | HAL_UART_Transmit_IT 、HAL_UART_TxCpltCallback |
| 数据接收 | HAL_UART_Receive_IT 、HAL_UART_RxCpltCallback |
| 异常处理 | HAL_UART_ErrorCallback |
中断方式的核心工作逻辑:调用串口的中断收发函数,仅完成「中断使能」的操作,并不会直接完成数据收发;当数据收发完成后,系统会自动调用对应的中断回调函数,我们可以在回调函数内做收发完成的状态标记;同时需要手动实现对应的等待函数,用于判断收发状态、处理超时逻辑。对于接收功能,为保证功能完整性,还可在中断接收异常时关闭对应中断,规避冗余的中断触发。
四、中断收发回调与等待函数开发
基于上述的中断工作逻辑,我们需要手动实现串口 2 发送的回调函数、发送完成等待函数,以及串口 4 接收的回调函数、接收完成等待函数,通过标志位的置位与清零,完成中断收发的状态判断,同时做超时异常处理,完整代码如下:
c
// 串口2发送完成标志位,0=未完成,1=完成
uint8_t g_uart2_tx_cplt = 0;
// 串口发送完成中断回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &huart2) // 判断为串口2的发送中断
{
g_uart2_tx_cplt = 1; // 置位发送完成标志
}
}
// 串口2发送完成等待函数,带超时控制
int wait_uart2_tx_cplt(int timeout)
{
//循环等待发送完成或超时
while(g_uart2_tx_cplt == 0 && timeout)
{
vTaskDelay(1);
timeout--;
}
if(timeout == 0) // 超时未完成,返回错误码
return -1;
else // 发送完成,清零标志位并返回成功
{
g_uart2_tx_cplt = 0;
return 0;
}
}
// 串口4接收完成标志位,0=未完成,1=完成
uint8_t g_uart4_rx_cplt = 0;
// 串口接收完成中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &huart4) // 判断为串口4的接收中断
{
g_uart4_rx_cplt = 1; // 置位接收完成标志
}
}
// 串口4接收完成等待函数,带超时控制
int wait_uart4_rx_cplt(int timeout)
{
//循环等待接收完成或超时
while(g_uart4_rx_cplt == 0 && timeout)
{
vTaskDelay(1);
timeout--;
}
if(timeout == 0) // 超时未完成,返回错误码
return -1;
else // 接收完成,清零标志位并返回成功
{
g_uart4_rx_cplt = 0;
return 0;
}
}
五、FreeRTOS 收发任务函数修改
完成中断相关的回调与等待函数开发后,在freertos.c文件中对原有串口收发任务函数做适配修改,将查询方式的收发函数替换为中断方式,同时调用上述编写的等待函数做状态判断,完整的任务代码如下:
c
extern UART_HandleTypeDef huart4;
extern UART_HandleTypeDef huart2;
// 声明中断等待函数
int wait_uart2_tx_cplt(int timeout);
int wait_uart4_rx_cplt(int timeout);
// 串口2 中断发送任务函数
static void CH1_UART2_TxTaskFunction(void *pvParameters)
{
uint8_t c = 0;
while(1)
{
HAL_UART_Transmit_IT(&huart2, &c, 1); // 开启串口2中断发送1字节
wait_uart2_tx_cplt(100); // 等待发送完成,超时100ms
vTaskDelay(1000); // 延时1秒,周期性发送
c++; // 发送数据自增
}
}
// 串口4 中断接收任务函数
static void CH2_UART4_RxTaskFunction(void *pvParameters)
{
uint8_t c = 0;
int cnt = 0;
char buf[100];
HAL_StatusTypeDef err;
while(1)
{
err = HAL_UART_Receive_IT(&huart4, &c, 1); // 开启串口4中断接收1字节
if(wait_uart4_rx_cplt(1000) == 0) // 等待接收完成,超时1000ms
{
//接收成功,格式化数据并显示到LCD
sprintf(buf, "Recv Data : 0x%02x, Cnt : %d", c, cnt++);
Draw_String(0, 0, buf, 0x0000ff00, 0);
}
else
{
//接收超时,终止当前中断接收,规避异常
HAL_UART_AbortReceive_IT(&huart4);
}
}
}
六、运行现象与功能补充说明
将修改后的工程代码烧录至开发板后,实测运行现象如下,整体功能与查询方式一致,数据收发正常且 LCD 能实时显示接收信息:

补充:HAL_UART_AbortReceive_IT 的作用:强制立即终止【串口的中断模式接收】(
HAL_UART_Receive_IT开启的接收),并关闭对应串口的接收中断、清空接收相关的 HAL 状态标记、释放串口硬件资源,能有效规避接收超时后的串口中断异常问题。
若想深入了解本次串口中断函数的底层工作原理,可参考笔者 HAL 库快速入门系列笔记的 UART 章节,其中详细记录了相关函数的底层实现逻辑。
七、总结
本次笔记完成了从查询方式到中断方式的串口 RS485 收发功能升级,基于 FreeRTOS 完成了全流程开发,核心知识点与开发要点总结如下:
- 串口中断方式对比查询方式的核心优势:无需循环轮询串口状态,大幅降低 CPU 资源占用,只有存在实际收发数据时才触发逻辑,是更高效的串口开发方案;
- STM32 的 HAL 库串口中断开发,核心是「中断触发函数 + 完成回调函数」的组合,收发完成的状态需通过自定义标志位与等待函数做判断;
- 中断方式的串口开发,必须做好超时控制与异常处理,
HAL_UART_AbortReceive_IT能有效终止异常的中断接收,保证串口工作稳定; - FreeRTOS 的任务逻辑无需大幅修改,仅需替换串口的收发接口,即可无缝适配中断方式,体现了多任务开发的灵活性与低耦合性;
- 中断回调函数中仅做简单的标志位置位即可,避免在回调中编写复杂逻辑,防止阻塞中断流程。
八、结尾
本次的串口中断开发实战,是嵌入式串口通信的进阶优化,也是对中断机制与 HAL 库串口驱动的一次综合应用。从查询到中断,看似只是接口的替换,实则是对 CPU 资源利用效率的优化,这也是嵌入式开发中一个重要的设计思路:让 CPU 只处理必要的事务,其余时间可进入低功耗或处理其他业务。
RS485 的串口通信是工业场景的基础能力,而中断方式则是该能力的最优实现形式,本次的开发逻辑同样适用于其他串口外设的中断开发。结合 FreeRTOS 的多任务调度,能让中断处理与业务逻辑完美拆分,让程序的结构更清晰、运行更稳定。
技术的优化永无止境,从基础实现到细节打磨,每一次的优化都是对技术理解的深化。脚踏实地做好每一个基础功能的开发与优化,才能在复杂项目中从容应对各类需求,稳步积累自身的技术能力。