Modbus RTU简介(上)

1 前言

1.1 什麽是Modbus

  • Modbus是一种用於工业控制的标准通信协议,它定义了装置之间在应用层的消息封装模式、沟通方法、沟通顺序。
  • Modbus的优势 :
    1. Modbus协议是开源免费的
    2. Modbus支援多种常见工控接口(RS232, RS485, TCP/IP etc.),且可以各种物理层装置上传输(双绞线、光纤、无线等)
    3. Modbus格式简单易於开发
  • Modbus能够将众多设备连接到DCS, PLC系统上,再利用服务器(云、中央计算机)进行监控与下达指令,以便於集中式控制。
    1-1 Modbus在工业上的应用

1.2 Modbus的通讯

  • Modbus通信协议是一个(master/slave)架构,或者(client/server)架构,在整个通讯网路中能够同时拥有多个slaves但是只允许拥有一个专门发送request的master。
  • Modbus是一个一主多从协议(最多可连接247台slave,所以slave地址范围在1~247之间),只有在master发出request时slave才会做出回应,slaves之间不能沟通。
  • 每条master所发出的request,都包含一个slave地址,每台slave都会收到该request,都只有符合该地址的slave才会回应该请求,如图1-1(地址0是广播模式,每台slave都会运行指令但不会回应master)。
1-2 Modbus的应答过程

  • ADU(Application Data Unit)是完整的协议传输封包而PDU(Protocol Data Unit)则是资料传输的基础单元,与通信无关,如图1-2所示。
1-3 通用Modbus封包格式

1.3 Modbus的错误通讯

当master传送的request不符合格式、slave不支援此功能等问题时,代表slave不能正确解码request内容,这时slave就会response一个error response,其组成为[slave ID][error code][exception code]

  • slave ID=original
  • error code=function code + 0x80
  • exception code=01, 02, 03, 04, etc(依照发生错误的原因)
  • CRC校正码
1-4 error response

1-5 Table of exception codes

2 Modbus重点内容

2.1 Modbus暂存器

2.1.1 暂存器种类

Modbus其中一重要的概念是暂存器,不同地址的暂存器存放着不同数据类型型与读写特性得资料,而这里所说的暂存器地址不一定是固定的内存地址,也可以是开发者自行定义的连续或不连续的一块储存区域。我们可以把Modbus暂存器分为如图2-1所示的4大主要部分。

2-1 Modbus暂存器种类

2.1.2 暂存器地址分配

  • 这里有关地址的概念容易混淆,我的理解是暂存器PLC地址是给"人"看的,想当然尔从1开始(使用10进位制),前缀的0、1、4、3只是用来代表功能码而已,真正要注意的是後4码地址。
  • 暂存器Modbus协议地址是给"机器"看的,所以使用16进位制处理,从0开始,然後再搭配1byte的功能码处理。
2-2 暂存器地址分配

2.2 RTU封包格式

  • Modbus封包最大长度为256bytes
2-3 Modbus的问答

2.2.1 master request format

[从机地址] [功能码] [暂存器地址] [暂存器数量(word数)] [验证码]

ex : [1F] [04] [00 0A] [00 04] [D2 75]

2.2.2 slave response format

[从机地址] [功能码] [几个bytes] [data] [验证码]

ex : [1F] [04] [08] [00 01 FF FF 00 00 00 00] [54 FE]

2.2.2 封包之间的区隔

Modbus封包之间的区隔是使用3.5个字符时间来判断,在该时间区间内,master与slave不做任何动作,利用字符时间的机制只适用於Modbus RTU。

封包之间的时间间格在计算上其实还取决於鲍率,还有格式问题:

  • 依据Modbus国家规范,一个字符时间为处理11 bits资料所需时间,一般包含一个起始位、8个数据位、1个校验位 、一个停止位,而不是所谓一个byte(8 bits)的时间,这里不要搞混,这是官方规定

  • 所谓封包之间的间隔指的是在serial port上的所有封包,包含request、reaponse,只要有资料流淌,就必须规定他们之间的区分方法

  • 所以我们假设当前鲍率为9600,也就是9600 bits per second,而3.5个字符时间就等於3.5*11=38.5位,那麽我们可以求得该Modbus RTU要求封包之间的时间间格为 : (38.5*1000)/9600 = 4.0104167ms

  • 由於频繁的计算会对CPU造成负担,所以Modbus官方规定,当鲍率超过19200时,封包时间间格会固定为1.75ms

  • 另外一种较为简易的设定是不管鲍率为何,统一设定成10ms的时间间格,好处是释放cpu压力、较为简便,对於对时间要求没有那麽即时的系统,坏处是效率较低、不够精准

  • 另外Modbus还有一个字元之间的判断,一样是利用字符时间机制,假设一个数据中相邻两个字元的时间差在1.5个
    字符时间以上,就会判断该笔资料丢失 (1.5*11*1000)/baud rate ms,当鲍率超过19200时,时间间格设定成定值0.75ms

