libmodbus STM32 板载串口实验(双串口主从通信)

目录

一、前言

在上一篇笔记中,我们完成了 libmodbus 适配 STM32 板载串口后端的核心代码改造,实现了板载串口与 libmodbus 协议层的解耦与统一管理。本次实验基于该改造成果,搭建双串口主从通信场景:将 UART2 作为 Modbus 主机(Client)发送请求,UART4 作为 Modbus 从机(Server)接收并响应请求,通过 LED 闪烁、LCD 显示寄存器累加数值的直观现象,验证板载串口后端的实际运行效果。

二、实验核心逻辑:双串口主从任务设计

实验通过 FreeRTOS 创建两个独立任务,分别实现 Modbus 主机(UART2)与从机(UART4)的核心逻辑,同时在 FreeRTOS 初始化函数中完成任务创建,以下是完整代码(关键逻辑已添加注释,未修改原有代码):

1. 从机任务:CH2_UART4_ServerTask(UART4 接收请求并响应)

c 复制代码
static void CH2_UART4_ServerTask( void *pvParameters )	
{
	uint8_t *query; // 请求报文缓冲区
	modbus_t *ctx; // Modbus从机上下文
	int rc; // 函数返回值
	modbus_mapping_t *mb_mapping; // 寄存器映射结构体
	char buf[100]; // 临时缓冲区(未使用)
	int cnt = 0; // 计数变量(未使用)
	
	// 创建UART4的RTU上下文:波特率115200、无校验、8数据位、1停止位
	ctx = modbus_new_st_rtu("uart4", 115200, 'N', 8, 1);
	modbus_set_slave(ctx, 1); // 设置从机地址为1
	query = pvPortMalloc(MODBUS_RTU_MAX_ADU_LENGTH); // 分配请求缓冲区

	// 初始化寄存器映射:四类寄存器起始地址0,数量各10
	mb_mapping = modbus_mapping_new_start_address(0,
												  10,
												  0,
												  10,
												  0,
												  10,
												  0,
												  10);
	
	// 初始化线圈寄存器为0(LED初始熄灭)
	memset(mb_mapping->tab_bits, 0, mb_mapping->nb_bits);
	// 初始化保持寄存器为0x55(测试用初始值)
	memset(mb_mapping->tab_registers, 0x55, mb_mapping->nb_registers*2);

	// 建立UART4串口连接
	rc = modbus_connect(ctx);
	if (rc == -1) {
		//fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno));
		modbus_free(ctx);
		vTaskDelete(NULL);; // 连接失败则删除任务
	}

	// 无限循环接收并响应主机请求
	for (;;) {
		do {
			// 接收主机请求报文,过滤无效请求(返回0的查询)
			rc = modbus_receive(ctx, query);
			/* Filtered queries return 0 */
		} while (rc == 0);
 
		/* The connection is not closed on errors which require on reply such as
		   bad CRC in RTU. */
		// 非CRC错误则继续循环
		if (rc == -1 && errno != EMBBADCRC) {
			/* Quit */
			continue;
		}

		// 解析请求并回复主机
		rc = modbus_reply(ctx, query, rc, mb_mapping);
		if (rc == -1) {
			//break;
		}

		// 线圈寄存器第0位控制PC12引脚LED:1点亮,0熄灭
		if (mb_mapping->tab_bits[0])
			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_RESET);
		else
			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_SET);

		// 延时1秒后,保持寄存器1的值自增1
		vTaskDelay(1000);
		mb_mapping->tab_registers[1]++;
	}

	// 释放资源(实际循环不会执行到此处)
	modbus_mapping_free(mb_mapping);
	vPortFree(query);
	modbus_close(ctx);
	modbus_free(ctx);

	vTaskDelete(NULL);
}

2. 主机任务:CH1_UART2_ClientTask(UART2 发送请求)

