MODBUS通讯协议及编程 点击:1567 | 回复:9



墨之殇

    
  • 精华:16帖
  • 求助:7帖
  • 帖子:395帖 | 4077回
  • 年度积分:102
  • 历史总积分:26826
  • 注册:2011年8月26日
发表于:2011-12-11 20:51:59
楼主

MODBUS通讯协议及编程

 

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

一、通讯协议

 

(一)、通讯传送方式:

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

编 码

8位二进制

起始位

1

数据位

8

奇偶校验位

1位(偶校验位)

停止位

1

错误校检

CRC(冗余循环码)

初始结构 = 4字节的时间

地址码 = 1 字节

功能码 = 1 字节

数据区 = N 字节

错误校检 = 16CRC

结束结构 = 4字节的时间

 

 

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

 

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

 

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

 

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

 



楼主最近还看过



墨之殇

  • 精华:17帖
  • 求助:8帖
  • 帖子:451帖 | 4077回
  • 年度积分:15
  • 历史总积分:26901
  • 注册:2011年8月26日
发表于:2011-12-11 20:54:07
1楼

(二)、通讯规约:

 

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

 

1.  信息帧结构

地址码

功能码

数据区

错误校验码

8

8

N × 8

16

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

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

代码

含义

操作

03

读取数据

读取当前寄存器内一个或多个二进制值

06

重置单一寄存器

把设置的二进制值写入单一寄存器

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

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

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

2.错误校验

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

墨之殇

  • 精华:17帖
  • 求助:8帖
  • 帖子:451帖 | 4077回
  • 年度积分:15
  • 历史总积分:26901
  • 注册:2011年8月26日
发表于:2011-12-11 20:55:35
2楼

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

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

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

   计算CRC码的步骤为:

·         预置16位寄存器为十六进制FFFF(即全为1)。称此寄存器为CRC寄存器;

·         把第一个8位数据与16CRC寄存器的低位相异或,把结果放于CRC寄存器;

·         把寄存器的内容右移一位(朝低位),用0填补最高位,检查最低位;

·         如果最低位为0:重复第3(再次移位); 如果最低位为1CRC寄存器与多项式A0011010 0000 0000 0001)进行异或;

·         重复步骤34,直到右移8次,这样整个8位数据全部进行了处理;

·         重复步骤2到步骤5,进行下一个8位数据的处理;

·         最后得到的CRC寄存器即为CRC码。

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

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

4.功能码06,单点保存

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

二、编程举例

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

(一)、通讯口设置

墨之殇

  • 精华:17帖
  • 求助:8帖
  • 帖子:451帖 | 4077回
  • 年度积分:15
  • 历史总积分:26901
  • 注册:2011年8月26日
发表于:2011-12-11 20:57:24
3楼

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;

墨之殇

  • 精华:17帖
  • 求助:8帖
  • 帖子:451帖 | 4077回
  • 年度积分:15
  • 历史总积分:26901
  • 注册:2011年8月26日
发表于:2011-12-11 20:59:33
4楼

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码错误判断、通讯故障处理等。

Modbus通讯协议


Modbus
协议最初由Modicon公司开发出来,在1979年末该公司成为施耐德自动化(SchneiderAutomation)部门的一部分,现在Modbus已经是工业领域全球最流行的协议。此协议支持传统的RS-232RS-422RS-485

墨之殇

  • 精华:17帖
  • 求助:8帖
  • 帖子:451帖 | 4077回
  • 年度积分:15
  • 历史总积分:26901
  • 注册:2011年8月26日
