banner
yono

yono

哈喽~欢迎光临
follow
github

modbus 协议介绍

参考资料#

各博客网站都是缺章少句,排版混乱,偶有错误,不建议参考。

这里有 modbus 中文网的资料

MODBUS 协议中文版 / 英文版预览及下载 | Modbus 物联网云平台

更权威的是官方文档

MODBUS Application Protocol 1 1 b

以及官方其他文档,在这里寻找

Modbus 规格和实施指南

当然官方文档我也不是很满意,为了保持他们的远古设计架构,各个帧段的介绍不直观了,同时还有少量笔误。

image

下面有自己重写的有效信息。

另外有我自己实现的库

stbanana/modbusX: modbus protocol support (github.com)

协议特性#

整体帧结构#

报文头地址域功能码数据域校验域
RTU1 字节 (从机 ID)1 字节n 字节不定2 字节 (对其他所有内容 CRC-MB16)
TCP6 字节
(2 字节事务号 +
2 字节协议标识 - 就是全 0+
2 字节后续字节总长 - 包括从机 ID)
1 字节 (从机 ID)1 字节n 字节不定

关于报文头,在官方文档的总体模型中划分到地址域的,但实际上的字节总长又包括从机 ID。大概是早期只有串口协议时,整体模型已经定下了,新加的 modbusTCP 只能为了兼容乱搞,

我这里直接重新归类好了,不标准,但我乐意。

寄存器属性#

RW 属性bit 位数
线圈 (0x01)可读写1 位 bit
离散输入寄存器 (0x02)只读1 位 bit
保持寄存器 (0x03)可读写16 位 bit
输入寄存器 (0x04)只读16 位 bit

整体模型#

使用软件抽象一个内存结构,每个地址存有不同意义的数据,和 FPGA 模拟 SRAM 是一个道理。不过每个寄存器地址不都是 16 位,也可以是 1 位。属性是 RO\RW。

RTU 系列#

(0x01) 读线圈#

主机请求#

地址域功能码起始地址线圈数量校验域
1 字节1 字节2 字节2 字节2 字节 (CRC-MB16)

从机响应#

地址域功能码字节数线圈状态校验域
1 字节1 字节1 字节 (计算线圈状态部分的字节数)n 字节2 字节 (CRC-MB16)

示例#

请求读 20~38 地址的线圈数据,总之回复地址是由低到高,最终字节不齐在高位填 0。

[!NOTE]

最终的输出状态 38-36 回复字节,用零填充 5 个剩余 bit(一直到高位端)。

请求响应
地址域 (从机 ID)0x01地址域 (从机 ID)0x01
功能码0x01功能码0x01
起始地址高 8 位0x00字节数0x03
起始地址低 8 位0x13输出状态 27-200xCD
输出数量高 8 位0x00输出状态 35-280x6B
输出数量低 8 位0x13输出状态 38-360x05
校验 CRC 低 8 位0xA9校验 CRC 低 8 位0x42
校验 CRC 高 8 位0xC8校验 CRC 高 8 位0x82

(0x02) 读离散输入寄存器#

主机请求#

地址域功能码起始地址离散输入数量校验域
1 字节1 字节2 字节2 字节2 字节 (CRC-MB16)

从机响应#

地址域功能码字节数离散输入状态校验域
1 字节1 字节1 字节 (计算离散输入状态部分的字节数)n 字节2 字节 (CRC-MB16)

示例#

请求读 197~218 地址的离散输入寄存器数据,总之回复地址是由低到高,最终字节不齐在高位填 0。

[!NOTE]

最终的输入状态 218-213 回复字节,用零填充 2 个剩余 bit(一直到高位端)。

请求响应
地址域 (从机 ID)0x01地址域 (从机 ID)0x01
功能码0x02功能码0x02
起始地址高 8 位0x00字节数0x03
起始地址低 8 位0xC4输入状态 204-1970xAC
输出数量高 8 位0x00输入状态 212-2050xDB
输出数量低 8 位0x16输入状态 218-2130x35
校验 CRC 低 8 位0xB8校验 CRC 低 8 位0x22
校验 CRC 高 8 位0x39校验 CRC 高 8 位0x88

(0x03) 读保持寄存器#

主机请求#

地址域功能码起始地址保持寄存器数量校验域
1 字节1 字节2 字节2 字节2 字节 (CRC-MB16)

从机响应#

地址域功能码字节数保持寄存器状态校验域
1 字节1 字节1 字节 (计算保持寄存器状态部分的字节数)n 字节2 字节 (CRC-MB16)

示例#

请求读 108~110 地址的保持寄存器数据

请求响应
地址域 (从机 ID)0x01地址域 (从机 ID)0x01
功能码0x03功能码0x03
起始地址高 8 位0x00字节数0x06
起始地址低 8 位0x6B寄存器值高八位(108)0x02
寄存器数量高 8 位0x00寄存器值低八位(108)0x2B
寄存器数量低 8 位0x03寄存器值高八位(109)0x00
校验 CRC 低 8 位0x74寄存器值低八位(109)0x00
校验 CRC 高 8 位0x17寄存器值高八位(110)0x00
寄存器值低八位(110)0x64
校验 CRC 低 8 位0x05
校验 CRC 高 8 位0x7A