2-4 Modbus RTU封包之间的间格机制

2.3 地址范围

Modbus的ID号为1bytes(0~255),但是前面有提过,slave最多只能设定成247,这是因为0是广播模式,而剩余的248~255是Modbus的保留ID,供更高级开发用,所以实际应用上我们能设定的范围在1~247之间。

2.4 功能码

Modbus的功能码主要对应四个大方向—DO, DI, AO, AI,其中的功能码主要也是处理这四大方向(对应连接的slave提供的可能是温度数据、湿度数据、是否上锁等数位类比消息)。功能码占总封包1个byte,不过允许范围在1~127,0在上面有提到分配给广播模式,而128~255之所以不分配主要原因为假如1~127发生错误加上0x80的错误码後会变成让上限变成255,所以若是开放128以後的数会让功能码区段超过1个bytes的范围。

  • 另外Modbus也提供使用者65~72, 100~110的自订义功能码区段
2-5Modbus主要功能码

2-6Modbus功能码区段

2.4.1 读取数位输出状态(DO)

  • 功能码代号 : 01(0x)
  • 功能 : 该功能码为读取coil线圈数位输出状态资讯,若状态为on则输出1,反之若为off则输出0
  • 占用大小 : 若为n个bits,则占用N=n/8个bits,若不能整除则N+1
  • 读写状态 : R/W
  • 线圈地址上限 : 1~2000
  • Modbus起始地址 : 0x0000~0xffff

Request

请求部份我们首先填入功能码01,再来需要注意的是起始地址为2个bytes,这里填的Modbus的地址主要由使用者决定(0x0000~0xffff)分别为HI高位与LO低位,由线圈20~38(Dec)可得需请求19个线圈数据,但是我们实际读取的是Modbus地址19~37(Dec)因此将首地址转换成HEX为0x0013,数量地址也分成高低两位共2个bytes,所以我们可以得到00Hi 13Lo。

请求封包占8bytes长度。

Response

如果我们要读取线圈20~38的状态,需要先计算总共需要回传38-20+1=19个bits,19/8=2...3,不能整除所以要多分配一个bytes(用来补足的bits都设定成0),可以得出回传的data占有3个bytes。假设线圈20~27的状态分别为on-on-off-off-on-on-off-on,要注意我们需要从LSB开始存取状态,因此Binary Status为 1100 1101转换成HEX就是CD,以此类推我们可以得到图2-7的资料封包的排序如下所示。

响应封包的资料长度视请求的暂存器数量而定,最长不超过256bytes,data最长2000/8=250bytes。

2-7 功能码01的封包格式

2.4.2 读取数位输入状态(DI)

  • 功能码代号 : 02(1x)
  • 功能 : 该功能码为读取coil线圈数位输入状态资讯,若状态为on则输出1,反之若为off则输出0
  • 占用大小 : 若为n个bits,则占用N=n/8个bits,若不能整除则N+1
  • 读写状态 : R
  • 线圈地址上限 : 1~2000
  • Modbus起始地址 : 0x0000~0xffff

Request

请求部份我们首先填入功能码02,再来需要注意的是起始地址为2个bytes,这里填的Modbus的地址主要由使用者决定(0x0000~0xffff)分别为HI高位与LO低位,由线圈19~218(Dec)可得需请求22个线圈数据,但是我们实际读取的是Modbus地址196~217(Dec)因此将首地址转换成HEX为0x00C4,数量地址也分成高低两位共2个bytes,所以我们可以得到00Hi C4Lo。

Response

