1. 项目背景与硬件连接
在工业自动化领域,Modbus协议因其简单可靠的特点被广泛应用于设备间的通信。但在实际项目中,我们常常遇到这样的场景:上位机(如PC或PLC)需要同时监控多个传感器和执行器,而直接连接多个设备会面临接口不足、协议复杂等问题。这时就需要一个中间主控设备作为"协议转换器",这就是我们要实现的Modbus网关系统。
1.1 硬件架构设计
整个系统的硬件连接采用分层结构:
code复制PC (USB转串口)
│
└── STM32H5主控板
├── UART2 (CH1) ── RS485总线A ── 开关量传感器 (地址1)
│ └── 环境监测传感器 (地址2)
└── UART4 (CH2) ── RS485总线B ── 温湿度传感器 (地址3)
硬件选型考虑:
- STM32H5作为主控:具备多路UART接口,支持FreeRTOS实时操作系统
- RS485物理层:采用MAX3485芯片,支持半双工通信
- 传感器选择:
- 开关量传感器:检测按键状态,控制继电器和LED
- 环境监测传感器:采集光敏电阻和可调电阻的ADC值
- 温湿度传感器:测量环境温湿度数据
1.2 为什么需要网关架构?
这种设计主要解决以下实际问题:
- 接口扩展:PC通常只有一个串口,无法直接连接多个RS485设备
- 协议简化:上位机只需与网关通信,无需了解底层各传感器的协议细节
- 数据处理:主控可以在本地进行数据预处理(如单位转换、报警判断)
- 实时性保障:通过任务调度,确保关键数据的采集频率
注意:虽然Modbus协议本身支持同一总线上多个设备(通过地址区分),但在实际项目中,将所有设备挂在同一总线会带来布线困难、终端电阻配置复杂等问题。分通道设计更符合工程实践。
2. 整体软件架构设计
2.1 基于FreeRTOS的多任务模型
系统采用实时操作系统管理多个并行任务:
code复制任务划分:
├── Modbus从站任务(UART1)
│ ├── 响应PC的Modbus请求
│ └── 操作全局映射表
├── 传感器采集任务A(UART2)
│ ├── 读取开关量传感器(地址1)
│ └── 读取环境监测传感器(地址2)
├── 传感器采集任务B(UART4)
│ └── 读取温湿度传感器(地址3)
└── LCD显示任务
└── 实时刷新各传感器状态
2.2 关键数据结构设计
核心是全局的Modbus映射表,使用libmodbus库提供的结构体:
c复制typedef struct {
uint8_t *tab_bits; // 线圈状态(可读可写)
uint8_t *tab_input_bits; // 离散输入(只读)
uint16_t *tab_input_registers; // 输入寄存器(只读)
uint16_t *tab_registers; // 保持寄存器(可读可写)
} modbus_mapping_t;
初始化时根据实际需求分配空间:
c复制g_h5_mapping = modbus_mapping_new_start_address(
0, 16, // 16个线圈(0x0000-0x000F)
0, 3, // 3个离散输入(0x0000-0x0002)
0, 4, // 4个输入寄存器(0x0000-0x0003)
0, 0 // 不使用保持寄存器
);
3. 寄存器映射详细设计
3.1 开关量传感器(地址1)映射
| H5地址 | 类型 | 对应传感器寄存器 | 功能描述 |
|---|---|---|---|
| 0x0000 | DI | 0x0000 | KEY1状态(1=按下) |
| 0x0001 | DI | 0x0001 | KEY2状态 |
| 0x0002 | DI | 0x0002 | KEY3状态 |
| 0x0001 | DO | 0x0000 | 继电器1控制 |
| 0x0002 | DO | 0x0001 | 继电器2控制 |
| 0x0003 | DO | 0x0002 | LED1控制 |
3.2 环境监测传感器(地址2)映射
| H5地址 | 类型 | 对应传感器寄存器 | 功能描述 |
|---|---|---|---|
| 0x0000 | AI | 0x0000 | 光敏ADC值(0-4095) |
| 0x0001 | AI | 0x0001 | 可调电阻ADC值 |
| 0x0006 | DO | 0x0000 | 蜂鸣器1控制 |
| 0x0007 | DO | 0x0001 | 蜂鸣器2控制 |
3.3 温湿度传感器(地址3)映射
| H5地址 | 类型 | 对应传感器寄存器 | 功能描述 |
|---|---|---|---|
| 0x0002 | AI | 0x0000 | 温度值(单位0.1℃) |
| 0x0003 | AI | 0x0001 | 湿度值(单位0.1%RH) |
| 0x000B | DO | 0x0000 | 蜂鸣器1控制 |
| 0x000C | DO | 0x0001 | 蜂鸣器2控制 |
4. 关键任务实现细节
4.1 Modbus从站任务实现
c复制void ModbusServerTask(void *pvParameters)
{
modbus_t *ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1);
modbus_set_slave(ctx, 1); // 网关自身地址为1
uint8_t query[MODBUS_RTU_MAX_ADU_LENGTH];
while(1) {
int rc = modbus_receive(ctx, query);
if (rc > 0) {
modbus_reply(ctx, query, rc, g_h5_mapping);
} else if (rc == -1) {
// 错误处理
modbus_flush(ctx);
}
vTaskDelay(1);
}
}
4.2 传感器采集任务实现(以温湿度为例)
c复制void SensorTask_TempHum(void *pvParameters)
{
modbus_t *ctx = modbus_new_rtu("/dev/ttyS3", 115200, 'N', 8, 1);
uint16_t regs[2];
char lcd_buf[32];
while(1) {
modbus_set_slave(ctx, 3); // 传感器地址3
if (modbus_read_input_registers(ctx, 0, 2, regs) == 2) {
// 更新全局映射表
g_h5_mapping->tab_input_registers[2] = regs[0]; // 温度
g_h5_mapping->tab_input_registers[3] = regs[1]; // 湿度
// LCD显示
xSemaphoreTake(lcd_mutex, portMAX_DELAY);
sprintf(lcd_buf, "Temp:%d.%dC Humi:%d.%d%%",
regs[0]/10, regs[0]%10, regs[1]/10, regs[1]%10);
LCD_DisplayString(2, lcd_buf);
xSemaphoreGive(lcd_mutex);
}
vTaskDelay(1000); // 1秒采集一次
}
}
5. 工程实践中的注意事项
5.1 通信可靠性保障
- 超时重试机制:
c复制#define MAX_RETRY 3
int retry = 0;
do {
rc = modbus_read_registers(ctx, addr, count, dest);
if (rc == count) break;
vTaskDelay(50);
} while (++retry < MAX_RETRY);
- 数据校验:
- 在写入关键执行器(如继电器)时,建议采用"读取-验证"模式
- 对模拟量数据可增加范围检查(如湿度不应超过100%)
5.2 性能优化技巧
- 采集周期差异化:
- 开关量:100ms(快速响应按键)
- 模拟量:500ms-1s(变化较慢)
- 批量读取优化:
c复制// 一次性读取多个寄存器,减少通信次数
modbus_read_input_registers(ctx, 0, 4, regs);
5.3 常见问题排查
- 通信失败排查步骤:
- 检查物理连接(接线、终端电阻)
- 确认波特率、校验位等参数一致
- 使用Modbus调试工具抓包分析
- 数据不同步问题:
- 确保全局映射表的访问是线程安全的
- 对关键数据区域考虑使用互斥锁
6. 系统扩展思路
- 协议扩展:
- 增加Modbus TCP支持,实现网络远程访问
- 添加JSON格式数据导出功能
- 功能增强:
- 实现数据本地存储(SD卡记录)
- 增加阈值报警功能
- 支持参数远程配置
- 设备兼容性:
- 设计动态映射表,支持热插拔设备
- 开发自动识别传感器类型的机制
这个Modbus网关设计方案在实际工业项目中已经多次验证,特别适合中小型自动化系统的数据采集与控制。通过灵活调整任务调度策略和映射表配置,可以适应各种不同的设备组合需求。