(0x04) 读输入寄存器#

主机请求#

地址域功能码起始地址输入寄存器数量校验域
1 字节1 字节2 字节2 字节2 字节 (CRC-MB16)

从机响应#

地址域功能码字节数输入寄存器状态校验域
1 字节1 字节1 字节 (计算输入寄存器状态部分的字节数)n 字节2 字节 (CRC-MB16)

示例#

请求读 9~10 地址的保持寄存器数据

请求响应
地址域 (从机 ID)0x01地址域 (从机 ID)0x01
功能码0x04功能码0x04
起始地址高 8 位0x00字节数0x06
起始地址低 8 位0x6B寄存器值高八位(9)0x02
寄存器地址高 8 位0x00寄存器值低八位(9)0x2B
寄存器地址低 8 位0x03寄存器值高八位(10)0x00
校验 CRC 低 8 位0xC1寄存器值低八位(10)0x00
校验 CRC 高 8 位0xD7校验 CRC 低 8 位0xF3
校验 CRC 高 8 位0xF4

(0x05) 写单个线圈#

[!NOTE]

写单个线圈,输出值部分仅允许 FF 00 表示 ON、00 00 表示 OFF,其他值都不合法

主机请求#

地址域功能码输出地址输出值校验域
1 字节1 字节2 字节2 字节2 字节 (CRC-MB16)

从机响应#

地址域功能码地址输出值校验域
1 字节1 字节2 字节2 字节2 字节 (CRC-MB16)

示例#

请求写 173 地址的线圈数据为 ON

请求响应
地址域 (从机 ID)0x01地址域 (从机 ID)0x01
功能码0x05功能码0x05
寄存器地址高 8 位0x00寄存器地址高 8 位0x00
寄存器地址低 8 位0xAC寄存器地址低 8 位0xAC
寄存器值高 8 位0xFF寄存器值高 8 位0xFF
寄存器值低 8 位0x00寄存器值低 8 位0x00
校验 CRC 低 8 位0x4C校验 CRC 低 8 位0x4C
校验 CRC 高 8 位0x1B校验 CRC 高 8 位0x1B

(0x06) 写单个保持寄存器#

主机请求#

地址域功能码保持寄存器地址寄存器值校验域
1 字节1 字节2 字节2 字节2 字节 (CRC-MB16)

从机响应#

地址域功能码保持寄存器地址寄存器值校验域
1 字节1 字节2 字节2 字节2 字节 (CRC-MB16)

示例#

请求写 2 地址的保持寄存器数据为 0x0003

请求响应
地址域 (从机 ID)0x01地址域 (从机 ID)0x01
功能码0x06功能码0x06
寄存器地址高 8 位0x00寄存器地址高 8 位0x00
寄存器地址低 8 位0x02寄存器地址低 8 位0x02
寄存器值高 8 位0x00寄存器值高 8 位0x00
寄存器值低 8 位0x03寄存器值低 8 位0x03
校验 CRC 低 8 位0x2C校验 CRC 低 8 位0x2C
校验 CRC 高 8 位0x0B校验 CRC 高 8 位0x0B

(0x0F) 写多个线圈#

主机请求#

地址域功能码起始地址设置数量字节数设置值校验域
1 字节1 字节2 字节2 字节1 字节 (计算设置值部分的字节数)n 字节2 字节 (CRC-MB16)

从机响应#

地址域功能码起始地址设置数量校验域
1 字节1 字节2 字节2 字节2 字节 (CRC-MB16)

示例#

请求写 20 地址开始的 10 个线圈

[!NOTE]

共需写入 2 字节 (16bit),用零填充 6 个剩余 bit(一直到高位端)。

线圈地址27262524232221202928
对应值1100110100000001

依据规范填充后,实际设置值段应当是 0xCD 0x01

请求响应
地址域 (从机 ID)0x01地址域 (从机 ID)0x01
功能码0x0F功能码0x0F
起始地址高 8 位0x00起始地址高 8 位0x00
起始地址低 8 位0x13起始地址低 8 位0x13
设置数量高 8 位0x00设置数量高 8 位0x00
设置数量低 8 位0x0A设置数量低 8 位0x0A
字节数0x02校验 CRC 低 8 位0x24
设置值 (27~20 地址)0xCD校验 CRC 高 8 位0x09
设置值 (29~28 地址)0x01
校验 CRC 低 8 位0x72
校验 CRC 高 8 位0xCB

(0x10) 写多个保持寄存器#

主机请求#

地址域功能码起始地址设置数量字节数设置值校验域
1 字节1 字节2 字节2 字节1 字节 (计算设置值部分的字节数)n 字节2 字节 (CRC-MB16)

从机响应#

地址域功能码起始地址设置数量校验域
1 字节1 字节2 字节2 字节2 字节 (CRC-MB16)