如果我们要读取线圈197~218的状态,需要先计算总共需要回传218-197+1=22个bits,22/8=2...6,不能整除所以要多分配一个bytes(用来补足的bits都设定成0),可以得出回传的data占有3个bytes。假设线圈197~204的状态分别为off-off-on-on-off-on-off-on,要注意我们需要从LSB开始存取状态,因此Binary Status为 1010 1100转换成HEX就是CD,以此类推我们可以得到图2-8的资料封包的排序如下所示。

2-8 功能码02的封包格式

2.5.1 读保持暂存器(AO)

  • 功能码代号 : 03(4x)
  • 功能 : 该功能码为读写暂存器类比输出状态资讯,其输出以Word为基本单位(1Word=2bytes)
  • 占用大小 : 若为n个Word,则占用2*n个bytes
  • 读写状态 : R/W
  • 暂存器地址上限 : 1~125
  • Modbus起始地址 : 0x0000~0xffff

Request

请求部份我们首先填入功能码03,再来需要注意的是起始地址为2个bytes,这里填的Modbus的地址主要由使用者决定(0x0000~0xffff)分别为HI高位与LO低位,由暂存器地址108~110(Dec)可得需请求3个暂存器数据,但是我们实际读取的是Modbus地址107~109(Dec)因此将首地址转换成HEX为0x006B,数量地址也分成高低两位共2个bytes,所以我们可以得到00Hi 6BLo。

Response

如果我们要读取暂存器108~110的状态,需要先计算总共需要回传110-108+1=3个Word,2*3=6个bytes,可以得出回传的data占有6个bytes。假设暂存器108的输出为555(Dec),我们可以经过转换得到0x022B,然後依高低位分别填入02Hi 2BLo,依此类推暂存器109、110输出分别为0以及100,经过转换分别得到00 00与00 64(HEX)。

另外假如回传暂存器状态设定成32bits,那麽暂存器取值就需要占用到108、109两个暂存器位置共4bytes,下一个暂存器将从110开始。

2-9 功能码03的封包格式

2.5.2 读输入暂存器(AI)

  • 功能码代号 : 04(3x)
  • 功能 : 该功能码为读取暂存器类比输入状态资讯,其输出以Word为基本单位(1Word=2bytes)
  • 占用大小 : 若为n个Word,则占用2*n个bytes
  • 读写状态 : R
  • 暂存器地址上限 : 1~125
  • Modbus起始地址 : 0x0000~0xffff

Request

请求部份我们首先填入功能码04,再来需要注意的是起始地址为2个bytes,这里填的Modbus的地址主要由使用者决定(0x0000~0xffff)分别为HI高位与LO低位,范例中我们要读取暂存器9的数值,但是我们实际读取的是Modbus地址08因此将首地址转换成HEX为0x0008,数量地址也分成高低两位共2个bytes,所以我们可以得到00Hi 08Lo。

Response

如果我们要读取暂存器9的状态,总计1个Word,共2个bytes,可以得出回传的data占有2个bytes。假设暂存器9的输出为10(Dec),我们可以经过转换得到0x000A,然後依高低位分别填入00Hi 0ALo。

另外假如回传暂存器状态设定成32bits,那麽暂存器取值就需要占用4bytes,那麽取直就需要暂用暂存器9与10的位置。

2-10 功能码04的封包格式


<<:  课堂笔记 - 深度学习 Deep Learning (10)

>>:  SSL 凭证制作与汇入

Day 27 - Rancher Fleet Kustomize 应用程序部署

本文将於赛後同步刊登於笔者部落格 有兴趣学习更多 Kubernetes/DevOps/Linux 相...

[FGL] 服务简单收 - IMPORT 3 利用http与XML套件取 Web资源

没有人能一次做好所有的事情,也不可能有一套系统收尽所有资料。既然如此,如何适当且适时的抓取外部资料...

D26 - 走!去浏览器重现奥运决胜点 in

前言 今天来试着用滑鼠事件重现 2021 奥运羽球决胜点! 麟洋配万岁~ 台湾万岁~~ 滑鼠 Eve...

Day25 安装 Heartbeat

今日本篇重点是要安装Heartbeat,以如何设定要监控的服务项目。 Heartbeat 安装 步骤...

Day 15 Decorator Part - 3

今天来实作一个 Decorator 的例子,当我们在画面上有一个按钮,想要透过点击该按钮触发 sho...