c 复制代码
static void CH1_UART2_ClientTask( void *pvParameters )	
{
	modbus_t *ctx; // Modbus主机上下文
	int rc; // 函数返回值
	uint16_t val; // 存储读取的寄存器值
	int nb = 1; // 单次读取的寄存器数量
	int level = 1; // LED控制电平(初始为1)
	char buf[100]; // 临时缓冲区(未使用)
	int cnt = 0; // 计数变量(未使用)
	
	// 创建UART2的RTU上下文:波特率115200、无校验、8数据位、1停止位
	ctx = modbus_new_st_rtu("uart2", 115200, 'N', 8, 1);
	modbus_set_slave(ctx, 1); // 设置目标从机地址为1
	
	// 建立UART2串口连接
	rc = modbus_connect(ctx);
	if (rc == -1) {
		//fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno));
		modbus_free(ctx);
		vTaskDelete(NULL);; // 连接失败则删除任务
	}

	// 无限循环执行主机操作
	for (;;) {
		/* read hoding register 1 */
		// 读取从机保持寄存器1的值
		rc = modbus_read_registers(ctx, 1, nb, &val);
		if (rc != nb)
		{
			continue; // 读取失败则跳过本次循环
		}

		/* display on lcd */
		// 将读取到的寄存器值显示在LCD屏幕上
		Draw_Number(0, 0, val, 0xff0000);

		/* delay 2s */
		// 延时2秒后,翻转LED控制电平并写入从机线圈寄存器0
		vTaskDelay(2000);
		modbus_write_bit(ctx, 0, level);
		level = !level;
	}

	// 释放资源(实际循环不会执行到此处)
	modbus_close(ctx);
	modbus_free(ctx);

	vTaskDelete(NULL);
}

3. FreeRTOS 任务初始化

c 复制代码
void MX_FREERTOS_Init(void) {
  // 创建主机任务:UART2客户端任务
  xTaskCreate(
	  CH1_UART2_ClientTask,
	  "CH1_UART2_ClientTask", 
	  200, // 任务栈大小
	  NULL, 
	  osPriorityNormal, // 任务优先级
	  NULL); 

  // 创建从机任务:UART4服务端任务
  xTaskCreate(
	  CH2_UART4_ServerTask, 
	  "CH2_UART4_ServerTask", 
	  200, // 任务栈大小
	  NULL, 
	  osPriorityNormal, // 任务优先级
	  NULL); 
	}

补充:双任务均采用osPriorityNormal普通优先级,栈大小配置为 200,适配本实验的轻量通信需求;若需扩展功能(如更多寄存器操作、复杂数据处理),可适当增大栈大小。

三、实验现象与功能验证

烧录程序后,开发板呈现以下核心现象,验证双串口主从通信逻辑正常:

  1. LED 闪烁:主机每 2 秒翻转一次从机线圈寄存器 0 的电平,从机根据该值控制 LED 亮灭,实现周期性闪烁;
  2. LCD 数值累加:从机每 1 秒将保持寄存器 1 的值自增 1,主机每 2 秒读取该值并显示在 LCD 上,可见数值持续累加。

实验效果如下图所示:

四、总结

  1. 实验基于双串口(UART2 主机、UART4 从机)搭建主从通信场景,验证了板载串口作为 libmodbus 后端的有效性;
  2. 主机任务实现寄存器读取、LCD 显示、LED 电平控制,从机任务实现请求响应、寄存器累加、LED 硬件联动,功能闭环完整;
  3. FreeRTOS 双任务调度稳定,核心现象(LED 闪烁、数值累加)符合预期,证明代码改造与任务设计的正确性。

五、结尾

本次实验完成了 libmodbus 板载串口后端的最终功能验证,通过双串口主从通信实现了硬件联动与数据可视化,这套双串口主从设计方案可直接复用至工业现场的多设备通信场景。至此,libmodbus 在 STM32 平台的 USB 串口、板载串口后端适配与验证已全部完成,形成了完整的移植与应用闭环。感谢各位的阅读,持续关注本系列笔记,一起探索更多嵌入式通信与开源库落地的实战方案!

相关推荐
时代的凡人5 小时前
0208晨间笔记
笔记
今天只学一颗糖6 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
testpassportcn6 小时前
AWS DOP-C02 認證完整解析|AWS DevOps Engineer Professional 考試
网络·学习·改行学it
不做无法实现的梦~7 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
游乐码9 小时前
c#变长关键字和参数默认值
学习·c#
饭碗、碗碗香10 小时前
【Python学习笔记】:Python的hashlib算法简明指南:选型、场景与示例
笔记·python·学习
Wils0nEdwards10 小时前
初中化学1
笔记
魔力军11 小时前
Rust学习Day4: 所有权、引用和切片介绍
开发语言·学习·rust
wubba lubba dub dub75011 小时前
第三十六周 学习周报
学习
学编程的闹钟11 小时前
PHP字符串表示方式全解析
学习