示例#

请求写 34 地址开始的 4 个保持寄存器

请求响应
地址域 (从机 ID)0x01地址域 (从机 ID)0x01
功能码0x10功能码0x10
起始地址高 8 位0x00起始地址高 8 位0x00
起始地址低 8 位0x22起始地址低 8 位0x22
设置数量高 8 位0x00设置数量高 8 位0x00
设置数量低 8 位0x04设置数量低 8 位0x04
字节数0x08校验 CRC 低 8 位0x61
设置值高 8 位 (34 地址)0x00校验 CRC 高 8 位0xC0
设置值低 8 位 (34 地址)0x40
设置值高 8 位 (35 地址)0x00
设置值低 8 位 (35 地址)0x24
设置值高 8 位 (36 地址)0x00
设置值低 8 位 (36 地址)0x01
设置值高 8 位 (37 地址)0xBF
设置值低 8 位 (37 地址)0x52
校验 CRC 低 8 位0x5F
校验 CRC 高 8 位0xCC

异常响应帧#

地址域功能码异常码校验域
1 字节1 字节 (功能码 + 0x80)1 字节2 字节 (CRC-MB16)
异常码名称含义
0x01非法功能码接收到的请求指令功能码是不可允许的操作。可能是功能码在其中不被支持,也可能是其正处于错误状态中处理请求。
0x02非法数据地址询问中接收到的数据地址是不可允许的地址。特别是,起始地址和传输长度的组合是无效的。对于带有 100 个寄存器的控制器来说,带有起始地址 96 和长度 4 的请求会成功,带有起始地址 96 和长度 5 的请求将产生异常码 0x02。
0x03非法数据值实际上是数据段非法的意思。例如非法数据段长度,或者写入或者读取的寄存器数量和数据段不匹配。注意的是,这并不代表寄存器被写入一个期望范围以外的值、实际写入失败 (这种情况是 0x04)。
0x04从站设备故障当服务器 (或从站),对寄存器执行请求的操作时出现差错,例如寄存器被写入一个期望范围以外的值等。
0x05确认其实并非错误,而是收到长耗时指令,表明已收到并开始处理.
0x06从属设备忙正在处理耗时命令在忙。(当从机空闲后,应当重发引起此错误的请求)
0x08存储奇偶性差错设法读取记录文件,但是在存储器中发现一个奇偶校验错误。
0x0A不可用网关路径与网关一起使用,指示网关不能为处理请求分配输入端口至输出端口的内部通信路径。通常意味着网关是错误配置的或过载的。
0x0B网关目标设备响应失败与网关一起使用,指示没有从目标设备中获得响应。通常意味着设备未在网络中。

TCP 系列#

总之 modbusTCP 相比于 RTU 只是包装了前面的报文头,同时去除了 CRC 校验👍。== 因为 TCP 管理层的链路传输已经很稳健了,而串口传输是不稳健的 ==

报文头中的事务号会在主机 (TCP 客户端) 发起请求时不断累加,从机 (TCP 服务器端) 响应请求会回复相同的事务号表示处理的是什么请求。所以 modbusTCP 是天生支持多帧连发的,主机不会因此误解回复👍。

个人是更喜欢这样流畅的通信协议的。|| 信息准确性什么的交给物理链路层就好了,大雾大雾,校验还是有必要的 ||

(0x01) 读线圈#

主机请求#

事务号协议标识字节总长地址域功能码起始地址线圈数量
2 字节2 字节 (全 0)2 字节 (后续字节总长)1 字节1 字节2 字节2 字节

从机响应#

事务号协议标识字节总长地址域功能码字节数线圈状态
2 字节2 字节 (全 0)2 字节 (后续字节总长)1 字节1 字节1 字节 (计算线圈状态部分的字节数)n 字节

示例#

请求读 20~38 地址的线圈数据,总之回复地址是由低到高,最终字节不齐在高位填 0。

[!NOTE]

最终的输出状态 38-36 回复字节,用零填充 5 个剩余 bit(一直到高位端)。

请求响应
事务号高 8 位0x00事务号高 8 位0x00
事务号低 8 位0x01事务号低 8 位0x01
协议标识 16 位 (全 0)0x00 0x00协议标识 16 位 (全 0)0x00 0x00
字节总长高 8 位0x00字节总长高 8 位0x00
字节总长低 8 位0x06字节总长低 8 位0x06
地址域 (从机 ID)0x01地址域 (从机 ID)0x01
功能码0x01功能码0x01
起始地址高 8 位0x00字节数0x03
起始地址低 8 位0x13输出状态 27-200xCD
输出数量高 8 位0x00输出状态 35-280x6B
输出数量低 8 位0x13输出状态 38-360x05

其他协议不赘述了

其他协议不赘述了

总之 modbusTCP 相比于 RTU 只是包装了前面的报文头,同时去除了 CRC 校验👍。

此文由 Mix Space 同步更新至 xLog
原始链接为 https://www.yono233.cn/posts/shoot/24_7_26_modbus


加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。