目录

一、前言

在完成开关量、环境监测、温湿度三类 F030 传感器的 Modbus 从机程序开发后,本次实现多传感器组网通信:将三类 F030 传感器的 485 接口接入 485 HUB,STM32H5 主控通过 485 HUB 与各传感器通信,作为 Modbus 主站读取三类传感器的核心数据(按键状态、光强、温湿度等),并在 LCD 屏幕上集中显示,同时实现对传感器 LED 的远程控制。

二、组网架构:H5 主控 + 多 F030 从机传感器

  • 硬件连接:F030 开关量传感器、环境监测传感器、温湿度传感器的 485 接口均接入 485 HUB;H5 主控的 485 接口与 HUB 相连,分为 CH1、CH2 两个通信通道。

  • 任务规划

    1. 任务 1(CH1):访问开关量传感器(地址 01H)、环境监测传感器(地址 02H),读取按键状态、ADC 值(光强 / 可调电阻)并控制 LED 闪烁,数据显示在 LCD;
    2. 任务 2(CH2):访问温湿度传感器(地址 03H),读取温湿度数据并控制 LED 闪烁,数据显示在 LCD。
  • 关键注意:两个任务均操作 LCD,需添加互斥保护机制,避免多任务同时绘图导致 LCD 花屏。

三、H5 主控程序设计:多任务 + Modbus 主站

1. 任务创建

基于 FreeRTOS 创建两个 Modbus 客户端任务,分别对应 CH1、CH2 通信通道:

  // 创建CH1客户端任务:访问开关量、环境监测传感器
  xTaskCreate(
      LibmodbusCH1ClientTask, 
      "LibmodbusCH1ClientTask", 
      200,  // 任务栈大小
      NULL, 
      osPriorityNormal,  // 任务优先级
      NULL); 
    
  // 创建CH2客户端任务:访问温湿度传感器
  xTaskCreate(
      LibmodbusCH2ClientTask, 
      "LibmodbusCH2ClientTask", 
      200, 
      NULL, 
      osPriorityNormal, 
      NULL); 

2. 基础 Modbus 客户端函数(参考模板)

以下是基于 FreeRTOS 和 libmodbus 实现的基础 Modbus 主站任务函数,核心逻辑为读取从站寄存器、LCD 显示、改写寄存器值,作为后续修改的模板:

// 基础Modbus RTU客户端(主站)任务函数
// 功能:读取从站保持寄存器1的值→LCD显示→自增后写回保持寄存器2
static void LibmodbusClientTask( void *pvParameters )	
{
	// Modbus RTU上下文
	modbus_t *ctx;
	// 函数返回值,判断操作是否成功
	int rc;
	// 存储寄存器读取值
	uint16_t val;
	// 读取/写入的寄存器数量
	int nb = 1;
	
	// 创建USB虚拟串口的Modbus RTU上下文:波特率115200、无校验、8数据位、1停止位
	ctx = modbus_new_st_rtu("usb", 115200, 'N', 8, 1);
	// 设置从机地址为1
	modbus_set_slave(ctx, 1);
	
	// 建立Modbus RTU连接
	rc = modbus_connect(ctx);
	if (rc == -1) {
		//fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno));
		modbus_free(ctx);
		vTaskDelete(NULL);; // 连接失败则删除任务
	}

	// 无限循环:读取→显示→改写寄存器
	for (;;) {
		/* 读取保持寄存器1的值 */
		rc = modbus_read_registers(ctx, 1, nb, &val);
		if (rc != nb) // 读取失败则跳过本次循环
			continue;

		/* 在LCD上显示数值(坐标0,0,红色) */
		Draw_Number(0, 0, val, 0xff0000);

		/* 数值自增 */
		val++;

		/* 将自增后的值写入保持寄存器2 */
		rc = modbus_write_registers(ctx, 2, nb, &val);
	}

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

3. CH1 客户端任务(开关量 + 环境监测传感器)

基于基础模板修改,实现 CH1 通道对两台传感器的访问,读取按键状态、ADC 值并控制 LED 闪烁:

// CH1客户端任务:访问开关量传感器(01H)、环境监测传感器(02H)
static void LibmodbusCH1ClientTask( void *pvParameters )	
{
	// Modbus RTU上下文
	modbus_t *ctx;
	// 函数返回值
	int rc;
	// 存储ADC/温湿度等值(16位寄存器)
	uint16_t vals[10];
	// 寄存器读取数量
	int nb = 1;
    // 存储开关量/线圈状态(8位)
    uint8_t bits[10];
    // LCD显示字符串缓冲区
    uint8_t buf[100];
    // LED状态(1-亮,0-灭),用于闪烁控制
    int led_status = 1;
	
	// 创建串口2的Modbus RTU上下文:对应CH1 485通道
	ctx = modbus_new_st_rtu("uart2", 115200, 'N', 8, 1);
	
	// 第一步:访问开关量传感器(从机地址01H)
	modbus_set_slave(ctx, 1);
	// 建立Modbus连接
	rc = modbus_connect(ctx);
	if (rc == -1) {
		//fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno));
		modbus_free(ctx);
		vTaskDelete(NULL);;
	}

	// 无限循环:采集数据+控制LED
	for (;;) {
        /* 1. 读取开关量传感器(01H):KEY1/KEY2/KEY3状态(DI寄存器0-2) */
        modbus_set_slave(ctx, 1);
		rc = modbus_read_input_bits(ctx, 0, 3, bits); // 读取3个离散输入位
		if (rc == 3) // 读取成功则显示
		{
            // 拼接按键状态字符串,显示在LCD第0行
            sprintf(buf, "SWITCH keys: %d %d %d", bits[0], bits[1], bits[2]);
            Draw_String(0, 0, buf, 0xff0000, 0);
		}
		// 控制开关量传感器LED1闪烁(改写DO寄存器2)
		rc = modbus_write_bit(ctx, 2, led_status);

        /* 2. 读取环境监测传感器(02H):两路ADC值(AI寄存器0-1) */
        modbus_set_slave(ctx, 2);
		rc = modbus_read_input_registers(ctx, 0, 2, vals); // 读取2个输入寄存器
		if (rc == 2) // 读取成功则显示
		{
            // 拼接ADC值字符串,显示在LCD第1行(16像素偏移)
            sprintf(buf, "ENV Sensor : opti 0x%x, res 0x%x          ", vals[0], vals[1]);
            Draw_String(0, 16, buf, 0xff0000, 0);
		}
		// 控制环境监测传感器LED1闪烁(改写DO寄存器2)
		rc = modbus_write_bit(ctx, 2, led_status);

        // 翻转LED状态,实现500ms闪烁
        led_status = !led_status;
        // 延时500ms,控制采集/刷新频率
        vTaskDelay(500);
	}

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

4. CH2 客户端任务(温湿度传感器)

采用与 CH1 一致的流程,实现 CH2 通道对温湿度传感器的访问,读取温湿度数据并控制 LED 闪烁:

// CH2客户端任务:访问温湿度传感器(03H)
static void LibmodbusCH2ClientTask( void *pvParameters )	
{
	// Modbus RTU上下文
	modbus_t *ctx;
	// 函数返回值
	int rc;
	// 存储温湿度值(16位寄存器)
	uint16_t vals[10];
	// 寄存器读取数量
	int nb = 1;
    // 存储线圈状态(备用)
    uint8_t bits[10];
    // LCD显示字符串缓冲区
    uint8_t buf[100];
    // LED状态,用于闪烁控制
    int led_status = 1;
	
	// 创建串口4的Modbus RTU上下文:对应CH2 485通道
	ctx = modbus_new_st_rtu("uart4", 115200, 'N', 8, 1);
	// 设置从机地址为3(温湿度传感器)
	modbus_set_slave(ctx, 3);
	
	// 建立Modbus连接
	rc = modbus_connect(ctx);
	if (rc == -1) {
		//fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno));
		modbus_free(ctx);
		vTaskDelete(NULL);;
	}

	// 无限循环:采集温湿度+控制LED
	for (;;) {
        /* 读取温湿度传感器(03H):温度、湿度值(AI寄存器0-1) */
		rc = modbus_read_input_registers(ctx, 0, 2, vals);
		if (rc == 2) // 读取成功则显示
		{
            // 拼接温湿度字符串(单位0.1℃/0.1%RH),显示在LCD第2行(32像素偏移)
            sprintf(buf, "TEM/HUM Sensor : temp %d.%d, humi %d.%d          ", vals[0]/10, vals[0]%10, vals[1]/10, vals[1]%10);
            Draw_String(0, 32, buf, 0xff0000, 0);
		}
		// 控制温湿度传感器LED1闪烁(改写DO寄存器2)
		rc = modbus_write_bit(ctx, 2, led_status);

        // 翻转LED状态
        led_status = !led_status;
        // 延时500ms
        vTaskDelay(500);
	}

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

四、LCD 互斥保护:避免多任务绘图花屏

由于两个任务同时操作 LCD,需在 LCD 驱动文件(draw.c)中添加互斥量保护,确保同一时间只有一个任务执行绘图操作:

1. 创建互斥量

在 LCD 初始化函数中创建互斥量:

// 定义LCD绘图互斥量(静态全局变量,仅draw.c可见)
static SemaphoreHandle_t g_spi_lcd_lock;

// LCD初始化函数
void Draw_Init(void)
{
    /* 创建互斥量:保护LCD绘图操作,避免多任务冲突 */
    g_spi_lcd_lock = xSemaphoreCreateMutex();
    
    // 获取LCD分辨率信息(原有逻辑)
    LCD_GetInfo(&g_lcd_width, &g_lcd_height);
}

2. 使用互斥量保护绘图核心函数

在 LCD 区域绘制函数中,先获取互斥量再绘图,完成后释放:

// LCD区域绘制核心函数(添加互斥保护)
static void Draw_Region(uint32_t x, uint32_t y, P_BitMap ptBitMap)
{   
    /* 获得互斥量:阻塞等待直到获取成功(portMAX_DELAY) */
    xSemaphoreTake(g_spi_lcd_lock, portMAX_DELAY);
    
    /* 原有绘图逻辑:设置显示区域→切换数据模式→发送像素数据 */
    // 设置要显示的矩形区域
    LCD_SetWindows(x, y, x + ptBitMap->width - 1, y + ptBitMap->height - 1);
    // 设置D/C引脚为数据模式(后续发送像素数据)
    LCD_SetDataLine();
    // 发送像素数据(RGB565格式,2字节/像素)
    LCD_WriteDatas(ptBitMap->datas, ptBitMap->height * ptBitMap->width * 2);

    /* 释放互斥量:允许其他任务访问LCD */
    xSemaphoreGive(g_spi_lcd_lock);
}

五、功能验证:多传感器数据采集与显示

完成程序编译烧录后,H5 主控可通过 485 HUB 同时访问三类 F030 传感器,LCD 屏幕上清晰显示各传感器数据:

  • 开关量传感器:KEY1/KEY2/KEY3 的按下状态;
  • 环境监测传感器:光敏电阻(光强)、可调电阻的 ADC 值;
  • 温湿度传感器:温度(单位 0.1℃)、湿度(单位 0.1% RH);
  • 三类传感器的 LED 均按 500ms 周期闪烁,验证远程控制功能有效。

实际测试效果如下:

请添加图片描述

六、总结

  1. 采用 485 HUB 实现多 F030 传感器组网,H5 主控作为 Modbus 主站通过不同通道 / 地址区分传感器;
  2. 基于 FreeRTOS 多任务调度,每个 485 通道对应独立任务,保障多传感器数据采集的并行性;
  3. 通过 FreeRTOS 互斥量保护 LCD 绘图操作,解决多任务同时访问导致的花屏问题;
  4. Modbus 主站程序通过切换从机地址,可在同一通道访问多个传感器,降低硬件成本。

七、结尾

本次完成了 Modbus 多传感器组网的核心开发,实现了 H5 主控对三类 F030 传感器的集中访问、数据采集与远程控制,这套组网方案可直接复用至工业多传感器监测场景。掌握多任务调度 + Modbus 主从通信 + 外设互斥保护的设计思路,是实现复杂工业监测系统的关键。感谢各位的阅读,持续关注本系列笔记,一起探索更多工业级 Modbus 组网与嵌入式系统开发的实用技巧!

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