modbus 协议笔记

1. 一些参考资料

这个是基本科普,一些专业名词和术语仅供参考,欢迎批评讨论。
比较好的资料参考:

  1. 视频:非常正统的 Modbus 协议介绍,如果能仔细看完那你绝对是 Modbus 协议方面的 dalao 了
    https://www.bilibili.com/video/BV1HE411j7z1?p=1&vd_source=f2ff02052a611dc0675fd173b01c047b
  2. 结合抓包软件 WireShark 对 Modbus-TCP 协议进行分析
    可以直观的感受以下真实的协议收发时的内容
    https://www.cnblogs.com/Db2k/p/12373388.html#_label0
  3. 视频:基于 C# 的 Modbus 协议的上位机驱动开发
    个人感觉内容清晰,节奏紧凑,能开发出驱动对协议基本就算是毕业了
    (因为协议还会涉及到差错控制、时序控制、状态转换等小细节)
  4. 视频:西门子 S7-1200 和 S7-1500 使用 Modbus 主站、从站功能进行通信
    这几个视频说的比较简洁,可以看看,里边有相关的寄存器、寄存器地址介绍
    https://www.bilibili.com/video/BV1BT411d78r/
    https://www.bilibili.com/video/BV1bg411S7cc/
  5. 各个功能码发送时的内容介绍
    有一些小地方介绍的有点模糊,适合了解了 Modbus 协议后做工具书快速查询
    https://blog.csdn.net/dengjin20104042056/article/details/116545046

2. 数据发送与接收者 – 服务端 / 客户端

image.png

  1. 服务端 (Server):下位机器,被动响应发送者的请求,将自身数据返回给客户端
  2. 客户端 (Client):数据接受者 – 上位监控设备,主动向发送者通信,要求获取对应数据
  3. 因此 modbus 是典型的主从架构的通信协议
    主机(Master)= 客户端,从机(Slave)= 服务端
    主机不问从机,从机就不会吭声 --> modbus 主机需要不断“轮询”(轮流询问)各个设备以不断更新数据

3. 数据传输的方式(modbus协议分类)

有多种数据传输方式,下面介绍最基本的两种:通过 485 传输,通过网络传输

3.1 通过 RS-485 进行传输


RS-485 是一种“物理层”的规定:只规定了接口的物理特性(如接口、接线、电压、速率、高低电平含义、传输距离等),更上层的特性(具体传输的数据、对传输数据的处理等)没有规定。
同理,RS-232 也是一种“物理层”的接口。
RS-232RS-485 统称“串口(Serial Port)
工业中常用 RS-485 传输,下面是 RS-485 在实际连接中的一些注意事项

  1. 通常用双绞线,头尾设备要加终端电阻
  2. 可以用菊花链的形式连接多台设备,标准规定最多 32 台设备(实际有一些芯片经过特殊设计能连接更多)
  3. 双绞线上只能接使用 modbus 485 协议的设备,不能和其他协议混用
  4. 串口传输数据,要规定波特率(速率)、数据位、校验位校验方法、停止位
    image.png
  5. 传输的数据有两种编码形式:ASCII 和 RTU --> 形成两种不同的 modbus 协议类型
  6. 传输的内容
    image.png

3.1.1 MODBUS-ASCII

使用 串口 物理层协议的、数据编码形式为 ASCII 的 modbus协议。

什么是编码形式?可理解为方言
如:两个中国人,都说普通话(两种 modbus 协议传输的具体内容都相同),但是一个人是北京人讲的是京腔(一个用 ASCII 编码),一个是广东人讲的是塑料普通话(一个用的是 二进制编码)

  • 发送端:利用 ASCII 编码将要发送的数据打包
  • 接收端:根据 ASCII 编码的规则将收到的内容解码
  • ASCII 码举例(原数据 <–> ASCII 编码后的数据)
    ASCII 码表:https://baike.baidu.com/item/ASCII/309296#%E4%BA%A7%E7%94%9F%E5%8E%9F%E5%9B%A0
    • 数字 0,1 至 9 <–> 十六进制 0x30 至 0x39
    • 英文小写字母 a-z <–> 十六进制 0x61 至 0x7A (这里只是举例子,modbus 协议只会传数字)
    • 英文大写字母 a-z <–> 十六进制 0x61 至 0x7A (这里只是举例子,modbus 协议只会传数字)
  • 可看到,ASCII 编码后的数据,每个字符(英文字母、数字、英文符号)都占一个字节,因此使用 ASCII 传输数据效率较低,但好处是计算机接收到数据后可直接打印出对应的报文内容

3.1.2 MODBUS-RTU

使用 串口 物理层协议的、数据编码形式为 二进制 的 modbus协议。
二进制编码:实际上就是没有编码,数字转换成二进制怎么显示就怎么传输

  • 一个字节可表示:数字 0 至 255 <–> 十六进制 0x00 至 0xFF
  • 两个字节可表示:数字 0 至 65535 <–> 十六进制 0x0000 至 0xFFFF
  • 二进制 RTU 形式传输的效率比 ASCII 高:如果用 ASCII 码传输数字“255”实际需要传输“0x32 0x35 0x35”,而用二进制形式传输只需要传输“0xFF”即可

