第七章:传感器点表设计与Modbus寄存器映射
7.1 Modbus寄存器类型回顾
在Modbus协议中,从设备通过四种类型的寄存器来暴露数据,每种寄存器有特定的访问属性和用途:
| 寄存器种类 | PLC/组态地址 | Modbus寄存器地址范围 | 简称 | 读写状态 | 数据宽度 | 典型用途 |
|---|---|---|---|---|---|---|
| 线圈状态 | 00001~09999 | 0000H~FFFFH | 0x | 可读可写 | 1位 | 开关输出(继电器、LED) |
| 离散输入状态 | 10001~19999 | 0000H~FFFFH | 1x | 只读 | 1位 | 开关输入(按键、传感器状态) |
| 输入寄存器 | 30001~39999 | 0000H~FFFFH | 3x | 只读 | 16位 | 模拟量输入(温度、湿度、电压) |
| 保持寄存器 | 40001~49999 | 0000H~FFFFH | 4x | 可读可写 | 16位 | 参数设置、控制输出 |
注意:Modbus协议中地址从0开始,但PLC组态地址通常从1开始编号,且前导数字表示类型。例如:40001对应保持寄存器地址0x0000。
7.2 传感器点表设计思路
点表(Point Map)是从设备将内部数据映射到Modbus寄存器地址的规划表。设计点表时,需要考虑以下因素:
-
数据类型:确定每个数据是位(布尔)还是16位整数(或浮点数拆分到多个寄存器)。
-
访问权限:哪些数据只读(传感器值),哪些可读写(控制参数)。
-
地址分配:合理规划地址范围,避免冲突,方便扩展。
-
单位与精度:明确数值的物理含义(如温度0.1°C,湿度0.01%等)。
-
硬件资源对应:将物理输入/输出与寄存器位一一对应。
7.3 开发板硬件资源分析
以提供的开发板图片为例,硬件资源包括:

