Modbus协议(一)
Modbus协议
概述
Modbus诞生于1979年,莫迪康Modicon公司(传闻是发明了第一台PLC的公司,后被施耐德电气公司收购),目前是工业领域应用最广泛的通信协议。
Modbus之所以使用广泛,主要原因是:
Modbus协议标准开放、无专利保护,任何厂商都可以用
Modbus协议支持多种电气接口,包括RS232、RS485、TCP/IP等,还可以在各种介质上传输,如双绞线、光纤、红外、无线等
Modbus协议消息帧格式简单、紧凑、通俗易懂。用户理解和使用简单,厂商容易开发和集成,方便形成工业控制网络
Modbus协议数据模型统一,通过预定义寄存器地址读写数据
Modbus协议是一种应用层报文传输协议,协议本身并没有定义物理层,而是定义了控制器能够认识和使用的消息结构,这就使得它可以在不同的物理层上实现设备和设备之间的通信。下表是Modbus协议在不同物理层应用举例。
| Modbus 协议类型 | 通信方式 | 物理层标准 | 线缆类型 | 接口/接头 | 最大传输距离 | 设备示例 | 应用场景举例 | 
|---|---|---|---|---|---|---|---|
| Modbus RTU | 串行通信 | RS-485 | 双绞屏蔽电缆(如:RVSP) | DB9、RJ45、端子排 | 1200 米(RS-485) | PLC、变频器、传感器、仪表等 | 工厂自动化、楼宇控制、产线设备 | 
| Modbus ASCII | 串行通信 | RS-232 / RS-485 | 普通双绞线或屏蔽线 | DB9、RJ45、端子排 | RS-232: 15米 RS-485: 1200米 | 调试工具、老式设备 | 调试、低速通信、简单人机交互 | 
| Modbus TCP | 以太网通信 | TCP/IP over Ethernet | 标准网线(Cat5e / Cat6) | RJ45 | 100 米(单段) 可扩展 | HMI、SCADA、服务器、工业交换机 | 工业以太网、远程监控、数据采集系统 | 
| Modbus UDP | 以太网通信 | UDP/IP | 标准网线(Cat5e / Cat6) | RJ45 | 同TCP/IP | 实时控制系统、运动控制设备 | 对实时性要求高、容忍少量丢包的控制系统 | 
其中在工业上最常见的就是ModbusRTU和ModbusTCP,一个是使用双绞线走RS485协议标准,一个是使用标准网线走TCP/IP协议标准。
数据模型
Modbus 定义了四种主要的数据区(寄存器类型),具体根据不同的通信对象(设备),会对应到PLC、变频器、智能仪表等内部的存储区,具体需要查看PLC、变频器、智能仪表的对应说明:
| 类型 | 可读/可写 | 描述 | 
|---|---|---|
| 线圈(Coils) | 读/写 | 开关量输出(0x 寄存器) | 
| 离散输入(Discrete Inputs) | 只读 | 开关量输入(1x 寄存器) | 
| 输入寄存器(Input Registers) | 只读 | 模拟量输入(3x 寄存器) | 
| 保持寄存器(Holding Registers) | 读/写 | 模拟量输出(4x 寄存器) | 
功能码
协议通过功能码结合设备地址+数据区地址实现对设备存储信息的读写。功能码如下表所示:
| 功能码(十进制) | 功能码(十六进制) | 名称 | 操作类型 | 操作对象 | Modbus地址范围 | 数据类型 | 最大支持数量 | 
|---|---|---|---|---|---|---|---|
| 1 | 0x01 | 读线圈状态 | 读 | 线圈(Coils) | 00001-09999 | 位(Bit) | 2000 | 
| 2 | 0x02 | 读离散输入 | 读 | 离散输入(Discrete Inputs) | 10001-19999 | 位(Bit) | 2000 | 
| 3 | 0x03 | 读保持寄存器 | 读 | 保持寄存器(Holding Registers) | 40001-49999 | 字(Word) | 125 | 
| 4 | 0x04 | 读输入寄存器 | 读 | 输入寄存器(Input Registers) | 30001-39999 | 字(Word) | 125 | 
| 5 | 0x05 | 写单线圈 | 写 | 单个线圈 | 00001-09999 | 位(Bit) | 1 | 
| 6 | 0x06 | 写单个寄存器 | 写 | 单个保持寄存器 | 40001-49999 | 字(Word) | 1 | 
| 15 | 0x0F | 写多个线圈 | 写 | 多个线圈 | 00001-09999 | 位(Bit) | 1968 | 
| 16 | 0x10 | 写多个寄存器 | 写 | 多个保持寄存器 | 40001-49999 | 字(Word) | 123 | 
| 22 | 0x16 | 屏蔽写寄存器 | 写 | 保持寄存器(位操作) | 40001-49999 | 字(位掩码) | 1 | 
| 23 | 0x17 | 读/写多个寄存器 | 读写混合 | 保持寄存器 | 40001-49999 | 字(Word) | 121/121 | 
异常功能码
所有异常响应 = 功能码 + 0x80(如 0x03 → 0x83)
| 异常码 | 名称 | 说明 | 
|---|---|---|
| 0x01 | 非法功能码 | 设备不支持该功能 | 
| 0x02 | 非法数据地址 | 请求地址超出设备范围 | 
| 0x03 | 非法数据值 | 数据域值无效 | 
| 0x04 | 从站设备故障 | 设备执行失败 | 
| 0x05 | 确认 | 设备已接收但需长时间处理 | 
| 0x06 | 从站设备忙 | 设备暂时无法响应 | 
| 0x07 | 存储奇偶错误 | 设备存储器校验失败 | 
| 0x08 | 不可用网关路径 | 网关无法分配请求路径 | 
| 0x0A | 网关目标设备无响应 | 网关无法获取从站响应 | 
关键说明
- 
地址映射规则: - 所有Modbus地址在协议中转换为0-based地址(如40001 → 0x0000)
- 部分设备需要偏移(如40001 → 0x0001)
 