3.2 通过网络(以太网)进行传输 – modbus-TCP

以太网是基于 TCP/IP 协议的(物理层、网络层、传输层都定义了),需要用 TCP/IP 协议打包要传输的数据(modbus TCP 中规定的数据)。由于使用了 TCP/IP 协议,这种协议是当今互联网的实通用标准,所以一般支持网络的工业设备一般都支持 modbus-TCP 协议,且设备还能传输其他数据,与 modbus-TCP 协议互不影响
image.png

  1. 使用标准的网线(无线网理论上也行,只要是能连互联网的网络和设备都行)
  2. 通过交换机、路由器等连接各个设备
  3. 网线上还可以传输其他使用 TCP/IP 协议的数据
  4. 传输的数据的编码:modbus-TCP 强制规定使用 二进制 方法(与 Modbus-RTU 相同)对数据进行编码
  5. TCP 协议传输的内容(与 Modbus-ASCII / Modbus-RTU 对比)
    image.png

3.3 其他种类 & 总结

实际上就是传输介质(485 / 以太网)+ 编码方式的组合

  • 传输介质:串口(485 / 232)、网络(以太网,包括 UDP / TCP)
  • 编码方式:ASCII 或 RTU(二进制不编码)
  • 转发:网络只负责将 Modbus-RTU / ASCII 协议进行传送,协议的编解码遵循 RTU / ASCII 标准而不是 Modbus-TCP 标准
    MODBUS-转发.png

以下是 Modbus 协议的分类(常见的用红色加粗进行标识)

编码方式
传输介质
二进制ASCII 码
网线(以太网)
两种传输协议
TCP / UDP
Modbus-TCP
Modbus-UDP
TCP 转发 RTU:TCP上的 Modbus-RTU
UDP 转发 RTU:UDP上的 Modbus-RTU



TCP 转发 ASCII:TCP上的 Modbus-ASCII
UDP 转发 ASCII:UDP上的 Modbus-RTU
串口
485 / 232
Modbus-RTU
(串口不能转发TCP/UDP)
Modbus-ASCII
(串口不能转发TCP/UDP)

4. Modbus 对数据类型的规定

  1. Modbus 协议规定了基本的数据类型,对应工控设备中常见的数据类型(AI/AO/DI/DO)
    image.png
  2. 不同类型的数据分开存放,通过功能码区分要操作的数据类型
  3. 每种数据存储于“寄存器中”,通过“寄存器地址”指定要操作的具体数据
    每种数据的寄存器地址都是 0x0000 至 0xFFFF (即十进制的 0 到 65535)
  4. 由于 Modbus 是“主从”结构的协议,因此需要以客户端 Client(主机 Master)的角度对协议报文进行分析

5. Modbus 的功能码

Modbus 功能码,决定了主机要向从机:

  • 进行什么样的操作:读 / 写
  • 操作什么类型的数据:AI / AO / DI / DO

功能码在串口(Modbus-ASCII 或 Modbus-RTU)、以太网(Modbus-TCP 或 Modbus-UDP)中都相同
image.png
在 Modbus 报文中,使用:【功能码】+【寄存器地址】指定要操作的数据
如:读寄存器地址为 0x01F0 的 DI 量 – 功能码 = 2 = 0x02,寄存器地址 = 0x1F0 = 496

6. Modbus 协议中的的数据部分

在实际发送的 Modbus 报文中,数据部分包含的内容如下:操作的寄存器起始地址、操作的数量、数据等

6.1 报文发送的顺序

  1. 从寄存器地址低到高发送数据。如发送地址为 0x22 - 0x24 的数据,则发送顺序先后为:0x22 0x23 0x24
  2. 同一寄存器的数据(有 2 Byte = 16 bit)。先发送高字节(高 8 bit),再发送低字节(低 8 bit)

6.2 请求报文(主机发出)

从“起始地址”开始,读 / 写连续 X 个数量的地址中的数据
image.png

6.2.1 要读 / 写的数据寄存器起始地址 (0x0000 – 0xFFFF)

由前几节的介绍可知,在 Modbus 标准中:

  • 用功能码区别不同类型(AI / AO / DI / DO)的数据、对数据的操作(读 / 写)
  • 不同类型的数据有独立的存储区间,称为【寄存器地址】
    每种类型的数据【寄存器地址】范围都是 0x0000 到 0xFFFF(十进制的 0 到 65535)

