RS485 双串口通信 + LCD 实时显示(中断版)

目录

一、前言

本篇笔记将承接上一篇的内容,对上一篇中查询方式 实现的串口 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 完成了全流程开发,核心知识点与开发要点总结如下:

  1. 串口中断方式对比查询方式的核心优势:无需循环轮询串口状态,大幅降低 CPU 资源占用,只有存在实际收发数据时才触发逻辑,是更高效的串口开发方案;
  2. STM32 的 HAL 库串口中断开发,核心是「中断触发函数 + 完成回调函数」的组合,收发完成的状态需通过自定义标志位与等待函数做判断;
  3. 中断方式的串口开发,必须做好超时控制与异常处理,HAL_UART_AbortReceive_IT能有效终止异常的中断接收,保证串口工作稳定;
  4. FreeRTOS 的任务逻辑无需大幅修改,仅需替换串口的收发接口,即可无缝适配中断方式,体现了多任务开发的灵活性与低耦合性;
  5. 中断回调函数中仅做简单的标志位置位即可,避免在回调中编写复杂逻辑,防止阻塞中断流程。

八、结尾

本次的串口中断开发实战,是嵌入式串口通信的进阶优化,也是对中断机制与 HAL 库串口驱动的一次综合应用。从查询到中断,看似只是接口的替换,实则是对 CPU 资源利用效率的优化,这也是嵌入式开发中一个重要的设计思路:让 CPU 只处理必要的事务,其余时间可进入低功耗或处理其他业务。

RS485 的串口通信是工业场景的基础能力,而中断方式则是该能力的最优实现形式,本次的开发逻辑同样适用于其他串口外设的中断开发。结合 FreeRTOS 的多任务调度,能让中断处理与业务逻辑完美拆分,让程序的结构更清晰、运行更稳定。

技术的优化永无止境,从基础实现到细节打磨,每一次的优化都是对技术理解的深化。脚踏实地做好每一个基础功能的开发与优化,才能在复杂项目中从容应对各类需求,稳步积累自身的技术能力。

相关推荐
confiself2 小时前
Engram论文学习
学习
小痞同学2 小时前
【铁头山羊STM32】HAL库 2.UART部分
stm32·单片机·嵌入式硬件
崇山峻岭之间2 小时前
Matlab学习记录36
学习
永远快乐的攻城狮2 小时前
二、学习电子元器件-电阻器
学习
乡野码圣2 小时前
【RK3588 Android12】高精度定时器hrtimer
单片机·嵌入式硬件
LateFrames2 小时前
泰勒级数:从 “单点” 到 “理论与实践的鸿沟”
学习·算法
xiaobobo33302 小时前
c语言什么时候适合用三目运算什么时候适合用阶梯判断
c语言·三目运算·阶梯判断·花括号作用域
宵时待雨3 小时前
数据结构(初阶)笔记归纳4:单链表的实现
c语言·开发语言·数据结构·笔记·算法
航Hang*3 小时前
第3章:复习篇——第4节:创建、管理视图与索引---题库
网络·数据库·笔记·sql·学习·mysql·期末