- 
数据打包规则: - 位操作:8位/字节(LSB优先)
- 字操作:大端序(高位在前)
 
- 
数量限制: - 读操作最大数量受协议限制(位操作2000,字操作125)
- 写操作数量受设备内存限制
 
- 
混合操作: - FC23(0x17)可同时读写寄存器,提升通信效率
 
ModbusRTU
01ModbusRTU读取线圈状态(多个位)
样例1
ModbusRTU协议按照下列模式发送报文
| 地址 | 功能码 | 起始地址高位 | 起始地址低位 | 读取线圈数量高位 | 读取线圈数量低位 | CRC16 校验码 | 
|---|---|---|---|---|---|---|
| 0x01 | 0x01 | 0x00 | 0x00 | 0x00 | 0x05 | 0xFC 0x09 | 
代表读取从站地址1的设备,地址从0开始的5个线圈的状态。
使用串口调试软件以Hex格式发送
010100000005FC09
收到反馈
01 01 01 0A D1 8F 
代表
| 字段 | 地址 | 功能码 | 接下来的数据部分字节数 | 十六进制的线圈状态 | CRC16校验 | 
|---|---|---|---|---|---|
| 值 | 01 | 01 | 01 | 0A | D1 8F | 
| 含义 | 从站1 | 读线圈 | 接下来的数据有1字节 | 0A=00001010:线圈 2``4为ON | CRC校验 | 

样例2
再试一次,读取多一点线圈数据。
发送
01 01 00 00 00 20 3D D2
收到响应
01 01 04 8A 10 02 00 D1 6C
分别代表,发送
| 地址 | 功能码 | 起始地址高位 起始地址低位 | 读取线圈数量高位 读取线圈数量低位 | CRC16 校验码 | 
|---|---|---|---|---|
| 0x01 | 0x01 | 0x00 0x00 | 0x00 0x20 | 0x3D 0xD2 | 
| 从站1 | 读多个线圈 | 从0号线圈开始 | 读十六进制的20个=16*2=30个线圈的值 | CRC16校验 | 
收到响应解释
| 字段 | 从站地址 | 功能码 | 字节数 | 数据1 | 数据2 | 数据3 | 数据4 | CRC 校验 | 
|---|---|---|---|---|---|---|---|---|
| 值 | 01 | 01 | 04 | 8A | 10 | 02 | 00 | D16C | 
| 含义(高位在前) | 从站地址为 1 | 读线圈(Read Coils) | 接下来的数据有 4 个字节(共 32 位) | 8A: 10001010 | 10: 00010000 | 02: 00000010 | 00: 00000000 | CRC16 校验码(两个字节) | 
线圈状态解析(按顺序)
| 线圈地址 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
|---|---|---|---|---|---|---|---|---|
| 状态 | OFF | ON | OFF | ON | OFF | OFF | OFF | ON | 
| 线圈地址 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 
| 状态 | OFF | OFF | OFF | OFF | ON | OFF | OFF | OFF | 
| 线圈地址 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 
| 状态 | OFF | OFF | OFF | OFF | OFF | OFF | ON | OFF | 
| 线圈地址 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 
| 状态 | OFF | OFF | OFF | OFF | OFF | OFF | OFF | OFF | 
串口调试软件截图

ModSim43截图

02ModbusRTU读取离散输入状态
报文格式与01读线圈类似
样例
| 地址 | 功能码 | 起始地址高位 起始地址低位 | 读取输入点数量高位 读取输入点数量低位 | CRC16 校验码 | 
|---|---|---|---|---|
| 0x01 | 0x02 | 0x00 0x00 | 0x00 0x05 | 0xB8 0x09 | 
| 从站1 | 读输入 | 起始输入地址0 | 读取5个 | CRC16校验 | 
使用串口调试助手发送
010200000005B809
收到响应
``·bash
01 02 01 09 61 8E
···
收到响应解释
| 字段 | 从站地址 | 功能码 | 字节数 | 数据 | CRC 校验 | 
|---|---|---|---|---|---|
| 值 | 01 | 02 | 01 | 09 | 61 8E | 
| 含义(高位在前) | 从站地址为 1 | 读离散输入( Discrete Inputs ) | 接下来的数据有 41个字节(共8位) | 09: 00001001 | CRC16 校验码(两个字节) | 
| 输入地址 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
|---|---|---|---|---|---|---|---|---|
| 状态 | ON | OFF | OFF | ON | OFF | OFF | OFF | OFF | 
串口调试软件截图

ModSim截图

由于发送的读取输入报文是读5个输入点的信号,所以可以看到,即使ModSim的输入6、7位都是ON,但收到的代表输入状态的值仍是09也就是00001001(LSB first 最低有效位在前)。
这里有一点需要注意,ModSim的寄存器或线圈的地址都是从1开始计数,而使用ModbusRTU读写命令的地址是从0开始,这一点需要进行匹配。也就是Modbus命令寄存器地址n对应Modsim的寄存器地址n+1。
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号