风过空庭,字句正徐来。
关于关于本站关于我给我点钱
更多时间线友链文件服务wiki
联系写留言发邮件GitHub
© 2024-2026 yono. | RSS 订阅 | 站点地图 | | Stay hungry. Stay foolish.
Powered by Mix Space&
白い
.
| 粤 ICP 备2024284785号-1 |
正在被0人看爆
纸白微明,未成篇章。

modbus 协议介绍

(已编辑)
415
AI·GEN

关键洞察

这篇文章上次修改于,可能部分内容已经不适用,如有疑问可询问作者。

modbus 协议介绍

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • 参考资料

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

    这里有 modbus 中文网的资料

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

    更权威的是官方文档

    MODBUS Application Protocol 1 1 b

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

    Modbus 规格和实施指南

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

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

    另外有我自己实现的库

    协议特性

    整体帧结构

    报文头 地址域 功能码 数据域 校验域
    RTU 1字节(从机 ID) 1字节 n 字节不定 2字节(对其他所有内容 CRC-MB16)
    TCP 6字节
    (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-20 0xCD
    输出数量高8位 0x00 输出状态 35-28 0x6B
    输出数量低8位 0x13 输出状态 38-36 0x05
    校验 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-197 0xAC
    输出数量高8位 0x00 输入状态 212-205 0xDB
    输出数量低8位 0x16 输入状态 218-213 0x35
    校验 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(一直到高位端)。

    线圈地址 27 26 25 24 23 22 21 20 — — — — — — 29 28
    对应值 1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1

    依据规范填充后,实际设置值段应当是 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-20 0xCD
    输出数量高8位 0x00 输出状态 35-28 0x6B
    输出数量低8位 0x13 输出状态 38-36 0x05

    其他协议不赘述了

    其他协议不赘述了

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