modbus协议_modbus协议详解 - CSDN
  • Modbus协议解析--小白一看就懂的协议

    千次阅读 多人点赞 2019-07-09 11:49:07
    文章目录提问三连1.什么是Modbus?2.Modbus用来干什么?3.Modbus的内容是什么? 提问三连 什么是Modbus? Modbus用来干什么? Modbus怎么用?...Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schn...

    提问三连

    什么是Modbus?
    Modbus用来干什么?
    Modbus怎么用?
    

    1.什么是Modbus?

    顾名思义,他是一个bus,即总线协议.如果你接触到这种协议,相信你所处的行业是工业方面或者你的产品用于工业.

    Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。

    好了,现在知道了大概知道了,这是一个总线协议,是一个mod什么的公司发表的一个通信协议.那为什么要用这个呢? 答案就是他们都在用,你就得学,啊哈哈!!

    正经的说,它被工业领域所接受的原因是它具备一下三个优点:

    公开发表并且无版权要求
    易于部署和维护
    对供应商来说,修改移动本地的比特或字节没有很多限制

    简单的概括,就是免费+简单+方便修改! (不仅外国人喜欢,中国人也喜欢!)


    归纳:
    Modbus就是一种用在工业上的简单协议!

    2.Modbus用来干什么?

    用两个字概括:通信!

    是的,所有协议都是用来通信的,协议的制定就是让两个人根据这个协议看懂传来的一组数据.
    比如我给你一个1234,你要是没有协议,就只知道这是1234,而有了协议,你就知道了这是在问我是不是单身狗?(哈哈,举个例子) 当然,也可以表示其他意思,具体什么意思就看你协议怎么制定!


    归纳:
    Modbus用来通信喽,是个人都知道!

    3.Modbus的内容是什么?

    先给个官方的文档:https://download.csdn.net/download/panda5_csdn/10534090
    要想知道Modbus的内容,就得先知道它有几个种类:

    大致分为以下几种:
    Modbus-RTU+Modbus-ASCII
    Modbus-TCP
    Modbus-Plus
    以上三种协议,一个设备只会有一种协议,如果你的设备使用的是Modbus-RTU,只需查看以下对应部分,其他部分无需查看,因为看多了,你会糊涂,哈哈.当然,学习的话可以全部阅读.

    通讯过程:
    Modbus是主从方式通信,也就是说,不能同步进行通信,总线上每次只有一个数据进行传输,即主机发送,从机应答,主机不发送,总线上就没有数据通信.(所以说,这也算是一个缺点了)

    举例1: 一个总线上有一个主机,多个从机,主机查询其中一个从机,首先你必须得这些从机分配地址(这样才能知道哪个从机,而且每个地址必须唯一),分配好地址后,主机要查询,然后数据下发(数据内容下面会介绍),从机得到主机发送的数据,然后对应地址的从机回复,主机得到从机数据,这样就是一个主机到从机的通信过程,是不是很简单呢?

    举例2: 就像打电话,你得知道对方的电话(这就是唯一地址),然后你打电话过去,相当于主机查找从机,然后对方接通电话,给你回复(返回数据);正常是这样的.
    如果这时候,对方正在打电话,你应该听到的是"sorry,you…"这一串英文,说明对方忙,但是Modbus总线不能判断对方是否忙,也没有对应的仲裁机制,好了你又知道了一个缺点了!但是,你可以在用软件的办法进行适当的处理数据!

    3.1 Modbus-RTU+Modbus-ASCII

    这种协议是基于异步串行通信上,一般的介质有:RS-232,RS485,RS-422上,这也是工业上使用的最多的;

    对于串行连接,存在两个变种,它们在数值数据表示不同和协议细节上略有不同。
    Modbus RTU是一种紧凑的,采用二进制表示数据的方式,Modbus ASCII是一种人类可读的,冗长的表示方式。
    RTU格式后续的命令/数据带有循环冗余校验的校验和,而ASCII格式采用纵向冗余校验的校验和。

    3.1.1 Modbus-RTU协议

    这是本人目前使用的协议,所以对这个协议比较了解.

    概述

    设备必须要有RTU协议!这是modbus协议上规定的,且默认模式必须是RTU,Ascii作为选项。(也就是说,一般的设备只有RTU这个协议,ascii一般很少)所以说,一般学习Modbus协议,只需要了解RTU的协议,acsii作为学习的了解就足够了。

    1. 帧结构

    帧结构 = 地址 + 功能吗 + 数据 + 校验

    地址: 占用一个字节,范围0-255,其中有效范围是1-247,其他有特殊用途,比如255是广播地址(广播地址就是应答所有地址,正常的需要两个设备的地址一样才能进行查询和回复)

    功能码:" 占用一个字节,功能码的意义就是,知道这个指令是干啥的,比如你可以查询从机的数据,也可以修改数据,所以不同功能码对应不同功能.

    数据: 根据功能码不同,有不同结构,在后续的实例中有说明;

    校验: 为了保证数据不错误,增加这个,然后再把前面的数据进行计算看数据是否一致,如果一致,就说明这帧数据是正确的,我再回复;如果不一样,说明你这个数据在传输的时候出了问题,数据不对的,所以就抛弃了;

    在这里插入图片描述

    2.实战

    直接进实战了,是不是感觉有点虚?淡定,直接在实战中联系,更加有效!其实前面的都是废话,让你知道所以然而已,实际中用的就这些,看了就懂了!

    2.1 查询

    功能描述:
    现在我是主机,我要查询从机地址为1的数据.
    (这里我推荐使用modbus Pull这个软件,操作简单,功能强大,下载连接:https://download.csdn.net/download/panda5_csdn/10534894 如果没有积分下载,可以私信我)
    于是,我需要发送以下数据:

    主机发送: 01 03 00 00 00 01 84 0A
    从机回复: 01 03 02 12 34 B5 33

    那么这一组数据是什么意思呢?
    从上面的结构图中,可以看出,大致是 地址+功能码+数据+校验;
    所以解析如下:

    /*发送数据解析*/
    01-地址
    03-功能码,代表查询功能,其他功能后面再说
    00 00-代表查询的起始寄存器地址.说明从0x0000开始查询.
    (这里需要说明以下,Modbus把数据存放在寄存器中,通过查询寄存器来得到不同变量的值,一个寄存器地址对应2字节数据;)
    00 01-代表查询了一个寄存器.结合前面的00 00,意思就是查询从0开始的1个寄存器值;
    84 0A-循环冗余校验,是modbus的校验公式,从首个字节开始到84前面为止;
    (这里新手可能不懂,这个校验就是保证数据传输过程没有错误的一种手段,不同的协议这种校验公式不一样,
    只需了解这个就足够了,具体怎么求的,可以直接在输出数据得到结果,地址如下:
    http://www.ip33.com/crc.html)
    /*回复解析*/
    01-地址
    03-功能码
    02-代表后面数据的字节数,因为上面说到,一个寄存器有2个字节,所以后面的字节数肯定是2*查询的寄存器个数;
    12 34-寄存器的值是12 34,结合发送的数据看出,01这个寄存器的值为12 34
    B5 33-循环冗余校验
    

    好了,是不是很简单呢?基本流程就是:
    发送:地址正确+我要查的寄存器个数+校验
    回复:从机的地址+数据的字节数+数据+校验
    就是这么简单!!!

    2.2 修改

    如果我要修改从机的数据呢?那么这个协议有吗,答案是Yes!

    1. 修改-0x06功能码

    主机发送: 01 06 00 00 00 01 48 0A
    从机回复: 01 06 00 00 00 01 48 0A

    诶,看上去怎么一样的啊?是不是错了?答案是这是正确的;

    *发送数据解析*/
    01-主机要查的地址
    06-功能码,代表修改单个寄存器功能,修改有些不同,有修改一个寄存器和修改多个寄存器;
    00 00-代表修改的起始寄存器地址.说明从0x0000开始.
    00 01-代表修改的值为00 01.结合前面的00 00,意思就是修改0号寄存器值为00 01;
    48 0A-循环冗余校验,是modbus的校验公式,从首个字节开始到48前面为止;
    
    /*回复解析*/
    01-从机返回的地址,说明这就是主机查的从机
    06-功能码,代表修改单个寄存器功能;
    00 00-代表修改的起始寄存器地址.说明是0x0000.
    00 01-代表修改的值为00 01.结合前面的00 00,意思就是修改0号寄存器值为00 01;
    48 0A-循环冗余校验,是modbus的校验公式,从首个字节开始到48前面为止;
    

    如果回复的一样,说明这个数据是修改成功的;如果功能码不是06,而是86,说明你发送的数据出错了;

    2.修改-0x10功能码

    如果我要修改多个寄存器,难道用06发好几次,这样不会太傻了吗?所以,modbus RTU协议包含了修改连续多个寄存器的方法,就是功能码为0x10;
    下面先举例用0x10功能码进行一个字节的修改和0x06功能码有什么区别:

    主机发送: 01 10 00 00 00 01 02 11 22 2A 19
    从机回复: 01 10 00 00 00 01 01 C9

    *发送数据解析*/
    01-主机要查的地址
    10-功能码,代表修改多个寄存器功能;
    00 00-代表修改的起始寄存器地址.说明从0x0000开始.
    00 01-代表修改的寄存器数量,这里开始于0x06的修改不同;
    02 -表示修改的总字节数,由于只修改了1个寄存器,所以数据要有两个字节;
    11 22-表示修改的值,结合上面,就是从第0000寄存器开始修改一个寄存器值为11 22,就是把0000寄存器改为11 222A 19-循环冗余校验,是modbus的校验公式,从首个字节开始到22前面为止;
    
    /*回复解析*/
    01-从机返回的地址,说明这就是主机查的从机
    10-功能码
    00 00-代表修改的起始寄存器地址.说明是0x0000.
    00 01-代表修改的寄存器数量,只需要回复这么多久足够了,从机告诉主机,你修改了哪几个寄存器就足够了;
    01 C9-循环冗余校验;
    

    现在,修改多个寄存器:

    主机发送: 01 10 00 00 00 02 04 11 22 33 44 42 5A
    从机回复: 01 10 00 00 00 02 41 C8
    (这个请读者自己解析内容,如果需要不懂之处,请评论)


    归纳
    modbus RTU协议只需要看懂功能码0x03,0x06,0x10这三个基本的就已经足够了;分别回想下其数据域部分:
    03-主机需要发送起始地址+寄存器数量,从机回复总字节数+数据
    06-主机发送起始地址+数据内容(因为你只需要修改一个,所以起始地址就是所要修改的地址),从机返回起始地址+数据内容(发现居然一样!)
    10-主机发送起始地址+寄存器个数+总字节数+数据,从机返回起始地址+寄存器数量

    3.1.2 Modbus-ACSII协议

    概述

    一般只需要了解RTU协议,因为前面有说过,必须要有RTU协议的,所以只需要了解了RTU协议,就可以读出设备信息了,至于ACSII协议,做个大概了解

    1.帧形式

    对于RTU协议,比如RTU发送一个字节:0x12;
    ASCII协议则需要发送2个字节:一个字节代表1,一个代表2,即31和32,才能代表0x12.
    所以,acsii协议的效率比较低。
    在这里插入图片描述
    从上面的图中,看出:
    1)比RTU多了起始段 :, 多个结束符CR,LF
    2)地址和功能都变成了2个字节;
    3)数据部分更加繁琐,但是更符合人们的查看;


    归纳:
    由于Modbus-RTU和modbus-ACSII都是基于232和485链路的,所以其通讯模式半双工,一般是主机和从机的模式。其差别就是其字节的格式不同,一个是16进制的数据,一个是acsii数据。
    Ascii多了帧头和帧尾,也就是说可以有用这个头尾判断一帧字节来判断是否结束;而RTU没有帧头和帧尾,所以协议里明确两帧之间要大于3.5个字节时间间隔,作为一帧结束的判断依据。

    对于RS485来说,总线上一般允许最大32个设备。

    3.2 Modbus-TCP

    以太网作为介质进行传输,

    3.3 Modbus-PLUS

    高速令牌传递网络


    归纳:
    在这里插入图片描述

    展开全文
  • MODBUS协议最简单又是最直白的解释

    万次阅读 多人点赞 2018-01-11 10:00:36
    Modbus是一种单主站的主/从通信模式。... Modbus通信标准协议可以通过各种传输方式传播,如 RS232C、RS485、光纤、无线电等。 Modbus具有两种串行传输模式,ASCII 和 RTU。它们定义了数据如何打包、解码的
    Modbus是一种单主站的主/从通信模式。Modbus网络上只能有一个主站存在,主站在 Modbus网络上没有地址,从站的地址范围为 0 - 247,其中 0 为广播地址,从站的实际地址范围为 1 - 247。 Modbus通信标准协议可以通过各种传输方式传播,如 RS232C、RS485、光纤、无线电等。 
    Modbus具有两种串行传输模式,ASCII 和 RTU。它们定义了数据如何打包、解码的不同方式。支持 Modbus 协议的设备一般都支持 RTU 格式。通信双方必须同时支持上述模式中的一种。
    
    上面说的是官话,下面是我说的大白话:
    
    modbus协议也只是通讯协议的一种,没什么神秘的,通讯协议包括两个方面:
    
    一、通讯格式,即: 波特率,检验方式,数据位,停止位
    
    波特率:一秒钟传送的位数,也就是通讯速率;比如波特率为9600,即,一秒种可以传送9600个位数,位的概念看下面的数据位介绍
    校验方式:奇校验或偶校验或无校验,目的是判断传输过程中是否有错误!它只是用于判断一个字符(比如八个位或是七个位组成一个字符)传输是否有错误。但是它并不能完全能够判断传输是否有错。比如偶校验,在检验送八个“11111111”时,如果到达接收方,由于干扰而变成了“10111101”,“1”的个数仍然是偶数,接收方就判断不出来传送的字符已经错误!
    数据位:传输一个字符由几个位组成,计算机的基本单位就是“位”,其值非“0”即“1”,又如传送A,定义通讯格式时,是定义的八位,其传送的数据可能就是:00001010;
    停止位:传输一个字符有几个停止位,用天判断某个字符是否传输结束,以便开始接收下一个字符。
    
    通讯格式的作用是规范发送方与接收方的传输格式,如果双方通讯格式不一样,接收方就不可能正确判断发送方发来的东西是什么。
    比如,接收方设置的波特率是10(一秒只接收十个位)位,而发送方的波特率是20(一秒发送二十个位),那么发送方一秒种发送的20个字符,接收方就不可能都收到,只能接收到10个,造成通讯出错。
    校验方式:双方校验方式不一样,就没有一个统一的标准认定传输是否有错误。
    数据位,接收方设定的七位,即它接收到七个位就认为是一个字符,而实际发送方设定的是八位,那么接收方认定的字符与发送方发送的字符就不一样了。
    
    ***参预通讯的双方设定的通讯格式必须一样的!!
    
    二、通讯规范(这个词是我自己定义的,不能引用,会被人笑话的)
    通讯格式只是保证接收方正确地接收到发送方传输过来的每一个字符(实际如上所述,检验方式并不能保证完全正确,还要靠通讯规范中的校验和计算来验证整体正确性,下面会继续说明),那么接收到的整串字符做什么用呢,就要靠通讯规范了
    MODBUS的通讯规范很简单!
    
    先说ASCII方式:
    ASCII方式发送时的规范定义如下:
    起始符 +  设备地址   +  功能代码   +   数据  +   校验   +  结束符
    
    
    1\起始符:    接收到一串字符,总要知道这串字符从哪个地方开始吧,这就是起始符的作用,接收方不管以前收到多少个字符。当接收到起始符时,以前的字符就不再理它了,从起始符开始分析以后的字符!  MODBUS的ASCII方式起始符是一个冒号  “:”
    2\设备地址:
    前面说过,MODBUS是单主站的主/从通信模式,一个主站下面可以接十多个从站。大家都挂在一条线,如果没有一个设备地址,就不知道是发给哪个从站的,大家都回应的话,这条线上的信号就乱七八糟了,主站也不知道接收到的是什么了。所以,设这么一个设备地址,告诉是给那个从站的。只要这个从站回答,其他的闭嘴!“二号,请您回答,其他人就不要吱声了。”
    

    广播地址(0)是命令式的,不要求从站回答的。“都听着,晚上全体到我家喝酒去,不去者死,散会!”
    设备地址是要求两个字符,比如发给2号站,则是“02”
    现在的组合是“:02”

    3\功能代码:
    

    告诉从站应该做什么,比如读数据的命令是“3”,从站接收到这个命令,再根据下面数据要求的具体地址,把具体地址的数据返回给主站。
    功能代码也是要求两个字符,比如读命令3,则是“03”
    现在的组合是“:0203“

    4\数据:
    1、告诉从站具体的元件通讯地址,写入到哪里,从哪里读。如读变频器的设定频率的通讯地址是00A0
    元件的通讯地址要求是四个字符,如果控制器的元件地址不足四个字符,则在前面补0,比如元件通讯地址是A0,则在前面补足两个0:“00A0”

    2、数据又有可能包括您要读取的字节数( 有的控制器是字数),比如连续读取PLC的两个十六位寄存器,其字节数为四个,则是“0004”。您看出来了吧,读取个数也是要求四个字符,不足四个,前面补零
    现在的组合是“:020300A00004“

    3、而当您要实现写入功能时,数据又可能包括写入的数据,比如写入一个十六位寄存器的值,则要包括是写入的数值,如“0D98”
    现在的组合是“:020600A00D98“ 06是单个寄存器的写入命令

    4、当连续写入多个寄存器时,这个数据包括的内容又不一样,它可能是:
    寄存器通讯地址(四个字符) +字数(四个字符)+字节数(两个字符)+ 要写入的数值

    您看乱了吗?没关系的,等您拿到具体控制器时,此控制器的通讯说明上会告诉您此数据都包括什么内容,以什么样的格式排列!您一定为我上面四点中的可能字样而生气,您认为讲解就应该讲解的具体,而不是可能什么又可能什么!
    这又要重复说明一下MODBUS的通讯规范,
    起始符 + 设备地址 + 功能代码 + 数据 + 校验 + 结束符
    MODBUS是一种标准通讯协议,这种标准定义了上面红色字符的通讯规范,除了数据项,其他的都是固定字符个数。
    数据呢,因为功能代码的不同,其包含的内容也不同!所以我只好说可能包含这个,可能包含那个。您无须担心此数据变来变去,造成接收方不知道如何分析。接收方在接收到功能代码时,就已经知道此数据包含多少个字符了!

    5\检验和:
    前面说过,通讯格式里的校验方式并不能保证每个字符都正确,所以这里就把所有字符的值加在一起,其和(检验值)传给接收方,接收再把接收到的字符的值加在一起,与发送方传送过来的检验值比较,如果相等,就算接收正确了。
    "这种方式极大地提高了传输的可靠性,保证了传输的顺利进行,为传输事业做出了巨大贡献"
    这样的描述让您想到了什么?假话呗,检验只是提高了校验的可靠性。并不能完全判断传输是否正确。想要最大限度的提高传输的可靠性,唯有最大限度地降低干扰!于是产生了232,485,422传输方式,他们的区别就在于传输的可靠性!
    

    1+2+3=6 3+2+1=6 这两个字符串的作用肯定是不一样的!但是其校验和是一样的,如果在传输过程中,由于干扰,1变3,3变1,根据校验和的计算,接收方并不知道由于干扰而造成传输错误,此时,或是出现通讯错误,或是出现通讯混乱。
    如,命令码03,由于干扰而变成了30,此时校验和是一样的,而MODBUS并没有30这个命令码,接收不认识,于是出现通讯错误。
    再如,读变频器的设定频率通讯地址是0001,由于干扰而变成了0010,此时校验和是一样的,但是通讯地址却变了,变频器就可能返回的是其他数据,造成通讯混乱!
    校验字符是要求两个字符,如果计算结果超过两个字符,则取后两位!
    **参加校验计算的字符是起始符与校验符之间的字符串(不含起始符与校验符)
    现在的组合是“:02030A000004FB“ (假设校验和为FB)
    02030A000004参加校验和计算

    6\结束符:
    

    接收到一串字符,总要知道在那个地方结束吧,这就是结束符的作用,接收方不管以后还会收到多少个字符。当接收到结束符时,以后再接收的字符就算是下一轮的东西了,从起始符到结束符之间的字符就是它要分析的字符! MODBUS的ASCII方式结束符是— Chr(13)+Chr(10)
    现在的组合是“:02030A000004FB“+ Chr(13)+Chr(10)
    至此,ASCII方式的发送就完成了,控制器接收到此串字符后,根据MODBUS协议定义的通讯规范分析此串字符的作用,然后返回相应的字符!

    注意:发送的字符都是以十六进制数表示!

    控制器返回的字符根据命令的不同而不同,此处不好讲解,在下面具体例子中会有说明!

    再说RTU方式:
    
    RTU方式发送时的规范定义如下:
    至少3.5个字符传输时间的停顿间隔时间标定消息的开始  设备地址  + 功能代码+   数据  +   校验  +  至少3.5个字符传输时间的停顿间隔时间标定了消息的结束  
    
    其他的就不用说了,与ASCII方式一样的作用,唯独这3.5个字符的时间搞晕了很多人,实际我也不敢太解释,大致说一下吧,您就当听着玩,比如通讯格式是9600,E,8,1
    波特率是做什么的?一秒传输多少个位(比如一秒传送9600个位),一个字符是多少个位呢?通讯格式已经标定了(7个位或是8个位),那3.5个字符的传送时间就好算了吧:
    

    3.5*11(或10)=39个位(35个位),传3.5个字符需要的时间是:39/9600=4毫秒。
    不是说八位嘛,怎么乘11,记住了,还有一个起始位,奇或偶的校验位(无奇偶校验,则没有此位),停止位(两个停止位就是2了。)
    如9600,N,8,2为11个位,
    9600,N,7,1.为9个位
    就是说,您得保证发送字符串的连续性,中间停顿时间超过4毫秒,接收方就认为您已经发送完了这组消息,开始处理了。这就是至少3.5个字符传输时间的停顿间隔时间标定了消息的结束的含义
    如果您发送的太连续,下一组消息与上一组消息之间的间隔时间没超过4毫秒,接收方就认为这些字符是一组消息,按一组消息去处理。所以,您发送结束一组命令后,必须间隔4毫秒才能发送下一组命令. 这就是至少3.5个字符传输时间的停顿间隔时间标定消息的开始的含义

    至此我的大白话结束,有什么错误,请您批评,多谢!

    通讯协议实际也就是这回事,任何一个协议都大同小异。通讯格式,通讯规范两种而已。
    您如果愿意,也可以自己定义一个通讯规范,用PLC或是VB语言按照您自己定义的这个规范处理,如果可靠性超过modbus,那您的通讯规范就是最流行的了!
    

    我经常问某些产品推广人员,“您设备的通讯协议是什么”,他回答:“232”或是“485”。今天您看了上面这些大白话,请您就不要再这样回答了。
    Modbus通信标准协议可以通过各种传输方式传播,如 RS232C、RS485、光纤、无线电等。
    “232”或是“485”只是一种线路传输方式,与协议是无关的!232传输抗干扰性差,485传输抗干扰相对强。

    MODBUS做为一种标准的协议,应用于各种PLC,控制器,仪表。这些仪表或是控制器应用中,元件的通讯地址肯定是不一样了;各个命令码的各部分组成的意义也许也会不同;
    但是,它一定会遵守MODBUS的协议规范。即,每个命令码的组成一定符合MODBUS的规范!一样不多,一样不少!

    下面针对某种支持MODBUS协议的控制器,说一下具体的读/写例子

    1\ 读某控制器的十五个寄存器值,发送的字符串是:(ASCII方式)

    “: 01031000000FDD”+ Chr(13)+Chr(10)
    起始符“:” + 站号(01) + 读命令(03)+ 起始寄存器通讯地址(1000)+ 字数(15;转换成十六进制000F)+ 校验和(DD)+ 结束符

    控制器返回的字符数是71个
    起始符“:” + 站号(01) + 读命令(03)+ 字数(0F)+ 60个数据字符(一个寄存器是4个,一共十五个) + 校验和(DD)+ 结束符(两个)

    2\ 分别向某控制器两个寄存器写入数值,发送的字符串是:(ASCII方式)

    “: 01101000000204”+ 写入的数值(8个字符)+ 校验和 + Chr(13)+Chr(10)
    起始符“:” + 站号(01) + 写命令(10)+ 起始寄存器通讯地址(1000)+ 字数(2;转换成十六进制0002)+ 字节数(04) + 校验和(因为写入数值是变化的,需要得经过计算得出校验和)+ 结束符

    控制器返回的字符数是17个
    起始符“:” + 站号(01) + 写命令(10)+ 起始寄存器通讯地址(1000)+ 字数(02)+ 错误码(2个) + 校验和(DD)+ 结束符(两个)

    *错误码,当通讯正确时是什么,通讯错误时是什么,具体控制器会有说明

    读其他仪表的某值时,参照上述读的规范,也就是更改一下起始寄存器通讯地址、字数、校验和(校验和是编制程序块自动计算的。)
    要读多个仪表的同一个检测值更简单,更改站号就可以了。

    展开全文
  • Modbus应用协议详解

    千次阅读 2019-03-18 18:38:23
    Modbus应用协议缩略语协议描述服务器正常响应服务器异常响应三种PDUmb_req_pdumb_rsp_pdumb_excep_rsp_pdu数据编码Modbus数据模型Modbus模型实现的实例 ...Modbus协议定义了一个与基础通信层无关的简单协议数据单...

    Modbus应用协议

    缩略语

    在这里插入图片描述

    协议描述

    Modbus是一个请求/应答协议,并且提供功能码规定的服务。Modbus功能码是Modbus请求/应答PDU 的元素。

    Modbus协议定义了一个与基础通信层无关的简单协议数据单元(PDU),特定总线或网络上的Modbus协议映射能够在应用数据单元(ADU)上引入一些附加域
    在这里插入图片描述

    启动Modbus事务处理的客户机(主)创建Modbus应用数据单元,功能码向服务器(从)指示将执行哪种操作,Modbus协议建立了客户机启动的请求格式。

    用一个字节编码Modbus数据单元的功能码域,有效的码字范围是十进制1-255(128-255为异常响应保留),当从客户机向服务器设备发送报文时,功能码域通知服务器执行哪种操作。

    向一些功能码加入子功能码来定义多项操作

    从客户机向服务器设备发送的报文数据域包括附加信息,服务器使用这个信息执行功能码定义的操作。这个域还包括离散量和寄存器地址、处理的项目数量以及域中的实际数据字节数。

    在某种请求中,数据域可以是不存在的(0长度),在此情况下服务器不需要任何附加信息,功能码仅说明操作

    如果在一个正确接收的Modbus ADU中,不出现与请求Modbus功能有关的差错,那么服务器至客户机的响应数据域包括所请求的数据。如果出现与所请求Modbus功能有关的差错,那么该域包括一个异常码,服务器应用能够使用这个域确定下一个执行的操作

    服务器正常响应

    当服务器对客户机响应时,它使用功能码域来指示正常(无差错)响应或者出现某种差错(称为异常响应),对于一个正常响应来说,服务器仅复制原始功能码
    在这里插入图片描述

    服务器异常响应

    对于异常响应,服务器将原始功能码的最高有效位设置为逻辑1后返回
    在这里插入图片描述

    注:需要管理超时,以避免无限期的等待可能不会出现的应答
    

    三种PDU

    Modbus最初在串行链路上的实现(最大RS485 ADU=256字节)限制了Modbus PDU的长度,因此对串行链路通信来说,Modbus PDU=256-服务器地址(1字节)-CRC(2字节)=253字节,从而,RS232/RS485 ADU=253字节+服务器地址+CRC=256字节

    Modbus协议定义了三种PDU,他们是:
    1.	Modbus请求PDU,mb_req_pdu
    2.	Modbus响应PDU,mb_rsp_pdu
    3.	Modbus异常响应PDU,mb_excep_rsp_pdu
    

    mb_req_pdu

    mb_req_pdu={function_code,request_data}其中
    fuction_code:1字节,Modbus功能码
    request data:n字节,这个域与功能码有关,并且通常包括诸如参考变量、变量计数、数据偏移、子功能码等信息
    

    mb_rsp_pdu

    mb_rsp_pdu={function_code,response_data},其中
    function code:1字节,modbus功能码
    response data:n字节,这个域与功能码有关,并且通常包括诸如参考变量、变量计数、数据偏移、子功能码等信息
    

    mb_excep_rsp_pdu

    mb_excep_rsp_pdu={function_code,exception_code},其中
    function code:1字节,modbus功能码+0x80
    exception code:1字节,modbus异常码
    

    数据编码

    Modbus使用最高有效字节在低地址存储的方式表示地址和数据项,这意味着当发送多个字节时,首先发送最高有效字节,例如:

    0x1234,发送的第一个字节为0x12,然后是0x34
    

    Modbus数据模型

    Modbus的数据模型是以一组具有不同特征的表为基础建立的
    在这里插入图片描述

    输入与输出之间以及位寻址和字寻址的数据项之间的区别并不意味着应用特性的差别,如果所有4个表相互覆盖是对该目标机器最自然的解释,也是完全可接受的,而且很普遍

    对于每个基本表,协议都允许单个的选择65536个数据项,而且其读写操作被设计为可以越过多个连续数据项直到数据大小规格限制,这个数据大小规格限制与事务处理功能码有关

    很显然,必须将Modbus处理的所有数据(位,寄存器)放置在设备应用存储器中,但是存储器的物理地址不应该与寄存器编号混淆,仅要求将寄存器编号与物理地址链接

    Modbus功能码中使用的Modbus寄存器逻辑编号是以0开始的无符号整数索引

    Modbus模型实现的实例

    下列实例表示了两种在设备中组织数据的方法,有多种数据组织的方法,在本部分中没有被全部描述,每个设备根据其应用都有它自己的组织数据的方法

    实例一:有四个独立块的设备

    下图表示了含有数字量、模拟量、输入量和输出量的设备中的数据组织,由于不同块中的数据不相关,每个块是相互独立的,可通过不同Modbus功能码访问每个块
    在这里插入图片描述

    实例二:仅有一个块的设备

    设备仅有一个数据块,通过几个Modbus功能码能够得到相同的数据,既可以通过16位访问也可以通过1位访问
    在这里插入图片描述

    Modbus寻址模型

    Modbus应用协议精确地定义了PDU寻址规则
    在Modbus PDU中,从0-65536寻址每个数据
    Modbus应用协议还明确的定义了由4个块构成的Modbus数据协议,每个块由编号为1-n的元素构成
    在Modbus数据模型中,从1-n为数据块中的每个元素编号
    然后必须将Modbus模型与设备应用结合
    Modbus数据模型和设备应用之间的映射完全与特定设备相关
    下图表示了用Modbus PDU的X-1寻址编号为X的Modbus数据
    在这里插入图片描述

    Modbus事务处理的定义

    下图描述了在服务器侧Modbus事务处理的一般处理过程
    在这里插入图片描述
    一旦服务处理请求,使用合适的Modbus服务器事务处理建立Modbus响应
    根据处理结果,可以建立两种类型响应:

    1. 一个正常的Modbus响应,响应功能码=请求功能码
    2. 一个异常的Modbus响应
      a) 用来为客户机提供处理过程中与所发现的差错相关的信息
      b) 异常功能码=请求功能码+0x80
      c) 提供一个异常码来指示差错原因

    功能码分类

    有三类Modbus功能码,它们是:

    1. 公共功能码
      a) 被确切定义的功能码
      b) 保证是唯一的
      c) 由Modbus.org确认的
      d) 公开的文档
      e) 可进行一致性测试
      f) 在MB ITEF RDC中归档
      g) 包含已被定义的公共功能码和保留给未来使用的功能码
    2. 用户自定义功能码
      a) 有两个用户定义功能码的区域,即十进制的65-72和100-110
      b) 用户无需Modbus组织的任何批准就可以选择和实现一个功能码
      c) 不能保证被选功能码的使用是唯一的
      d) 如果用户希望将某种功能设置为一个公共功能码,那么用户必须启动RFC,以便将改变引入公共分类中,并且指配一个新的公共功能码
    3. 保留功能码
      a) 某些公司在传统产品上现行使用的功能码,不作为公共使用
      在这里插入图片描述

    公共功能码定义

    在这里插入图片描述

    功能码描述

    (0x01)读线圈

    使用该功能码从一个远程设备中读1-2000个连续的线圈状态,请求PDU指定了起始地址,即指定了第一个线圈地址和线圈数目,地址从0开始

    响应报文中的线圈按数据域的每位一个线圈进行打包,状态被表示为1=ON和0=OFF,第一个数据的LSB包含询问中所寻址的输出,其他线圈以此类推,直到这个字节的高位端为止,并在后续字节中按照从低位到高位的顺序排列

    如果返回的输出数量不是8的倍数,将用0填充最后数据字节中的剩余位,字节数量域说明了数据的全部字节数

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求读离散量输出20-38的实例:
    在这里插入图片描述
    将输出27-20的状态表示为十六进制字节值0xCD或二进制11001101,输出27是这个字节的MSB,输出20是LSB

    第一个字节的输出从左到右为27-20,下一个字节的输出从左到右为35-28,当串行发送位时,从LSB向MSB传输:20…27…35…

    在最后的数据字节中,将输出38-36的状态表示为十六进制值0x05或二进制00000101,输出38是左侧第6个位置,输出36是这个字节LSB,用0填充高5位
    在这里插入图片描述

    (0x02)读离散量输入

    使用该功能码从一个远程设备中读1-2000个连续的离散量输入状态,请求PDU指定了起始地址,即指定了第一个离散量输入地址和离散量输入数目,在PDU中从0开始寻址离散量输入,因此编号1-16的离散量输入寻址为0-15

    响应报文中的离散量输入按数据域的每位一个离散量输入进行打包,状态表示成1=ON和0=OFF,第一个数据字节的LSB包含询问中所寻址的输入,其他离散量输入以此类推,直到这个字节的高位端为止,并在后续的字节中按照从低位到高位的顺序排列

    如果返回的输入数量不是8的倍数,将用0填充最后数据字节中的剩余位,字节数量域说明了数据的全部字节数

    mb_req_pdu/ mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求离散量输入197-218的实例:
    在这里插入图片描述
    将离散量输入204-197的状态表示为十六进制值0xAC或二进制10101100,输入204是这个字节的MSB,输入197是这个字节的LSB

    将离散量输入218-213的状态表示为十六进制值0x35或二进制00110101,离散量输入218位于左侧第三位,离散量输入213是这个字节的LSB,用0填充高2位
    在这里插入图片描述

    (0x03)读保持寄存器

    使用该功能码从远程设备中读保持寄存器连续块的内容,请求PDU说明了起始寄存器地址和寄存器数量,在PDU中,从0开始寻址寄存器,因此编号1-16的寄存器寻址为0-15

    将响应报文中的寄存器数据打包成每个寄存器有两字节

    对于每个寄存器,第一个字节为高位字节,第二个字节为低位字节

    mb_req_pdu/ mb_rsp_pdu/ mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求读寄存器108-110的实例:
    在这里插入图片描述
    将寄存器108的内容表示为两个十六进制字节值0x022B,将寄存器109-110的内容分别表示为十六进制0x000和0x0064
    在这里插入图片描述

    (0x04)读输入寄存器

    使用该功能码从一个远程设备中读1-125个连续输入寄存器,请求PDU说明了起始地址和寄存器数量,在PDU中从0开始寻址寄存器,因此编号为1-16的寄存器寻址为0-15

    将响应报文中的寄存器数据打包成每个寄存器两个字节

    对于每个寄存器,第一个字节为高位字节,第二个字节为低位字节

    mb_req_pdu/ mb_rsp_pdu/ mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求读输入寄存器9的实例:
    在这里插入图片描述
    将输入寄存器9的内容表示为两个十六进制值0x000A
    在这里插入图片描述

    (0x05)写单个线圈

    使用该功能码将一个远程设备中的一个输出写为ON/OFF

    请求数据域中的常数指定所请求的ON/OFF状态,十六进制0xFF00请求输出为ON,十六进制0x0000请求输出为OFF,其他所有值均是非法的,并且对输出不起作用

    请求PDU说明了被强制的线圈地址,从0开始寻址线圈,因此编号为1的线圈寻址为0

    正常的响应是请求的复制,在写入线圈状态之后被返回

    mb_req_pdu/ mb_rsp_pdu/ mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求写线圈173为ON的实例:
    在这里插入图片描述
    在这里插入图片描述

    (0x06)写单个寄存器

    使用该功能码在一个远程设备中写一个保持寄存器

    请求PDU指定了被写入的寄存器地址,从0开始寻址寄存器,因此编号为1的寄存器被寻址为0

    正常的响应是请求的复制,在写入寄存器内容之后被返回

    mb_req_pdu/ mb_rsp_pdu/ mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求将十六进制0x0003写入寄存器2的实例:
    在这里插入图片描述
    在这里插入图片描述

    (0x07)读异常状态(仅用于串行链路)

    使用这个功能码从一个远程设备中读8个异常状态输出的内容

    因为异常码输出参量是已知的(在这个功能中不需要输出参量),该功能提供一种简单的访问这种信息的方法

    正常的响应包括8个异常状态输出的内容,将这些输出打包成一个字节,每个输出一个位,在该字节的LSB中包含最低位输出参量的状态

    mb_req_pdu/ mb_rsp_pdu/ mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求读异常状态的实例:
    在这里插入图片描述
    在这个实例中,输出数据是十六进制0x6D(01101101),从左到右,输出为OFF-ON-ON-OFF-ON-ON-OFF-ON,从最高到最低寻址输出的方式显示状态
    在这里插入图片描述

    (0x08)诊断(仅用于串行链路)

    Modbus功能码提供一系列测试,用于检测客户机(主站)与服务器(从站)之间的通信系统,或服务器中的各种内部差错状态

    这个功能使用询问中的2个字节的子功能码域来定义所执行的测试类型,服务器在正常的响应中复制功能码和子功能码,一些诊断会导致远程设备通过正常响应的数据域返回相应数据

    通常,向远程设备发送诊断功能,不影响远程设备中的用户程序运行,诊断不能访问用户逻辑,某些功能可以任意的复位远程设备中的差错计数器

    但是,可以强制服务器进入“只听”模式,在这种模式中服务器监视通信系统中的报文而不对报文进行响应,如果应用程序与远程设备的进一步数据交换有关,就可能影响应用程序的结果,一般情况下,这种模式被强制用来从通信系统中去除有故障的远程设备

    下列诊断功能专门用于串行链路设备

    对返回询问数据请求的正常响应是回送相同的数据,同时还复制功能码和子功能码

    mb_req_pdu/ mb_rsp_pdu/ mb_excep_rsp_pdu

    在这里插入图片描述

    串行链路设备支持的子功能码

    下图是串行链路设备所支持的子功能码列表,所列出的每个子功能码均附带用于诊断的数据域内容的实例
    在这里插入图片描述

    (0000)返回询问数据

    在响应中返回请求数据域中传递的数据,响应报文应该与请求报文全部一致
    在这里插入图片描述

    (0001)重新启动通信选项

    必须初始化和重新启动远程设备串行链路端口,并且清除所有通信事件计数器。如果端口正在只听模式下,不返回响应,它是唯一的能将端口带出只听模式的功能,如果端口不是处于只听模式,返回正常的响应,在进行重新启动之前,会出现这种状况

    当远程设备接收到请求时,它设法进行重新启动,并进行加电确认测试,成功的完成测试将端口带入联机状态

    请求数据域内容是十六进制0xFF00时,能够清除端口通信事件记录,内容是0x0000时,保持记录与重新启动之前相同
    在这里插入图片描述

    (0002)返回诊断寄存器

    在响应中返回远程设备的16位诊断寄存器内容
    在这里插入图片描述

    (0003)改变ASCII输入分隔符

    请求数据域中传递的字符“CHAR”作为报文结束分隔符(替代默认的LF字符),为以后报文使用,在ASCII报文结束不要求换行的情况下,使用这种功能
    在这里插入图片描述

    (0004)强制只听模式

    强制被寻址的远程设备进入Modbus通信的只听模式,这就使远程设备与网络中的其他设备断开,允许网络中其他未被强制只听的设备继续通信,没有返回响应

    当远程设备进入只听模式时,关闭所有激活的通信控制,允许就绪看门狗定时器超时,锁定控制关断,当设备在这种模式下,监视对设备寻址的报文和广播,但不进行任何操作,不发送任何响应

    进入这种模式后处理的唯一功能是重新启动通信选项
    在这里插入图片描述

    (000A)清除计数器和诊断寄存器

    目的是清除所有计数器和诊断寄存器,上电时也会清除计数器
    在这里插入图片描述

    (000B)返回总线报文计数

    响应数据域返回上一次重启动、清除计数器操作或加电之后远程设备在通信系统中检测到的报文数量
    在这里插入图片描述

    (000C)返回总线通信错误计数

    响应数据域返回上一次重新启动、清除计数器操作或加电之后远程设备遇到的CRC错误数量
    在这里插入图片描述

    (000D)返回总线异常错误计数

    响应数据域返回上一次重新启动、清除计数器操作或加电之后远程设备Modbus异常响应数量
    在这里插入图片描述

    (000E)返回从站报文计数

    响应数据域返回上一次重新启动、清除计数器操作或加电之后对远程设备寻址的报文数量或远程设备处理的广播报文数量
    在这里插入图片描述

    (000F)返回从站无响应计数

    响应数据域返回上一次重启动、清除计数器或加电之后对没有返回响应(既没有正常响应又没有异常响应)的远程设备寻址的报文数量
    在这里插入图片描述

    (0011)返回从站忙计数

    返回数据域返回上一次重启动、清除计数器操作或加电之后对返回从站设备忙异常响应的远程设备寻址的报文数量
    在这里插入图片描述

    (0012)返回总线字符超限计数

    响应数据域返回上一次重启动、清除计数器操作或加电之后,对由于字符超限状况而无法处理的远程设备寻址的报文数量,字符抵达端口的速度高于存储字符的速度,或者由于硬件故障而丢失字符均产生字符超限
    在这里插入图片描述

    (0014)清除超限计数器和标志

    清除超限错误计数器,并复位错误标志
    在这里插入图片描述

    实例与状态图

    下图是一个请求远程设备返回询问数据的实例,它使用子功能码0x000,用两字节数据域0xA537发送返回的数据
    在这里插入图片描述
    在这里插入图片描述

    (0x0B)获得通信事件计数器(仅用于串行链路)

    使用这个功能码从远程设备通信事件计数器中获取状态字和事件计数,通过在一系列报文之前和之后获取的当前计数,客户机可以确定远程设备是否正确的处理报文

    每次成功的完成报文传输就将远程事件计数器加1,异常响应、轮询命令或读事件计数器命令不增加计数器

    利用重新启动通信选项的子功能码(0001),或者清除计数器和诊断寄存器(000A),复位事件计数器

    正常响应包括两字节状态字和两字节事件计数,如果远程设备一直在处理前面发出的程序命令(存在忙状态),那么状态字将全为1(FFFF),否则状态字全为0

    mb_req_pdu/ mb_rsp_pdu/ mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求获得远程设备中通信事件计数器的实例
    在这里插入图片描述
    在这里插入图片描述

    (0x0C)获得通信事件记录(仅用于串行链路)

    使用这个功能码从远程设备中获得状态字、事件计数、报文计数以及一个事件字节域

    该状态字和事件计数与获得通信事件计数器(0x0B)返回的信息一致

    报文计数器包含上一次重启动、清除计数器操作或加电之后远程设备处理的报文数量,报文计数与诊断功能(0x08)子功能总线报文计数(0x0B)返回的报文一致

    事件字节域包含0-64个字节,每个字节与远程设备的一个Modbus发送或接收操作状态对应,远程设备按时间顺序将事件放入域中,字节0是最新事件,新字节替代域中的旧字节

    正常响应包括2字节状态字、2字节事件计数、2字节报文计数、一个含有0-64个事件字节的域和1个字节计数域,包括这四个域的总数据长度

    mb_req_pdu/mb_rsp_pdu/mb_excep_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求获得远程设备中通信事件记录的实例
    在这里插入图片描述
    在这个实例中,状态字是十六进制0x00,表示远程设备没有正在处理程序功能,事件计数表示了远程设备已经计数0x0108个事件,报文计数表示了已经处理了0x0121个报文

    用事件0字节表示最新通信事件,最新通信事件的内容0x20表示远程设备最近进入了只听模式
    用事件1字节表示前一个事件,前一个事件的内容0x00表示远程设备接收一个通信重启动

    下面说明响应事件字节的格式
    由获得通信事件记录功能返回的事件字节可以是以下四种类型中的任何一种,每个字节中的BIT7定义了这种类型,还可以由BIT6进一步定义:

    1. 远程设备Modbus接收事件
      当远程设备收到询问报文时,存储这种类型的事件字节,在远程设备处理报文之前存储,通过将BIT7设置为逻辑1定义这种事件,其他位在相应状态为真时被设置为逻辑1
      在这里插入图片描述
    2. 远程设备Modbus发送事件
      当远程设备处理完请求报文时,存储这种类型的事件字节,远程设备返回正常响应、异常响应或者没有响应后存储,通过将BIT7 置为逻辑0,BIT6置为逻辑1定义这种事件,其他位在相应状态为真时设置为逻辑1
      在这里插入图片描述
    3. 远程设备进入只听模式
      当远程设备进入只听模式时,存储种类型的事件字节,内容十六进制0x04定义了这个时间
    4. 远程设备开始重启通信
      当远程设备重新启动通信端口时,存储这种类型的事件字节,通过附带子功能重新启动通信选项的诊断功能可以重新启动远程设备
      该功能还能将远程设备置于“错误状态下继续”或“错误状态下停止”模式,如果将设备置于“错误状态下继续”模式,则将事件字节加入现存事件记录汇总,如果将设备置于“错误状态下停止”模式,则将事件字节加入事件记录中,并将其与记录清0
      内容0定义了该事件
      在这里插入图片描述

    (0x0F)写多个线圈

    该功能码将一个远程设备中的一个线圈序列的每个线圈强制为ON/OFF,请求PDU指定了被强制的线圈编号,从0开始寻址线圈

    请求数据域的内容指定了被请求的ON/OFF状态,逻辑1=ON,逻辑0=OFF

    正常的响应返回功能码、起始地址和被强制的线圈数量

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求从线圈20开始写入10个线圈的实例
    在这里插入图片描述
    传输的第一个字节0xCD对应线圈为27-20,LSB对应20
    在这里插入图片描述

    (0x10)写多个寄存器

    使用该功能码在一个远程设备中写连续寄存器块(1-123) 寄存器

    在请求数据域中指定了请求写入的值,将数据打包成每寄存器2字节

    正常的响应返回功能码、起始地址和被写入寄存器的数量

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求将0x000A和0x0102写入以2开始的两个寄存器的实例
    在这里插入图片描述
    在这里插入图片描述

    (0x11)报告从站ID(仅用于串行链路)

    使用这个功能码读远程设备特定的类型描述、当前状态以及其他信息

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求报告ID和状态的实例
    在这里插入图片描述

    (0x14/0x06)读文件记录

    使用该功能码读文件记录,根据字节数量提供所有请求数据长度,并且根据寄存器提供所有记录长度

    文件是记录的结构,每个文件包括10000个记录,寻址这些记录为0-9999,记录12的寻址为12(非从0计数)

    该功能可以读多个参量组,这些组可以是分散的,但每组中的参量必须是连续的

    用含有7个字节的单独的“子请求”域定义每个组:
    -参量类型:1个字节,必须指定为6
    -文件号:2个字节
    -文件中的起始记录号:2个字节
    -所读记录的长度:2个字节

    被读取的的寄存器数量加上预期响应中的所有其他域不能超过Modbus报文PDU允许的长度:253字节

    正常的响应是一系列“子响应”与“子请求”一一对应,字节计数域是所有“子响应”中的全部字节数,另外每个“子响应”都包括一个表示自身字节计数的域

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求从远程设备读两个参量组的实例:
    组1包括文件4中的2个寄存器,以寄存器1开始(地址0001)
    组2包括文件3中的2个寄存器,以寄存器9开始(地址0009)
    在这里插入图片描述
    在这里插入图片描述

    (0x15/0x06)写文件记录

    使用该功能码进行文件记录写入

    文件是记录的结构,每个文件包括10000个记录,寻址这些记录为0-9999

    该功能可以写多个参量组,这些组可以是分散的即不连续的,但每组内的参量必须是连续的

    用含有7个字节的“子请求”域定义每个组:
    参量类型:1字节,必须指定为6
    文件号:2字节
    文件中起始记录号:2字节
    被写入的数据:每个寄存器为2字节

    被写入的寄存器数量加上询问中的所有其他域不能超过Modbus报文PDU允许的长度:253字节
    正常的响应是请求的复制

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述
    在这里插入图片描述

    实例与状态图

    下图是一个请求将一个参考组写入远程设备的实例:
    组包括文件4中的3个寄存器,以寄存器7开始
    在这里插入图片描述‘’

    屏蔽写寄存器

    该功能码用于通过利用“AND”屏蔽、“OR”屏蔽以及当前寄存器内容的组合来修改保持寄存器的内容,使用这个功能设置或清除寄存器中不同的位

    请求指定了被写入的保持寄存器,被用于“AND”屏蔽的数据和被用于“OR”屏蔽的数据

    从0开始寻址寄存器,因此寄存器编号为1-16的寻址为0-15
    在这里插入图片描述

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个利用屏蔽值在远程设备中对寄存器5的屏蔽写入的实例:
    在这里插入图片描述
    在这里插入图片描述

    (0x17)读/写多个寄存器

    这个功能码实现了在一个单独的Modbus事务中一个读操作和一个写操作的组合,在读之前进行写操作

    从0开始寻址保持寄存器,因此寄存器编号为1-16的寻址为0-15

    请求指定了被读取的保持寄存器的起始地址和数目,以及被写入的保持寄存器的起始地址和数目,在写数据域中,字节计数指定随后的字节数目

    正常的响应包括所读寄存器组中的数据,在读数据域中,字节计数指定了随后的自己数目

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个请求从寄存器4开始读6个寄存器并且从寄存器15开始写3个寄存器的实例:
    在这里插入图片描述
    在这里插入图片描述

    (0x18)读FIFO队列

    这个功能码允许读远程设备中的FIFO寄存器队列内容,这个功能码返回队列中寄存器的计数,随后为队列的数据,最大可以读32个寄存器:计数+31个寄存器,首先返回队列计数寄存器然后是队列数据寄存器

    这个功能码读取队列内容,但是不能清除队列内容

    在正常的响应中,字节计数标识了随后的字节数量,包括队列计数字节和数值寄存器字节(不包括差错校验)

    队列计数是队列中数据寄存器的数量,不包括计数寄存器

    如果队列计数大于31,返回一个含有差错码03的异常响应

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    实例与状态图

    下图是一个对远程设备读FIFO队列请求的实例,请求读寄存器0x04DE开始的队列
    在这里插入图片描述
    在这个实例中,FIFO指针寄存器返回的队列计数器值0x02,队列计数后面是两个数据寄存器0x01B8和0x1284
    在这里插入图片描述

    (0x2B)封装接口传输

    Modbus封装接口(MEI)传输是一个在Modbus PDU内部将服务请求、方法调用和相关返回隧道化机制

    MEI传输的主要特征是封装作为已定义接口的一部分方法调用或服务请求,以及方法调用返回和服务响应
    在这里插入图片描述
    网络接口可以是用于发送Modbus PDU的任何通信栈,诸如:TCP/IP或穿行链路

    MEI类型是Modbus指配的编号,因此它是唯一的,值1-127是公共的,值128-255是用户定义的

    MEI传输实现使用MEI类型来分派对所指定的方法调用

    由于MEI传输服务是接口未知的,因此接口所需的任何特性或策略需由该接口提供,例如MEI事务处理和MEI接口差错处理等

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    (0x2B/0x0E)读设备标识

    这个功能码允许读取与远程设备物理和功能描述相关的标识和附加信息

    读设备标识接口由一组可寻址的数据元素组成的地址空间构成,数据元素被称作对象,由对象ID标识他们

    接口由3类对象组成:

    1. 基本设备标识:所有此种对象都是必备的:厂商名称、产品代码和修订版本号
    2. 常规设备标识:除基本数据对象外,设备提供了附加的可选择的标识以及数据对象描述
    3. 扩展设备标识:除常规数据对象外,设备提供了附加的可选择的标识以及专用数据描述

    在这里插入图片描述

    mb_req_pdu/mb_rsp_pdu/mb_excep_rsp_pdu

    在这里插入图片描述

    请求参数描述

    功能码:0x2B

    MEI类型:Modbus指配的编号

    ReadDevId码:允许定义四种访问类型:
    01:请求获得基本设备标识(流访问)
    02:请求获得常规设备标识(流访问)
    03:请求获得扩展设备标识(流访问)
    04:请求获得特定标识对象(单个访问)

    如果ReadDevId是无效的,在响应中返回异常码03

    在设备标识不适用单独响应的情况下,需要进行多个事务处理,对象ID字节给出了要获得的第一个对象标识,对于一个事物处理来说,客户机必须设置对象ID为0,以便获得设备标识数据的起始信息,对于随后事务处理来说,客户机必须设置对象ID为服务器在此前响应中的返回值

    如果流访问中对象ID不匹配任何已知对象,那么服务器的响应如同指向对象0

    在单个访问的情况下:ReadDevId代码04,请求中的对象ID给出了要获取的对象标识

    如果在单个访问中对象ID不匹配任何已知对象,那么服务器返回一个异常码02(非法数据地址)的异常响应

    如果服务器设备被要求高于其一致性的描述,服务器必须根据其对应的实际等级响应

    响应参数描述

    功能码:0x2B

    ReadDevId码:与请求的ReadDevID码相同:01、02、03或04

    一致性等级:设备的标识一致性等级和支持访问的类型
    0x01:基本标识(仅流访问)
    0x02:常规标识(仅流访问)
    0x03:扩展标识(仅流访问)
    0x81:基本标识(流访问和单个访问)
    0x82:常规标识(流访问和单个访问)
    0x83:扩展标识(流访问和单个访问)

    接续标识:在ReadDevId码01、02或03(流访问)的情况下,在标识数据不适用单独响应的情况下,需要进行多个事物处理
    0x00:没有后续对象
    0xFF:有后续对象,并且需要更多Modbus事务处理
    在ReadDevId码04(单个访问)情况下,必须设置这个域为0

    下一个对象ID:如果“接续标识=0xFF”,那么该值就是请求下一个对象的标识码
    如果“接续标识=0x00”,那么必须设置为0

    对象数目:在响应中返回的对象标识数目(对于单个访问,对象数目=1)

    对象0.Id:PDU中返回的第一个对象识标识(流访问)或请求对象的标识(单个访问)
    对象0.长度:第一个对象的字节长度
    对象0.值:第一个对象的值

    对象N.id:在响应中最后对象的标识
    对象N.长度:最后对象的字节长度
    对象N.值:最后对象的值

    实例与状态图

    下图是一个读基本设备标识的实例,一个响应PDU中发送的所有报文
    在这里插入图片描述
    如果一个设备需要多个事务处理发送响应,那么启动随后事务处理
    第一个事务处理
    在这里插入图片描述
    在这里插入图片描述
    第二个事务处理
    在这里插入图片描述
    在这里插入图片描述

    Modbus异常响应

    当客户机设备想服务器设备发送请求时,客户机希望得到一个正常的响应,主站的询问可能导致下列四种事件之一:

    1. 如果服务器设备接收无通信错误的请求,并且可以正常的处理询问,那么服务器将返回一个正常响应
    2. 如果由于通信错误,服务器没有收到请求,那么不能返回响应,客户机程序将视为超时
    3. 如果服务器收到请求,但是检测到一个通信错误(奇偶校验、LRC、CRC…)那么不能返回响应,客户机视为超时
    4. 如果服务器收到无通信错误的请求,但不能处理这个请求(例如请求读一个不存在输出或保持寄存器),服务器将返回一个异常响应,通知客户机错误的实际情况

    异常响应有2个与正常响应不同的域:

    1. 功能码域:在正常的响应中,服务器响应的功能码复制请求功能码,所有功能码的MSB都为0,在异常响应中,服务器设置功能码的MSB为1
    2. 数据域:在正常的响应中,服务器可以在数据域返回数据或统计信息,在异常响应中服务器在数据域返回异常码

    异常码表

    在这里插入图片描述

    展开全文
  • Modbus协议讲解及实现

    千人学习 2020-03-31 10:55:20
    本课程主讲老师集10多年在Modbus协议学习、使用中的经验心得,结合当前物联网浪潮下Modbus协议开发的痛点,推出这套面向Modbus 协议初学者的课程。本课程不同于以往市面课程只是协议讲解无实现代码,而是采用讲解与...
  • Modbus通信协议详解

    千次阅读 2018-06-15 14:43:39
    一、Modbus 协议简介    Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。它已经成为一通用工业标准。有了它,不同...

    一、Modbus 协议简介

     

     Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。它已经成为一通用工业标准。有了它,不同厂商生产的控制设备可以连成工业网络,进行集中监控。

     

     此协议定义了一个控制器能认识使用的消息结构,而不管它们是经过何种网络进行通信的。它描述了一控制器请求访问其它设备的过程,如果回应来自其它设备的请求,以及怎样侦测错误并记录。它制定了消息域格局和内容的公共格式。

     

     当在一Modbus网络上通信时,此协议决定了每个控制器须要知道它们的设备地址,识别按地址发来的消息,决定要产生何种行动。如果需要回应,控制器将生成反馈信息并用Modbus协议发出。在其它网络上,包含了Modbus协议的消息转换为在此网络上使用的帧或包结构。这种转换也扩展了根据具体的网络解决节地址、路由路径及错误检测的方法。

    1、在Modbus网络上转输

     标准的Modbus口是使用一RS-232C兼容串行接口,它定义了连接口的针脚、电缆、信号位、传输波特率、奇偶校验。控制器能直接或经由Modem组网。

     

     控制器通信使用主—从技术,即仅一设备(主设备)能初始化传输(查询)。其它设备(从设备)根据主设备查询提供的数据作出相应反应。典型的主设备:主机和可编程仪表。典型的从设备:可编程控制器。

     

     主设备可单独和从设备通信,也能以广播方式和所有从设备通信。如果单独通信,从设备返回一消息作为回应,如果是以广播方式查询的,则不作任何回应。Modbus协议建立了主设备查询的格式:设备(或广播)地址、功能代码、所有要发送的数据、一错误检测域。

     

     从设备回应消息也由Modbus协议构成,包括确认要行动的域、任何要返回的数据、和一错误检测域。如果在消息接收过程中发生一错误,或从设备不能执行其命令,从设备将建立一错误消息并把它作为回应发送出去。

    2、在其它类型网络上转输

     在其它网络上,控制器使用对等技术通信,故任何控制都能初始和其它控制器的通信。这样在单独的通信过程中,控制器既可作为主设备也可作为从设备。提供的多个内部通道可允许同时发生的传输进程。

     

     在消息位,Modbus协议仍提供了主—从原则,尽管网络通信方法是“对等”。如果一控制器发送一消息,它只是作为主设备,并期望从从设备得到回应。同样,当控制器接收到一消息,它将建立一从设备回应格式并返回给发送的控制器。

    3、查询—回应周期


    (1)查询

     查询消息中的功能代码告之被选中的从设备要执行何种功能。数据段包含了从设备要执行功能的任何附加信息。例如功能代码03是要求从设备读保持寄存器并返回它们的内容。数据段必须包含要告之从设备的信息:从何寄存器开始读及要读的寄存器数量。错误检测域为从设备提供了一种验证消息内容是否正确的方法。

    (2)回应

     如果从设备产生一正常的回应,在回应消息中的功能代码是在查询消息中的功能代码的回应。数据段包括了从设备收集的数据:象寄存器值或状态。如果有错误发生,功能代码将被修改以用于指出回应消息是错误的,同时数据段包含了描述此错误信息的代码。错误检测域允许主设备确认消息内容是否可用。

    二、两种传输方式

     控制器能设置为两种传输模式(ASCII或RTU)中的任何一种在标准的Modbus网络通信。用户选择想要的模式,包括串口通信参数(波特率、校验方式等),在配置每个控制器的时候,在一个Modbus网络上的所有设备都必须选择相同的传输模式和串口参数。

    ASCII模式

    : 地址 功能代码 数据数量 数据1 数据n LRC高字节 LRC低字节 回车 换行

    RTU模式

    地址 功能代码 数据数量 数据1 数据n CRC低字节 CRC高字节

     所选的ASCII或RTU方式仅适用于标准的Modbus网络,它定义了在这些网络上连续传输的消息段的每一位,以及决定怎样将信息打包成消息域和如何解码。

     在其它网络上(象MAP和Modbus Plus)Modbus消息被转成与串行传输无关的帧。

    1、ASCII模式

     当控制器设为在Modbus网络上以ASCII(美国标准信息交换代码)模式通信,在消息中的每个8Bit字节都作为两个ASCII字符发送。这种方式的主要优点是字符发送的时间间隔可达到1秒而不产生错误。

     代码系统

    • 十六进制,ASCII字符0…9,A…F
    • 消息中的每个ASCII字符都是一个十六进制字符组成

      每个字节的位

    • 1个起始位
    • 7个数据位,最小的有效位先发送
    • 1个奇偶校验位,无校验则无
    • 1个停止位(有校验时),2个Bit(无校验时)

     错误检测域

    • LRC(纵向冗长检测)

    2、RTU模式

     当控制器设为在Modbus网络上以RTU(远程终端单元)模式通信,在消息中的每个8Bit字节包含两个4Bit的十六进制字符。这种方式的主要优点是:在同样的波特率下,可比ASCII方式传送更多的数据。

    代码系统

    • 8位二进制,十六进制数0…9,A…F
    • 消息中的每个8位域都是一个两个十六进制字符组成
    • 每个字节的位
    • 1个起始位
    • 8个数据位,最小的有效位先发送
    • 1个奇偶校验位,无校验则无
    • 1个停止位(有校验时),2个Bit(无校验时)

      错误检测域

    • CRC(循环冗长检测)

    三、Modbus消息帧

     两种传输模式中(ASCII或RTU),传输设备以将Modbus消息转为有起点和终点的帧,这就允许接收的设备在消息起始处开始工作,读地址分配信息,判断哪一个设备被选中(广播方式则传给所有设备),判知何时信息已完成。部分的消息也能侦测到并且错误能设置为返回结果。

    1、ASCII帧

     使用ASCII模式,消息以冒号(:)字符(ASCII码 3AH)开始,以回车换行符结束(ASCII码 0DH,0AH)。

     

     其它域可以使用的传输字符是十六进制的0…9,A…F。网络上的设备不断侦测“:”字符,当有一个冒号接收到时,每个设备都解码下个域(地址域)来判断是否发给自己的。

     

     消息中字符间发送的时间间隔最长不能超过1秒,否则接收的设备将认为传输错误。一个典型消息帧如下所示:

    起始位 设备地址 功能代码 数据 LRC校验 结束符
    1个字符 2个字符 2个字符 n个字符 2个字符 2个字符

    图2 ASCII消息帧

    2、RTU帧

     使用RTU模式,消息发送至少要以3.5个字符时间的停顿间隔开始。在网络波特率下多样的字符时间,这是最容易实现的(如下图的T1-T2-T3-T4所示)。传输的第一个域是设备地址。可以使用的传输字符是十六进制的0…9,A…F。网络设备不断侦测网络总线,包括停顿间隔时间内。当第一个域(地址域)接收到,每个设备都进行解码以判断是否发往自己的。在最后一个传输字符之后,一个至少3.5个字符时间的停顿标定了消息的结束。一个新的消息可在此停顿后开始。

     

     整个消息帧必须作为一连续的流转输。如果在帧完成之前有超过1.5个字符时间的停顿时间,接收设备将刷新不完整的消息并假定下一字节是一个新消息的地址域。同样地,如果一个新消息在小于3.5个字符时间内接着前个消息开始,接收的设备将认为它是前一消息的延续。这将导致一个错误,因为在最后的CRC域的值不可能是正确的。一典型的消息帧如下所示:

     

    起始位 设备地址 功能代码 数据 CRC校验 结束符
    T1-T2-T3-T4 8Bit 8Bit n个8Bit 16Bit T1-T2-T3-T4

    图3 RTU消息帧

    3、地址域

     消息帧的地址域包含两个字符(ASCII)或8Bit(RTU)。可能的从设备地址是0…247 (十进制)。单个设备的地址范围是1…247。主设备通过将要联络的从设备的地址放入消息中的地址域来选通从设备。当从设备发送回应消息时,它把自己的地址放入回应的地址域中,以便主设备知道是哪一个设备作出回应。

     

     地址0是用作广播地址,以使所有的从设备都能认识。当Modbus协议用于更高水准的网络,广播可能不允许或以其它方式代替。

    4、如何处理功能域

     消息帧中的功能代码域包含了两个字符(ASCII)或8Bits(RTU)。可能的代码范围是十进制的1…255。当然,有些代码是适用于所有控制器,有此是应用于某种控制器,还有些保留以备后用。

     

     当消息从主设备发往从设备时,功能代码域将告之从设备需要执行哪些行为。例如去读取输入的开关状态,读一组寄存器的数据内容,读从设备的诊断状态,允许调入、记录、校验在从设备中的程序等。

     

     当从设备回应时,它使用功能代码域来指示是正常回应(无误)还是有某种错误发生(称作异议回应)。对正常回应,从设备仅回应相应的功能代码。对异议回应,从设备返回一等同于正常代码的代码,但最重要的位置为逻辑1。

     例如:一从主设备发往从设备的消息要求读一组保持寄存器,将产生如下功能代码:

    0 0 0 0 0 0 1 1 (十六进制03H)

     对正常回应,从设备仅回应同样的功能代码。对异议回应,它返回:

    1 0 0 0 0 0 1 1 (十六进制83H)

     除功能代码因异议错误作了修改外,从设备将一独特的代码放到回应消息的数据域中,这能告诉主设备发生了什么错误。

     主设备应用程序得到异议的回应后,典型的处理过程是重发消息,或者诊断发给从设备的消息并报告给操作员。

    5、数据域

     数据域是由两个十六进制数集合构成的,范围00…FF。根据网络传输模式,这可以是由一对ASCII字符组成或由一RTU字符组成。

     从主设备发给从设备消息的数据域包含附加的信息:从设备必须用于进行执行由功能代码所定义的所为。这包括了象不连续的寄存器地址,要处理项的数目,域中实际数据字节数。

     例如,如果主设备需要从设备读取一组保持寄存器(功能代码03),数据域指定了起始寄存器以及要读的寄存器数量。如果主设备写一组从设备的寄存器(功能代码10十六进制),数据域则指明了要写的起始寄存器以及要写的寄存器数量,数据域的数据字节数,要写入寄存器的数据。

     如果没有错误发生,从从设备返回的数据域包含请求的数据。如果有错误发生,此域包含一异议代码,主设备应用程序可以用来判断采取下一步行动。

     在某种消息中数据域可以是不存在的(0长度)。例如,主设备要求从设备回应通信事件记录(功能代码0B十六进制),从设备不需任何附加的信息。

    6、错误检测域

     标准的Modbus网络有两种错误检测方法。错误检测域的内容视所选的检测方法而定。

    ASCII

     当选用ASCII模式作字符帧,错误检测域包含两个ASCII字符。这是使用LRC(纵向冗长检测)方法对消息内容计算得出的,不包括开始的冒号符及回车换行符。LRC字符附加在回车换行符前面。

    RTU

     当选用RTU模式作字符帧,错误检测域包含一16Bits值(用两个8位的字符来实现)。错误检测域的内容是通过对消息内容进行循环冗长检测方法得出的。CRC域附加在消息的最后,添加时先是低字节然后是高字节。故CRC的高位字节是发送消息的最后一个字节。

    7、字符的连续传输

     当消息在标准的Modbus系列网络传输时,每个字符或字节以如下方式发送(从左到右):

     最低有效位…最高有效位

     使用ASCII字符帧时,位的序列是:

     有奇偶校验

    启始位 1 2 3 4 5 6 7 奇偶位 停止位

     无奇偶校验

    启始位 1 2 3 4 5 6 7 停止位 停止位

    图4. 位顺序(ASCII)

     使用RTU字符帧时,位的序列是:

     有奇偶校验

    启始位 1 2 3 4 5 6 7 8 奇偶位 停止位

     无奇偶校验

    启始位 1 2 3 4 5 6 7 8 停止位 停止位

    图4. 位顺序(RTU)

    四、错误检测方法

     标准的Modbus串行网络采用两种错误检测方法。奇偶校验对每个字符都可用,帧检测(LRC或CRC)应用于整个消息。它们都是在消息发送前由主设备产生的,从设备在接收过程中检测每个字符和整个消息帧。

     用户要给主设备配置一预先定义的超时时间间隔,这个时间间隔要足够长,以使任何从设备都能作为正常反应。如果从设备测到一传输错误,消息将不会接收,也不会向主设备作出回应。这样超时事件将触发主设备来处理错误。发往不存在的从设备的地址也会产生超时。

    1、奇偶校验

     用户可以配置控制器是奇或偶校验,或无校验。这将决定了每个字符中的奇偶校验位是如何设置的。

     如果指定了奇或偶校验,“1”的位数将算到每个字符的位数中(ASCII模式7个数据位,RTU中8个数据位)。例如RTU字符帧中包含以下8个数据位:

    1 1 0 0 0 1 0 1

     整个“1”的数目是4个。如果便用了偶校验,帧的奇偶校验位将是0,便得整个“1”的个数仍是4个。如果便用了奇校验,帧的奇偶校验位将是1,便得整个“1”的个数是5个。

     如果没有指定奇偶校验位,传输时就没有校验位,也不进行校验检测。代替一附加的停止位填充至要传输的字符帧中。

     

    2、LRC检测

     

     使用ASCII模式,消息包括了一基于LRC方法的错误检测域。LRC域检测了消息域中除开始的冒号及结束的回车换行号外的内容。

     LRC域是一个包含一个8位二进制值的字节。LRC值由传输设备来计算并放到消息帧中,接收设备在接收消息的过程中计算LRC,并将它和接收到消息中LRC域中的值比较,如果两值不等,说明有错误。

     LRC方法是将消息中的8Bit的字节连续累加,丢弃了进位。

     LRC简单函数如下:

    static unsigned char LRC(auchMsg,usDataLen)

    unsigned char *auchMsg ; /* 要进行计算的消息 */

    unsigned short usDataLen ; /* LRC 要处理的字节的数量*/

    { unsigned char uchLRC = 0 ; /* LRC 字节初始化 */

    while (usDataLen–) /* 传送消息 */

    uchLRC += *auchMsg++ ; /* 累加*/

    return ((unsigned char)(-((char_uchLRC))) ;

    }

    3、CRC检测

     使用RTU模式,消息包括了一基于CRC方法的错误检测域。CRC域检测了整个消息的内容。

     CRC域是两个字节,包含一16位的二进制值。它由传输设备计算后加入到消息中。接收设备重新计算收到消息的CRC,并与接收到的CRC域中的值比较,如果两值不同,则有误。

     CRC是先调入一值是全“1”的16位寄存器,然后调用一过程将消息中连续的8位字节各当前寄存器中的值进行处理。仅每个字符中的8Bit数据对CRC有效,起始位和停止位以及奇偶校验位均无效。

     CRC产生过程中,每个8位字符都单独和寄存器内容相或(OR),结果向最低有效位方向移动,最高有效位以0填充。LSB被提取出来检测,如果LSB为1,寄存器单独和预置的值或一下,如果LSB为0,则不进行。整个过程要重复8次。在最后一位(第8位)完成后,下一个8位字节又单独和寄存器的当前值相或。最终寄存器中的值,是消息中所有的字节都执行之后的CRC值。

     CRC添加到消息中时,低字节先加入,然后高字节。

     

     

     

     

    MODBUS通讯协议及编程【二】

      ModBus通讯协议分为RTU协议和ASCII协议,我公司的多种仪表都采用ModBus RTU通讯协议,如:CH2000智能电力监测仪、CH2000M电力参数采集模块、巡检表、数显表、光柱数显表等。下面就ModBus RTU协议简要介绍如下:

    一、通讯协议

    (一)、通讯传送方式
       通讯传送分为独立的信息头,和发送的编码数据。以下的通讯传送方式定义也与MODBUS RTU通讯规约相兼容:

    编 码


    8位二进制


    起始位


    1位


    数据位


    8位


    奇偶校验位


    1位(偶校验位)


    停止位


    1位


    错误校检


    CRC(冗余循环码)


    初始结构 = ≥4字节的时间
    地址码 = 1 字节
    功能码 = 1 字节
    数据区 = N 字节
    错误校检 = 16位CRC码
    结束结构 = ≥4字节的时间


      地址码:地址码为通讯传送的第一个字节。这个字节表明由用户设定地址码的从机将接收由主机发送来的信息。并且每个从机都有具有唯一的地址码,并且响应回送均以各自的地址码开始。主机发送的地址码表明将发送到的从机地址,而从机发送的地址码表明回送的从机地址。

      功能码:通讯传送的第二个字节。ModBus通讯规约定义功能号为1到127。本仪表只利用其中的一部分功能码。作为主机请求发送,通过功能码告诉从机执行什么动作。作为从机响应,从机发送的功能码与从主机发送来的功能码一样,并表明从机已响应主机进行操作。如果从机发送的功能码的最高位为1(比如功能码大与此同时127),则表明从机没有响应操作或发送出错。

      数据区:数据区是根据不同的功能码而不同。数据区可以是实际数值、设置点、主机发送给从机或从机发送给主机的地址。

       CRC码:二字节的错误检测码。

    (二)、通讯规约:

       当通讯命令发送至仪器时,符合相应地址码的设备接通讯命令,并除去地址码,读取信息,如果没有出错,则执行相应的任务;然后把执行结果返送给发送者。返送的信息中包括地址码、执行动作的功能码、执行动作后结果的数据以及错误校验码。如果出错就不发送任何信息。

    1.信息帧结构

    地址码


    功能码


    数据区


    错误校验码


    8位


    8位


    N × 8位


    16位


      地址码:地址码是信息帧的第一字节(8位),从0到255。这个字节表明由用户设置地址的从机将接收由主机发送来的信息。每个从机都必须有唯一的地址码,并且只有符合地址码的从机才能响应回送。当从机回送信息时,相当的地址码表明该信息来自于何处。

       功能码:主机发送的功能码告诉从机执行什么任务。表1-1列出的功能码都有具体的含义及操作。

    代码


    含义


    操作


    03
    读取数据
    读取当前寄存器内一个或多个二进制值
    06
    重置单一寄存器
    把设置的二进制值写入单一寄存器

      数据区:数据区包含需要从机执行什么动作或由从机采集的返送信息。这些信息可以是数值、参考地址等等。例如,功能码告诉从机读取寄存器的值,则数据区必需包含要读取寄存器的起始地址及读取长度。对于不同的从机,地址和数据信息都不相同。

      错误校验码:主机或从机可用校验码进行判别接收信息是否出错。有时,由于电子噪声或其它一些干扰,信息在传输过程中会发生细微的变化,错误校验码保证了主机或从机对在传送过程中出错的信息不起作用。这样增加了系统的安全和效率。错误校验采用CRC-16校验方法。

    注:信息帧的格式都基本相同:地址码、功能码、数据区和错误校验码。

    2.错误校验

       冗余循环码(CRC)包含2个字节,即16位二进制。CRC码由发送设备计算,放置于发送信息的尾部。接收信息的设备再重新计算接收到信息的 CRC码,比较计算得到的CRC码是否与接收到的相符,如果两者不相符,则表明出错。

      CRC码的计算方法是,先预置16位寄存器全为1。再逐步把每8位数据信息进行处理。在进行CRC码计算时只用8位数据位,起始位及停止位,如有奇偶校验位的话也包括奇偶校验位,都不参与CRC码计算。

       在计算CRC码时,8位数据与寄存器的数据相异或,得到的结果向低位移一字节,用0填补最高位。再检查最低位,如果最低位为1,把寄存器的内容与预置数相异或,如果最低位为0,不进行异或运算。

       这个过程一直重复8次。第8次移位后,下一个8位再与现在寄存器的内容相相异或,这个过程与以上一样重复8次。当所有的数据信息处理完后,最后寄存器的内容即为CRC码值。CRC码中的数据发送、接收时低字节在前。

       计算CRC码的步骤为:

    • 预置16位寄存器为十六进制FFFF(即全为1)。称此寄存器为CRC寄存器;
    • 把第一个8位数据与16位CRC寄存器的低位相异或,把结果放于CRC寄存器;
    • 把寄存器的内容右移一位(朝低位),用0填补最高位,检查最低位;
    • 如果最低位为0:重复第3步(再次移位); 如果最低位为1:CRC寄存器与多项式A001(1010 0000 0000 0001)进行异或;
    • 重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;
    • 重复步骤2到步骤5,进行下一个8位数据的处理;
    • 最后得到的CRC寄存器即为CRC码。

    3.功能码03,读取点和返回值:

      仪表采用Modbus RTU通讯规约,利用通讯命令,可以进行读取点(“保持寄存器”) 或返回值(“输入寄存器” )的操作。保持和输入寄存器都是16位(2字节)值,并且高位在前。这样用于仪表的读取点和返回值都是2字节。一次最多可读取寄存器数是60。由于一些可编程控制器不用功能码03,所以功能码03被用作读取点和返回值。从机响应的命令格式是从机地址、功能码、数据区及CRC码。数据区中的寄存器数据都是每两个字节高字节在前。

    4.功能码06,单点保存

      主机利用这条命令把单点数据保存到仪表的存储器。从机也用这个功能码向主机返送信息。

    二、编程举例

      下面是一个用VC编写的ModBus RTU通讯的例子

    (一)、通讯口设置

    DCB dcb;
    hCom=CreateFile(“COM1”,
         GENERIC_READ|GENERIC_WRITE,
         0,
         NULL,
         OPEN_EXISTING,
         0,
         NULL);
    if(hCom==INVALID_HANDLE_VALUE)
    {
      MessageBox(“createfile error,error”);
    }
    BOOL error=SetupComm(hCom,1024,1024);
    if(!error)
      MessageBox(“setupcomm error”);
    error=GetCommState(hCom,&dcb);
    if(!error)
      MessageBox(“getcommstate,error”);
    dcb.BaudRate=2400;
    dcb.ByteSize=8;

    dcb.Parity=EVENPARITY;//NOPARITY;
    dcb.StopBits=ONESTOPBIT;

    error=SetCommState(hCom,&dcb);

    (二)、CRC校验码计算

    UINT crc
    void calccrc(BYTE crcbuf)
    {
    BYTE i;

    crc=crc ^ crcbuf;
    for(i=0;i<8;i++)
    {
    BYTE TT;
    TT=crc&1;
    crc=crc>>1;
    crc=crc&0x7fff;
    if (TT==1)
    crc=crc^0xa001;
    crc=crc&0xffff;
    }
    }

    (三)、数据发送

    zxaddr=11;//读取地址为11的巡检表数据
    zxnum=10;//读取十个通道的数据

    writebuf2[0]=zxaddr;
    writebuf2[1]=3;
    writebuf2[2]=0;
    writebuf2[3]=0;
    writebuf2[4]=0;
    writebuf2[5]=zxnum;
    crc=0xffff;
    calccrc(writebuf2[0]);
    calccrc(writebuf2[1]);
    calccrc(writebuf2[2]);
    calccrc(writebuf2[3]);
    calccrc(writebuf2[4]);
    calccrc(writebuf2[5]);

    writebuf2[6]=crc & 0xff;
    writebuf2[7]=crc/0x100;
    WriteFile(hCom,writebuf2,8,&comnum,NULL);

    (四)、数据读取

    ReadFile(hCom,writebuf,5+zxnum*2,&comnum,NULL);//读取zxnum个通道数据
    可增加错误处理程序,如地址码错误、CRC码错误判断、通讯故障处理等。

     

     

     

     

     

     

     CRC简单函数如下:

    unsigned short CRC16(puchMsg, usDataLen)

    unsigned char *puchMsg ; /* 要进行CRC校验的消息 */

    unsigned short usDataLen ; /* 消息中字节数 */

    {

    unsigned char uchCRCHi = 0xFF ; /* 高CRC字节初始化 */

    unsigned char uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */

    unsigned uIndex ; /* CRC循环中的索引 */

    while (usDataLen–) /* 传输消息缓冲区 */

    {

    uIndex = uchCRCHi ^ *puchMsgg++ ; /* 计算CRC */

    uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex} ;

    uchCRCLo = auchCRCLo[uIndex] ;

    }

    return (uchCRCHi << 8 | uchCRCLo) ;

    }

    /* CRC 高位字节值表 */

    static unsigned char auchCRCHi[] = {

    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40

    } ;

    /* CRC低位字节值表*/

    static char auchCRCLo[] = {

    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,

    0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,

    0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,

    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,

    0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,

    0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,

    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,

    0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,

    0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,

    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,

    0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,

    0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,

    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,

    0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,

    0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,

    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,

    0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,

    0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,

    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,

    0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,

    0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,

    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,

    0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,

    0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,

    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,

    0x43, 0x83, 0x41, 0x81, 0x80, 0x40

    } ; 

            </div>
                </div>
    
    展开全文
  • Modbus协议(翻自wiki)

    千次阅读 2018-10-31 13:49:34
    Modbus 是一种串行通信协议,最初由Modicon(现为施耐德电气)于 1979 年出版,用于其可编程逻辑控制器( PLC )。 Modbus 已成为约定俗成的标准通信协议,现在已成为连接工业电子设备的常用方法。[^1] 在工业环境中...
  • MODBUS通讯协议及编程

    万次阅读 2018-05-27 19:39:56
    一、Modbus 协议简介 Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。它已经成为一通用工业标准。有了它,不同厂商生产的控制...
  • modbus通讯协议

    千次阅读 2019-07-03 16:17:50
    MODBUS协议最简单又是最直白的解释 2018年01月11日 10:00:36 JiaoCL 阅读数 16468 Modbus是一种单主站的主/从通信模式。Modbus网络上只能有一个主站存在,主站在 Modbus网络上没有地址,从站的地址范围为 0 - 247...
  • 简单粗暴的理解 MODBUS通信协议

    万次阅读 2018-07-06 13:20:02
    amp;wfr=spider&amp;for=pcMODBUS通讯协议,是1979年由美国Modicon 公司提出的,就是被...MODBUS是世界上第一个用于工业现场的总线协议,可以说,它的出现标志着工业现场从模拟量时代向通讯时代迈进。PLC之父 迪...
  • 寄存器PLC地址与寄存器modbus协议地址

    万次阅读 多人点赞 2020-04-23 23:54:16
    寄存器PLC地址指存放于控制器中的地址,这些控制器可以是PLC,也可以使触摸屏,或是文本... 寄存器modbus协议地址指通信时使用的寄存器地址,在实际编程中,由于寄存器PLC地址前缀的区分作用,所以只需说明后4位数...
  • linux下modbus协议应用

    2020-08-19 20:24:04
    1996年施耐德公司推出基于以太网TCP/IP的modbus协议:modbusTCP。 Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。 标准的Modbus协议物理层接口有RS232、RS422、RS485和以太网接口,采用...
  • Modbus协议中文版【完整版】

    千次下载 热门讨论 2020-07-30 21:56:54
    Modbus协议 通信协议 Modbus协议 通信协议 Modbus协议 通信协议
  • modbus协议-01

    2020-06-18 12:28:18
    modbus协议是工业上常用的一种通信协议,特别是在要求一个系统与多个从机系统进行数据通信时,使用modbus协议特别的方便。在之前的项目经历中,做了一个主控系统,带动两扇屏蔽门驱动系统的主从控制。关于对于modbus...
  • ModBus协议报文格式解析说明

    万次阅读 2018-12-10 15:33:22
     Modbus协议是一个master/slave架构的协议。有一个节点是master节点,其他使用Modbus协议参与通信的节点是slave节点。每一个slave设备都有一个唯一的地址。在串行和MB+网络中,只有被指定为主节点的节点可以启动一...
  • Modbus协议最初由Modicon公司开发出来,是针对PLC设备设计的基于串行总线的主从模式的应用层总线设备协议。ModbusTCP是封装在TCP包内的Modbus协议,虽然有一些变化,但是根本上还是主从模式。随着嵌入式技术的发展,...
  • RS485温湿度传感器与modbus协议一、modbus协议介绍二、modbus与RS485的关系三、传感器使用3.1传感器介绍3.1 PC机与传感器3.2 单片机与传感器三、modbus RTU常用功能码 一、modbus协议介绍 Modbus协议是应用于 电子...
  • Modbus 协议中文版完整版
  • Modbus协议的理解

    千次阅读 2019-08-12 19:29:34
    当上位机和下位机进行通讯或多个微控制器进行通讯时需要进行通讯,很常用的一种... 我采用了自己编的协议,但参考了Modbus协议Modbus协议包括ASCII、RTU、TCP等,并没有规定物理层。此协议定义了控制器能够认识...
  • 本人的视频课程<<Modbus 协议讲解及实现>>视频课程在CSDN发布。...本课程主讲老师集10多年在Modbus协议学习、使用中的经验心得,结合当前物联网浪潮下Modbus协议开发的痛点,推出这套面向Mod...
  • 一文看懂Modbus协议

    千次阅读 2020-04-05 11:34:17
    Modbus协议允许在各种网络体系结构内进行简单通信,每种设备(PLC、HMI、控制面板、驱动程序、动作控制、输入/输出设备)都能使用 Modbus协议来启动远程操作。在基于串行链路和以太 TCP/IP 网络的 Modbus上可以进行...
1 2 3 4 5 ... 20
收藏数 8,243
精华内容 3,297
关键字:

modbus协议