1、简介
Modbus是一种串行通信协议,是Modicon于1979年,为使用可编程逻辑控制器(PLC)而发表的。MODBUS是工业领域通信协议的业界标准,并且现在是工业电子设备之间相当常用的连接方式。
2、OSI7层模型
层 | ISO/OSI模型 | |
---|
7 | 应用层 | Modbus协议 |
6 | 表示层 | NULL |
5 | 会话层 | NULL |
4 | 传输层 | NULL |
3 | 网络层 | NULL |
2 | 数据链路层 | Modbus串行链路协议 |
1 | 物理层 | RS485/RS422/RS232 |
3、MODBUS帧格式
ADU:应用数据单元
PDU:简单协议数据单元
modbus帧最大为256字节
图1 modbus帧格式
modbus是big-Endian
大端模式,发送数据的时候高位在前,如发送0x1234时,先发送0x12再发送0x34
modbus事务正常处理流程
图2 modbus正常请求数据
图3 modbus正常请求数据异常
4、功能码
01:读线圈(继电器状态)
请求ADU
站号 | 功能码 | 起始地址H | 起始地址L | 线圈数量H | 线圈数量L |
---|
1Byte | 0x01 | 0x00~FF | 0x00~FF | 0x00~07 | 0x01~7D |
线圈数量范围是1~2000(0x0001 ~ 0x07d0)
返回ADU
站号 | 功能码 | 字节数 | 线圈状态 |
---|
1Byte | 0x01 | N* | N或N+1个字节 |
*N=输出数量/8,如果余数不等于 0,那么N = N+1
错误
站号 | 功能码 | 异常码 |
---|
1Byte | 0x81 | 01 或 02 或 03 或 04 |
异常码:
图 4modbus异常处理
- 01 不支持该功能码
- 02 起始地址不正确或起始地址+请求数量超过范围
- 03 输出数量不在范围内
- 04 读取离散量失败
例子:请求离散量输出20~38
图 5读取20~38线圈实例
请求报文的起始地址是0x0013(19),数组是从0开始的所以是低20个线圈。然后请求19个线圈状态,19/8=16余3
故返回2个正常字节,最后一字节0000 0101
搞5位是用0填充的。
02:读输入离散量(输入开关量)
与功能码01的区别,02读取的是物理输入开关量,对应PLC则是X输入的值。01可以是内部的寄存器也可以是物理线圈状态。
请求ADU
站号 | 功能码 | 起始地址H | 起始地址L | 输入离散量数量H | 输入离散量数量L |
---|
1Byte | 0x02 | 0x00~FF | 0x00~FF | 0x00~07 | 0x01~D0 |
输入离散量数量范围是1~2000(0x0001 ~ 0x07d0)
返回ADU
站号 | 功能码 | 字节数 | 输入离散量状态 |
---|
1Byte | 0x02 | N* | N或N+1个字节 |
*N=输出数量/8,如果余数不等于 0,那么N = N+1
错误
站号 | 功能码 | 异常码 |
---|
1Byte | 0x82 | 01 或 02 或 03 或 04 |
03:读保持寄存器
请求ADU
站号 | 功能码 | 起始地址H | 起始地址L | 保持寄存器数量H | 保持寄存器数量L |
---|
1Byte | 0x03 | 0x00~FF | 0x00~FF | 0x00~00 | 0x01~7D |
保持寄存器数量 1 至 125(0x007D)
返回ADU
站号 | 功能码 | 字节数 | 保持寄存器状态 |
---|
1Byte | 0x03 | 2×N* | N*×2字节 |
*N=寄存器的数量
错误
站号 | 功能码 | 异常码 |
---|
1Byte | 0x83 | 01 或 02 或 03 或 04 |
例子读寄存器108~110
04:读输入寄存器
请求ADU
站号 | 功能码 | 起始地址H | 起始地址L | 读输入寄存器H | 读输入寄存器L |
---|
1Byte | 0x04 | 0x00~FF | 0x00~FF | 0x00~00 | 0x00~7D |
保持寄存器数量 1 至 125(0x007D)
返回ADU
站号 | 功能码 | 字节数 | 输入寄存器状态 |
---|
1Byte | 0x04 | 2×N* | N*×2字节 |
*N=输入寄存器的数量
错误
站号 | 功能码 | 异常码 |
---|
1Byte | 0x84 | 01 或 02 或 03 或 04 |
05:写单个线圈
请求ADU
站号 | 功能码 | 线圈地址H | 线圈地址L | 输出值H | 输出值L |
---|
1Byte | 0x05 | 0x00~FF | 0x00~FF | | |
输出值为0xFF00为ON,输出0x0000为OFF
返回ADU
站号 | 功能码 | 输出地址H | 输出地址L | 输出值H | 输出值L |
---|
1Byte | 0x05 | 0x00~FF | 0x00~FF | | |
输出值为0xFF00为ON,输出0x0000为OFF
错误
站号 | 差错码 | 异常码 |
---|
1Byte | 0x85 | 01 或 02 或 03 或 04 |
06:写单个寄存器
请求ADU
站号 | 功能码 | 寄存器地址H | 寄存器地址L | 寄存器值H | 寄存器值L |
---|
1Byte | 0x06 | 0x00~FF | 0x00~FF | 0x00~FF | 0x00~FF |
返回ADU
站号 | 功能码 | 寄存器地址H | 寄存器地址L | 寄存器值H | 寄存器值L |
---|
1Byte | 0x06 | 0x00~FF | 0x00~FF | 0x00~FF | 0x00~FF |
错误
站号 | 差错码 | 异常码 |
---|
1Byte | 0x86 | 01 或 02 或 03 或 04 |
0F:写多个线圈
请求ADU
站号 | 功能码 | 线圈起始地址H | 线圈起始地址L | 输出数量H | 输出数量L | 字节数 | 输出值 |
---|
1Byte | 0x0F | 0x00~FF | 0x00~FF | 0x00~07 | 0x01~B0 | N* | N字节 |
N*=输出数量/8,如果余数不等于 0,那么N = N+1
输出数量范围0x0001~0x07B0
返回ADU
站号 | 功能码 | 输出地址H | 输出地址L | 输出数量H | 输出数量L |
---|
1Byte | 0x0F | 0x00~FF | 0x00~FF | 0x00~07 | 0x01~B0 |
错误
站号 | 差错码 | 异常码 |
---|
1Byte | 0x8F | 01 或 02 或 03 或 04 |
10:写多个寄存器
请求ADU
站号 | 功能码 | 寄存器起始地址H | 寄存器起始地址L | 寄存器数量H | 寄存器数量L | 字节数 | 寄存器值 |
---|
1Byte | 0x10 | 0x00~FF | 0x00~FF | 0x00 | 0x01~7B | 2×N* | 2×N*Byte |
*N=寄存器数量
支持写1~123
返回ADU
站号 | 功能码 | 起始地址H | 起始地址L | 寄存器数量H | 寄存器数量L |
---|
1Byte | 0x10 | 0x00~FF | 0x00~FF | 0x00 | 0x01~7B |
错误
站号 | 差错码 | 异常码 |
---|
1Byte | 0x90 | 01 或 02 或 03 或 04 |
5、CRC校验
基于循环冗余校验 (CRC - Cyclical RedundancyChecking) 算法的错误检验域。CRC 域检验整个报文的内容。CRC 域作为报文的最后的域附加在报文之后。计算后,首先附加低字节,然后是高字节。CRC高字节为报文发送的最后一个子节。
6、帧间隔
3.5个字符报文的间隔
7、参考文献
安富莱电子发布Modbus教程
NI