发表于:2011-12-11 21:01:23
5楼
和以太网设备。许多工业设备,包括PLCDCS,智能仪表等都在使用Modbus协议作为他们之间的通讯标准。有了它,不同厂商生产的控制设备可以连成工业网络,进行集中监控。 当在网络上通信时,Modbus协议决定了每个控制器须要知道它们的设备地址,识别按地址发来的消息,决定要产生何种行动。如果需要回应,控制器将生成应答并使用Modbus协议发送给询问方。 Modbus协议包括ASCIIRTUTCP等,并没有规定物理层。此协议定义了控制器能够认识和使用的消息结构,而不管它们是经过何种网络进行通信的。标准的Modicon控制器使用RS232C实现串行的ModbusModbusASCIIRTU协议规定了消息、数据的结构、命令和就答的方式,数据通讯采用Maser/Slave方式,Master端发出数据请求消息,Slave端接收到正确消息后就可以发送数据到Master端以响应请求;Master端也可以直接发消息修改Slave端的数据,实现双向读写。 Modbus协议需要对数据进行校验,串行协议中除有奇偶校验外,ASCII模式采用LRC校验,RTU模式采用16CRC校验,但TCP模式没有额外规定校验,因为TCP协议是一个面向连接的可靠协议。另外,Modbus采用主从方式定时收发数据,在实际使用中如果某Slave站点断开后(如故障或关机),Master端可以诊断出来,而当故障修复后,网络又可自动接通。因此,Modbus协议的可靠性较好。 下面简单的介绍一下,对于ModbusASCIIRTUTCP协议来说,其中TCPRTU协议非常类似,我们只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上50和一个6并通过TCP/IP网络协议发送出去即可。所以在这里我仅介绍一下ModbusASCIIRTU协议。 下表是ASCII协议和RTU协议进行的比较: 协议开始标记结束标记校验传输效率程序处理 ASCII:(冒号)CR,LFLRC低直观,简单,易调试 RTU无无CRC高不直观,稍复杂 通过比较可以看到,ASCII协议和RTU协议相比拥有开始和结束标记,因此在进行程序处理时能更加方便,而且由于传输的都是可见的ASCII字符,所以进行调试时就更加的直观,另外它的LRC校验也比较容易。但是因为它传输的都是可见的ASCII字符,RTU传输的数据每一个字节ASCII都要用两个字节来传输,比如RTU传输一个十六进制数0xF9,ASCII就需要传输’F’’9’ASCII0x390x46两个字节,这样它的传输的效率就比较低。所以一般来说,如果所需要传输的数据量较小可以考虑使用ASCII协议,如果所需传输的数据量比较大,最好能使用RTU协议。 下面对两种协议的校验进行一下介绍。 1LRC校验 LRC域是一个包含一个8位二进制值的字节。LRC值由传输设备来计算并放到消息帧中,接收设备在接收消息的过程中计算LRC,并将它和接收到消息中LRC域中的值比较,如果两值不等,说明有错误。 LRC校验比较简单,它在ASCII协议中使用,检测了消息域中除开始的冒号及结束的回车换行号外的内容。它仅仅是把每一个需要传输的数据按字节叠加后取反加1即可。下面是它的VC代码: BYTEGetCheckCode(constchar*pSendBuf,intnEnd)//获得校验码 { BYTEbyLrc=0; charpBuf【4】; intnData=0; for(i=1;i&lt;end;i+=2)//i初始为1,避开开始标记冒号 { //每两个需要发送的ASCII码转化为一个十六进制数 pBuf【0】=pSendBuf; pBuf【1】=pSendBuf【i+1】; pBuf【2】=&#39;\0&#39;; sscanf(pBuf,&quot;%x&quot;,&nData); byLrc+=nData; } byLrc=~byLrc; byLrc++; returnbyLrc; } 2CRC校验 CRC域是两个字节,包含一16位的二进制值。它由传输设备计算后加入到消息中。接收设备重新计算收到消息的CRC,并与接收到的CRC域中的值比较,如果两值不同,则有误。 CRC是先调入一值是全1”16位寄存器,然后调用一过程将消息中连续的8位字节各当前寄存器中的值进行处理。仅每个字符中的8Bit数据对CRC有效,起始位和停止位以及奇偶校验位均无效。 CRC产生过程中,每个8位字符都单独和寄存器内容相或(OR),结果向最低有效位方向移动,最高有效位以0填充。LSB被提取出来检测,如果LSB1,寄存器单独和预置的值或一下,如果LSB0,则不进行。整个过程要重复8次。在最后一位(第8位)完成后,下一个8位字节又单独和寄存器的当前值相或。最终寄存器中的值,是消息中所有的字节都执行之后的CRC值。 CRC添加到消息中时,低字节先加入,然后高字节。下面是它的VC

墨之殇

  • 精华:17帖
  • 求助:8帖
  • 帖子:451帖 | 4077回
  • 年度积分:15
  • 历史总积分:26901
  • 注册:2011年8月26日