6.2.2 常说的 PLC 地址是什么?

  1. 在一些设备的手册中,对使用 Modbus 协议时的配置,有类似以下的描述
    image.pngimage.pngimage.png
  2. 这里的【数据地址 0XXXX 1XXXX 3XXXX 4XXXX】实际上只是一些设备厂商(如西门子)自身的规定,后边形成了一种行业约定速成
    目的:使用 PLC 等设备中的寄存器地址对要操作的数据类型进行区分,以便根据数据类型使用不同的 Modbus 功能码传送数据。使用户只需要保证数据类型正确即可,无需关心具体操作对应的功能码,简化了协议使用的难度。
    1. 厂商规定的数据地址 = 寄存器/数据类型标识(0/1/3/4) + 寄存器地址(XXXX)
    2. 实际传输的报文 = 根据类型标识转换而成的功能码(0x01/0x02/0x04/0x03) + 寄存器地址(XXXX)
  3. 这里的【寄存器地址的范围】设定为 0-9999
    因为:有一些老旧设备中的寄存器地址最高只支持 5 位(最高位作为数据类型标识使用,则只能用剩下 4 位表示地址,因此只能用 0-9999 这 4 位)
    但实际上以 Modbus 标准表示:0-9999 = 0-09999 --> 传输的地址以十进制表示 = 00000 – 09999
  4. 这里寄存器的【起始地址】为 0001 而不是 0x0000(十进制 00000)
    这个是沿用一些 PLC 厂商(如西门子)的规定,在 PLC 的 Modbus 地址标准中,每一类寄存器的起始地址为 1 而不是 0
    PLC 标准的 Modbus 寄存器地址(XXXX) = 标准 Modbus 地址 + 1
  5. 总结:两者的对应关系
    image.png
    实际 Modbus 协议报文中的内容 = 功能码 + 寄存地址

6.2.3 要读 / 写的数据长度

注意这里不是 Byte 或 Word 个数,而是对应功能码对应的数据类型的变量个数:离散输入个数 / 线圈个数 / 寄存器个数

  1. 线圈 (DO) / 离散量输入(DI)
    每个地址表示一个 DI /DO 量的状态。如,要读 / 写 10 个寄存器,则数据长度 = 10 = 0x0A
  2. 输入寄存器 (DI) / 保持寄存器 (DO) – 每个地址包含 16bit 的数据
    1. INT (16 Bit) – 每个变量占一个地址
    2. FLOAT (32 Bit) – 每个变量占2个地址,读 / 写时用低位地址表示
    3. DOUBLE (64 Bit) – 每个变量占 4 个地址,读 / 写时用低位地址表示

6.2.4 (仅主机写)写的数据

  1. 线圈 (DO) / 离散量输入(DI)
    1. 一个字节 = 8 bit = 8个寄存器的数据
    2. 实际传输以字节为单位,传输数据字节数 = 要传输的寄存器数 / 8
      如果有余数则寄存器数量 + 1,剩下的位数用 0 补足。
    3. 如要传输 10 个寄存器的数据,数据为 0b1101001001 = 0x03 0x49 <-- 传输这两个值
  2. 输入寄存器 (DI) / 保持寄存器 (DO) – 每个地址包含 16bit 的数据
    1. INT (16 Bit) – 每个变量占一个地址
    2. FLOAT (32 Bit) – 每个变量占 2 个地址,读 / 写时用最低位地址表示
      如:一个 FLOAT 类型的数据,占用了从 0x0028 - 0x002A 两个连续的地址,则读 / 写此变量时,地址 = 0x0028
    3. DOUBLE (64 Bit) – 每个变量占 4 个地址,读 / 写时用最低位位地址表示
      如:一个 DOUBLE 类型的数据,占用了从 0x0028 - 0x002C 四个连续的地址,则读 / 写此变量时,地址 = 0x0028

6.3 响应报文(从机返回)

image.png

6.3.1 返回的数据长度(字节数)

返回的数据字节数

6.3.2 返回的对应地址的数据

根据返回的数据类型不同,字节数的计算方法有差异(同请求报文 - (仅主机写)写的数据

  1. 线圈 (DO) / 离散量输入(DI)
    1. 一个字节 = 8 bit = 8个寄存器的数据
    2. 实际传输以字节为单位,传输数据字节数 = 要传输的寄存器数 / 8
      如果有余数则寄存器数量 + 1,剩下的位数用 0 补足。
    3. 如要传输 10 个寄存器的数据,数据为 0b1101001001 = 0x03 0x49 <-- 传输这两个值
  2. 输入寄存器 (AI) / 保持寄存器 (AO) – 每个地址包含 16bit 的数据
    1. INT (16 Bit) – 每个变量占一个地址
    2. FLOAT (32 Bit) – 每个变量占 2 个地址,读 / 写时用最低位地址表示
      如:一个 FLOAT 类型的数据,占用了从 0x0028 - 0x002A 两个连续的地址,则读 / 写此变量时,地址 = 0x0028
    3. DOUBLE (64 Bit) – 每个变量占 4 个地址,读 / 写时用最低位位地址表示
      如:一个 DOUBLE 类型的数据,占用了从 0x0028 - 0x002C 四个连续的地址,则读 / 写此变量时,地址 = 0x0028
  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值