modbus 协议介绍

2024 年 7 月 26 日 星期五(已编辑)
234
AI 生成的摘要
这篇文章上次修改于 2025 年 1 月 3 日 星期五,可能部分内容已经不适用,如有疑问可询问作者。

modbus 协议介绍

参考资料

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

这里有 modbus 中文网的资料

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

更权威的是官方文档

MODBUS Application Protocol 1 1 b

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

Modbus 规格和实施指南

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

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

另外有我自己实现的库

协议特性

整体帧结构

报文头地址域功能码数据域校验域
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 校验👍。

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...