发表于:2011-12-11 21:03:24
6楼
码: WORDGetCheckCode(constchar*pSendBuf,intnEnd)//获得校验码 { WORDwCrc=WORD(0xFFFF); for(inti=0;i&lt;nEnd;i++) { wCrc^=WORD(BYTE(pSendBuf)); for(intj=0;j&lt;8;j++) { if(wCrc&1) { wCrc&gt;&gt;=1; wCrc^=0xA001; } else { wCrc&gt;&gt;=1; } } } returnwCrc; } 对于一条RTU协议的命令可以简单的通过以下的步骤转化为ASCII协议的命令: 1、把命令的CRC校验去掉,并且计算出LRC校验取代。 2、把生成的命令串的每一个字节转化成对应的两个字节的ASCII码,比如0x03转化成0x30,0x330ASCII码和3ASCII码)。 3、在命令的开头加上起始标记“:”,它的ASCII码为0x3A 4、在命令的尾部加上结束标记CR,LF0xD,0xA),此处的CR,LF表示回车和换行的ASCII码。 所以以下我们仅介绍RTU协议即可,对应的ASCII协议可以使用以上的步骤来生成。 下表是Modbus支持的功能码: 功能码名称作用 01读取线圈状态取得一组逻辑线圈的当前状态(ON/OFF) 02读取输入状态取得一组开关输入的当前状态(ON/OFF) 03读取保持寄存器在一个或多个保持寄存器中取得当前的二进制值 04读取输入寄存器在一个或多个输入寄存器中取得当前的二进制值 05恐玫ハ呷?nbsp;强置一个逻辑线圈的通断状态 06预置单寄存器把具体二进值装入一个保持寄存器 07读取异常状态取得8个内部线圈的通断状态,这8个线圈的地址由控制器决定 08回送诊断校验把诊断校验报文送从机,以对通信处理进行评鉴 09编程(只用于484)使主机模拟编程器作用,修改PC从机逻辑 10控询(只用于484)可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码9的报文发送后,本功能码才发送 11读取事件计数可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其他应答产生通信错误时 12读取通信事件记录可是主机检索每台从机的ModBus事务处理通信事件记录。如果某项事务处理完成,记录会给出有关错误 13编程(184/384484584)可使主机模拟编程器功能修改PC从机逻辑 14探询(184/384484584)可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13的报文发送后,本功能码才得发送 15强置多线圈强置一串连续逻辑线圈的通断 16预置多寄存器把具体的二进制值装入一串连续的保持寄存器 17报告从机标识可使主机判断编址从机的类型及该从机运行指示灯的状态 18884MICRO84)可使主机模拟编程功能,修改PC状态逻辑 19重置通信链路发生非可修改错误后,是从机复位于已知状态,可重置顺序字节 20读取通用参数(584L)显示扩展存储器文件中的数据信息 21写入通用参数(584L)把通用参数写入扩展存储文件,或修改之 2264保留作扩展功能备用 6572保留以备用户功能所用留作用户功能的扩展编码 73119非法功能 120127保留留作内部作用 128255保留用于异常应答 在这些功能码中较长使用的是123456号功能码,使用它们即可实现对下位机的数字量和模拟量的读写操作。 1、读可读写数字量寄存器(线圈状态): 计算机发送命令:设备地址】【命令号01】【起始寄存器地址高8】【8】【读取的寄存器数高8】【8】【CRC校验的低8】【CRC校验的高8例:【11】【01】【00】【13】【00】【25】【CRC】【CRC意义如下: &lt;1&gt;设备地址:在一个485总线上可以挂接多个设备,此处的设备地址表示想和哪一个设备通讯。例子中为想和17(十进制的17是十六进制的11)通讯。 &lt;2&gt;命令号01:读取数字量的命令号固定为01 &lt;3&gt;起始地址高8位、低8位:表示想读取的开关量的起始地址(起始地址为0)。比如例子中的起始地址为19 &lt;4&gt;寄存器数高8位、低8位:表示从起始地址开始读多少个开关量。例子中为37个开关量。 &lt;5&gt;CRC校验:是从开头一直校验到此之前。在此协议的最后再作介绍。此处需要注意,CRC校验在命令中的高低字节的顺序和其他的相反。 设备响应:设备地址】【命令号01】【返回的字节个数】【数据1】【数据2】...【数据n】【CRC校验的低8】【CRC校验的高8例:【11】【01】【05】【CD】【6B】【B2】【0E】【1B】【CRC】【CRC意义如下: &lt;1&gt;设备地址和命令号和上面的相同。 &lt;2&gt;返回的字节个数:表示数据的字节个数,也就是数据12...n中的n的值。 &lt;3&gt;

墨之殇

  • 精华:17帖
  • 求助:8帖
  • 帖子:451帖 | 4077回
  • 年度积分:15
  • 历史总积分:26901
  • 注册:2011年8月26日
发表于:2011-12-11 21:04:52
7楼

