当master传送的request不符合格式、slave不支援此功能等问题时,代表slave不能正确解码request内容,这时slave就会response一个error response,其组成为[slave ID][error code][exception code]
Modbus其中一重要的概念是暂存器,不同地址的暂存器存放着不同数据类型型与读写特性得资料,而这里所说的暂存器地址不一定是固定的内存地址,也可以是开发者自行定义的连续或不连续的一块储存区域。我们可以把Modbus暂存器分为如图2-1所示的4大主要部分。
[从机地址] [功能码] [暂存器地址] [暂存器数量(word数)] [验证码]
ex : [1F] [04] [00 0A] [00 04] [D2 75]
[从机地址] [功能码] [几个bytes] [data] [验证码]
ex : [1F] [04] [08] [00 01 FF FF 00 00 00 00] [54 FE]
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
Modbus的ID号为1bytes(0~255),但是前面有提过,slave最多只能设定成247,这是因为0是广播模式,而剩余的248~255是Modbus的保留ID,供更高级开发用,所以实际应用上我们能设定的范围在1~247之间。
Modbus的功能码主要对应四个大方向—DO, DI, AO, AI,其中的功能码主要也是处理这四大方向(对应连接的slave提供的可能是温度数据、湿度数据、是否上锁等数位类比消息)。功能码占总封包1个byte,不过允许范围在1~127,0在上面有提到分配给广播模式,而128~255之所以不分配主要原因为假如1~127发生错误加上0x80的错误码後会变成让上限变成255,所以若是开放128以後的数会让功能码区段超过1个bytes的范围。
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。
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的资料封包的排序如下所示。
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开始。
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的位置。
<<: 课堂笔记 - 深度学习 Deep Learning (10)
本文将於赛後同步刊登於笔者部落格 有兴趣学习更多 Kubernetes/DevOps/Linux 相...
没有人能一次做好所有的事情,也不可能有一套系统收尽所有资料。既然如此,如何适当且适时的抓取外部资料...
前言 今天来试着用滑鼠事件重现 2021 奥运羽球决胜点! 麟洋配万岁~ 台湾万岁~~ 滑鼠 Eve...
今日本篇重点是要安装Heartbeat,以如何设定要监控的服务项目。 Heartbeat 安装 步骤...
今天来实作一个 Decorator 的例子,当我们在画面上有一个按钮,想要透过点击该按钮触发 sho...