-
温湿度模块:模拟量传感器,输出温度和湿度值(通常通过I2C或单总线读取,但Modbus需要将其转换为寄存器值)。
-
用户按键:3个独立按键,可作为离散输入。
-
用户LED:4个独立LED,可作为线圈输出控制。
-
有源蜂鸣器:可通过线圈控制鸣响。
-
RS485接口:用于Modbus RTU通信。
-
其他:复位按键、启动模式、电源等。
7.4 点表设计示例
假设我们要为这个开发板设计Modbus从设备点表,需要定义以下数据:
7.4.1 离散输入(只读位)—— 对应物理按键
| 地址(PLC) | Modbus地址 | 简称 | 名称 | 说明 |
|---|---|---|---|---|
| 10001 | 0x0000 | 1x1 | 按键1状态 | 0=未按下,1=按下 |
| 10002 | 0x0001 | 1x2 | 按键2状态 | 同上 |
| 10003 | 0x0002 | 1x3 | 按键3状态 | 同上 |
7.4.2 线圈(可读写位)—— 控制LED和蜂鸣器
| 地址(PLC) | Modbus地址 | 简称 | 名称 | 说明 |
|---|---|---|---|---|
| 00001 | 0x0000 | 0x1 | LED1控制 | 0=灭,1=亮 |
| 00002 | 0x0001 | 0x2 | LED2控制 | 同上 |
| 00003 | 0x0002 | 0x3 | LED3控制 | 同上 |
| 00004 | 0x0003 | 0x4 | LED4控制 | 同上 |
| 00005 | 0x0004 | 0x5 | 蜂鸣器控制 | 0=关,1=开 |
7.4.3 输入寄存器(只读16位)—— 传感器数据
| 地址(PLC) | Modbus地址 | 简称 | 名称 | 数据类型 | 单位 | 说明 |
|---|---|---|---|---|---|---|
| 30001 | 0x0000 | 3x1 | 温度值 | uint16_t | 0.1°C | 实际温度 = 寄存器值 × 0.1 |
| 30002 | 0x0001 | 3x2 | 湿度值 | uint16_t | 0.01% | 实际湿度 = 寄存器值 × 0.01 |
7.4.4 保持寄存器(可读写16位)—— 参数设置
| 地址(PLC) | Modbus地址 | 简称 | 名称 | 数据类型 | 单位 | 说明 |
|---|---|---|---|---|---|---|
| 40001 | 0x0000 | 4x1 | 温度报警阈值 | uint16_t | 0.1°C | 当温度超过此值时触发报警 |
| 40002 | 0x0001 | 4x2 | 湿度报警阈值 | uint16_t | 0.01% | 当湿度超过此值时触发报警 |
| 40003 | 0x0002 | 4x3 | 设备地址 | uint16_t | - | Modbus从站地址(1~247) |
| 40004 | 0x0003 | 4x4 | 波特率 | uint16_t | - | 可选值:0=9600,1=19200,... |
7.5 点表在代码中的实现(libmodbus)
在libmodbus从设备中,我们需要使用modbus_mapping_t结构体来定义点表对应的内存区域。
// 定义存储数组
// 线圈:5个(LED1~4 + 蜂鸣器)
uint8_t coil_tab[1]; // 1字节即可存放8个线圈,但为了清晰可用数组
// 离散输入:3个按键
uint8_t discrete_input_tab[1];
// 输入寄存器:2个(温度、湿度)
uint16_t input_reg_tab[2];
// 保持寄存器:4个(阈值、地址、波特率)
uint16_t holding_reg_tab[4];
// 创建映射结构体
modbus_mapping_t *mb_mapping;
// 初始化映射
mb_mapping = modbus_mapping_new_start_address(
0, 5, // 线圈起始地址0,数量5
0, 3, // 离散输入起始地址0,数量3
0, 2, // 输入寄存器起始地址0,数量2
0, 4 // 保持寄存器起始地址0,数量4
);
// 将内存指针指向实际存储数组
mb_mapping->tab_bits = coil_tab;
mb_mapping->tab_input_bits = discrete_input_tab;
mb_mapping->tab_input_registers = input_reg_tab;
mb_mapping->tab_registers = holding_reg_tab;
// 初始化默认值
coil_tab[0] = 0; // 所有LED关闭
discrete_input_tab[0] = 0;
input_reg_tab[0] = 250; // 25.0°C
input_reg_tab[1] = 5000; // 50.00%
holding_reg_tab[0] = 300; // 30.0°C报警
holding_reg_tab[1] = 8000; // 80.00%报警
holding_reg_tab[2] = 1; // 地址1
holding_reg_tab[3] = 0; // 波特率9600
7.6 物理量与寄存器值的转换
7.6.1 温度转换
假设温湿度传感器返回真实温度 = 25.3°C,采用0.1°C单位:
// 写入输入寄存器
input_reg_tab[0] = (uint16_t)(temperature * 10); // 253
当主设备读取30001时,得到值253,实际温度 = 253 × 0.1 = 25.3°C。
7.6.2 湿度转换
湿度真实值 = 45.67%,采用0.01%单位:
input_reg_tab[1] = (uint16_t)(humidity * 100); // 4567
7.7 从设备主循环处理
在从设备主循环中,需要不断更新只读寄存器(输入寄存器、离散输入)的值,并根据线圈状态控制硬件。
int main(void)
{
modbus_t *ctx;
modbus_mapping_t *mb_mapping;
uint8_t req[MODBUS_RTU_MAX_ADU_LENGTH];
int rc;
// 初始化串口等硬件
// ...
// 创建Modbus RTU上下文
ctx = modbus_new_rtu("/dev/ttyS0", 9600, 'N', 8, 1);
modbus_set_slave(ctx, 1); // 从站地址1
// 创建数据映射(假设已经分配好存储数组)
mb_mapping = modbus_mapping_new_start_address(0,5, 0,3, 0,2, 0,4);
mb_mapping->tab_bits = coil_tab;
mb_mapping->tab_input_bits = discrete_input_tab;
mb_mapping->tab_input_registers = input_reg_tab;
mb_mapping->tab_registers = holding_reg_tab;
// 连接
modbus_connect(ctx);
while (1) {
// 1. 更新物理输入(按键状态)
discrete_input_tab[0] = read_button1();
discrete_input_tab[1] = read_button2();
discrete_input_tab[2] = read_button3();
// 2. 更新传感器数据(温度、湿度)
float temp = read_temperature();
float hum = read_humidity();
input_reg_tab[0] = (uint16_t)(temp * 10);
input_reg_tab[1] = (uint16_t)(hum * 100);
// 3. 根据线圈状态控制输出
if (coil_tab[0] & 0x01) set_led1(1); else set_led1(0);
if (coil_tab[0] & 0x02) set_led2(1); else set_led2(0);
if (coil_tab[0] & 0x04) set_led3(1); else set_led3(0);
if (coil_tab[0] & 0x08) set_led4(1); else set_led4(0);
if (coil_tab[0] & 0x10) set_buzzer(1); else set_buzzer(0);
// 4. 接收Modbus请求并回复
rc = modbus_receive(ctx, req);
if (rc > 0) {
modbus_reply(ctx, req, rc, mb_mapping);
} else if (rc == -1) {
// 错误处理
break;
}
}
// 清理
modbus_mapping_free(mb_mapping);
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
7.8 点表设计的进阶考虑
7.8.1 多字节数据(如浮点数)
如果传感器输出浮点数,可以用两个连续的保持寄存器组合成32位数据(例如IEEE754格式)。主设备需按相同格式解析。
7.8.2 地址对齐与扩展
建议在点表中预留一些空地址,方便后续功能添加。例如,线圈从0x00开始,离散输入从0x20开始,输入寄存器从0x40开始,保持寄存器从0x60开始,避免重叠。
7.8.3 异常处理
设计点表时要考虑非法地址访问时的异常响应,从设备应返回异常码0x02(非法数据地址)。
7.8.4 广播地址支持
如果需要支持广播(地址0),则从设备应处理写操作但不回复响应。
7.9 调试与测试点表
使用Modbus主站软件(如Modbus Poll)测试点表:
-
读离散输入:检查按键状态是否正确。
-
读输入寄存器:检查温湿度值是否合理。
-
写线圈:控制LED亮灭,验证输出。
-
写保持寄存器:修改报警阈值,再读取确认。
7.10 本章核心知识点总结
-
四种寄存器类型:线圈(0x)、离散输入(1x)、输入寄存器(3x)、保持寄存器(4x)。
-
点表设计步骤:列出所有数据→确定类型→分配地址→定义单位精度→编码实现。
-
物理量与寄存器转换:使用缩放因子(如×10、×100)将浮点转为整数。
-
从设备程序结构:更新输入数据→接收请求→处理请求→回复。
-
硬件对应:线圈→输出(LED、蜂鸣器);离散输入→输入(按键);输入寄存器→传感器值;保持寄存器→参数设置。
-
注意事项:地址范围、字节序、异常处理、广播地址。
更多推荐
所有评论(0)