数据1...n:由于每一个数据是一个8位的数,所以每一个数据表示8个开关量的值,每一位为0表示对应的开关断开,为1表示闭合。比如例子中,表示20(索引号为19)开关闭合,21号断开,22闭合,23闭合,24断开,25断开,26闭合,27闭合...如果询问的开关量不是8的整倍数,那么最后一个字节的高位部分无意义,置为0 &lt;4&gt;CRC校验同上。 2、读只可读数字量寄存器(输入状态): 和读取线圈状态类似,只是第二个字节的命令号不再是1而是23、写数字量(线圈状态):计算机发送命令:设备地址】【命令号05】【需下置的寄存器地址高8】【8】【下置的数据高8】【8】【CRC校验的低8】【CRC校验的高8例:【11】【05】【00】【AC】【FF】【00】【CRC】【CRC意义如下: &lt;1&gt;设备地址和上面的相同。 &lt;2&gt;命令号:写数字量的命令号固定为05 &lt;3&gt;需下置的寄存器地址高8位,低8位:表明了需要下置的开关的地址。 &lt;4&gt;下置的数据高8位,低8位:表明需要下置的开关量的状态。例子中为把该开关闭合。注意,此处只可以是【FF】【00】表示闭合【00】【00】表示断开,其他数值非法。 &lt;5&gt;注意此命令一条只能下置一个开关量的状态。设备响应:如果成功把计算机发送的命令原样返回,否则不响应。 4、读可读写模拟量寄存器(保持寄存器): 计算机发送命令:设备地址】【命令号03】【起始寄存器地址高8】【8】【读取的寄存器数高8】【8】【CRC校验的低8】【CRC校验的高8例:【11】【03】【00】【6B】【00】【03】【CRC】【CRC意义如下: &lt;1&gt;设备地址和上面的相同。 &lt;2&gt;命令号:读模拟量的命令号固定为03 &lt;3&gt;起始地址高8位、低8位:表示想读取的模拟量的起始地址(起始地址为0)。比如例子中的起始地址为107 &lt;4&gt;寄存器数高8位、低8位:表示从起始地址开始读多少个模拟量。例子中为3个模拟量。注意,在返回的信息中一个模拟量需要返回两个字节。设备响应:设备地址】【命令号03】【返回的字节个数】【数据1】【数据2】...【数据n】【CRC校验的低8】【CRC校验的高8例:【11】【03】【06】【02】【2B】【00】【00】【00】【64】【CRC】【CRC意义如下: &lt;1&gt;设备地址和命令号和上面的相同。 &lt;2&gt;返回的字节个数:表示数据的字节个数,也就是数据12...n中的n的值。例子中返回了3个模拟量的数据,因为一个模拟量需要2个字节所以共6个字节。 &lt;3&gt;数据1...n:其中数据1】【数据2】分别是第1个模拟量的高8位和低8位,数据3】【数据4】是第2个模拟量的高8位和低8位,以此类推。例子中返回的值分别是5550100 &lt;4&gt;CRC校验同上。 5、读只可读模拟量寄存器(输入寄存器):和读取保存寄存器类似,只是第二个字节的命令号不再是2而是4 6、写单个模拟量寄存器(保持寄存器): 计算机发送命令:设备地址】【命令号06】【需下置的寄存器地址高8】【8】【下置的数据高8】【8】【CRC校验的低8】【CRC校验的高8例:【11】【06】【00】【01】【00】【03】【CRC】【CRC意义如下: &lt;1&gt;设备地址和上面的相同。 &lt;2&gt;命令号:写模拟量的命令号固定为06 &lt;3&gt;需下置的寄存器地址高8位,低8位:表明了需要下置的模拟量寄存器的地址。 &lt;4&gt;下置的数据高8位,低8位:表明需要下置的模拟量数据。比如例子中就把1号寄存器的值设为3 &lt;5&gt;注意此命令一条只能下置一个模拟量的状态。设备响应:如果成功把计算机发送的命令原样返回,否则不响应。

zgsdzylj

  • 精华:0帖
  • 求助:0帖
  • 帖子:2帖 | 41回
  • 年度积分:0
  • 历史总积分:145
  • 注册:2012年3月15日
发表于:2015-03-14 19:20:06
8楼

这个太深奥了,小菜也看不懂啊

Daisyy

  • 精华:0帖
  • 求助:0帖
  • 帖子:1帖 | 127回
  • 年度积分:0
  • 历史总积分:25
  • 注册:2015年11月18日
发表于:2016-01-26 11:36:35
9楼

学习下。。。。。。。。。。。。。。


热门招聘
相关主题

官方公众号

智造工程师