MODBUS通讯实例.docx

上传人:b****8 文档编号:10506873 上传时间:2023-02-17 格式:DOCX 页数:25 大小:75.52KB
下载 相关 举报
MODBUS通讯实例.docx_第1页
第1页 / 共25页
MODBUS通讯实例.docx_第2页
第2页 / 共25页
MODBUS通讯实例.docx_第3页
第3页 / 共25页
MODBUS通讯实例.docx_第4页
第4页 / 共25页
MODBUS通讯实例.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

MODBUS通讯实例.docx

《MODBUS通讯实例.docx》由会员分享,可在线阅读,更多相关《MODBUS通讯实例.docx(25页珍藏版)》请在冰豆网上搜索。

MODBUS通讯实例.docx

MODBUS通讯实例

RTU协议和ASCII协议,我公司的多种仪表都采用ModBusRTU通智能电力监测仪、CH2000M电力参数采集模块、巡检表、数显表、ModBusRTU协议简要介绍如下:

MODBUS通讯协议及编程#1Modbus通讯协议分为讯协议,如:

CH2000光柱数显表等。

下面就一、通讯协议

(一)、通讯传送方式:

通讯传送分为独立的信息头,和发送的编码数据。

以下的通讯传送方式定义也与

ModbusRTU通讯规约相兼容:

编码8位二进制

起始位1位

数据位8位

奇偶校验位1位(偶校验位)

CRC(冗余循环码)

停止位1

错误校检

=字节的时间

初始结构

地址码=1字节功能码=1字节

数据区=N字节错误校检=16位CRC码结束结构=字节的时间

地址码:

地址码为通讯传送的第一个字节。

这个字节表明由用户设定地址码的从机将接收由主机发送来的信息。

并且每个从机都有具有唯一的地址码,并且响应回送均以各自的地址码开始。

主机发送的地址码表明将发送到的从机地址,而从机发送的地址码表明回送的从机地址。

功能码:

通讯传送的第二个字节。

Modbus通讯规约定义功能号为1到127。

本仪表只利用其中的一部分功能码。

作为主机请求发送,通过功能码告诉从机执行什么动作。

作为从机响应,从机发送的功能码与从主机发送来的功能码一样,并表明从机已响应主机进行操作。

如果从机发送的功能码的最高位为1(比如功能码大与此同时127),则表明从机没有响应操

作或发送出错。

CRC码:

二字节的错误检测码。

二)、通讯规约:

当通讯命令发送至仪器时,符合相应地址码的设备接通讯命令,并除去地址码,读取

信息,如果没有出错,则执行相应的任务;然后把执行结果返送给发送者。

返送的信息中包括地址码、执行动作的功能码、执行动作后结果的数据以及错误校验码。

如果出错就不发送任何信息。

1.信息帧结构地址码功能码数据区错误校验码

8位8位NX8位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(1010000000000001)进行异或;重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;重复步骤2到步骤5,进行下一个8位数据的处理;最后得到的CRC寄存器即为CRC码。

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

仪表采用ModbusRTU通讯规约,利用通讯命令,可以进行读取点(“保持寄存器”)或返回值(“输入寄存器”的)操作。

保持和输入寄存器都是16位(2字节)值,并且高位在前。

这样用于仪表的读取点和返回值都是2字节。

一次最多可读取寄存器数是60。

由于一些可编程控制器不用功能码03,所以功能码03被用作读取点和返回值。

从机响应的命令格式是从机地址、功能码、数据区及CRC码。

数据区中的寄存器数据都是每两个字节高字节在前。

4.功能码06,单点保存

从机也用这个功能码向主机返送信

主机利用这条命令把单点数据保存到仪表的存储器。

息。

二、编程举例

面是一个用VC编写的ModbusRTU通讯的例子

一)、通讯口设置

DCBdcb;hCom=CreateFile("COM1",

GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,

0,NULL);

if(hCom==INVALID_HANDLE_VALUE){

MessageBox("createfileerror,error");

}

BOOLerror=SetupComm(hCom,1024,1024);if(!

error)

MessageBox("setupcommerror");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校验码计算

UINTcrc

voidcalccrc(BYTEcrcbuf){

BYTEi;

crc=crc人crcbuf;

for(i=0;i<8;i++)

{

BYTETT;

TT=crc&1;

crc=crc>>1;crc=crc&0x7fff;

if(TT==1)

crc=crcAOxaOO1;crc=crc&0xffff;

}

}

三)、数据发送zxaddr=11;//读取地址为11的巡检表数据

zxnum=10;//读取十个通道的数据

 

writebuf2

writebuf2

writebuf2

=zxaddr;

=3;

=0;

=0;

writebuf2【4】=0;writebuf2【5】=zxnum;

crc=0xffff;

calccrc(writebuf2

calccrc(writebuf2

calccrc(writebuf2

calccrc(writebuf2

calccrc(writebuf2

calccrc(writebuf2writebuf2【6】=crc&0xff;writebuf2【7】=crc/0x100;

WriteFile(hCom,writebuf2,8,&comnum,NULL);

4)、数据读取

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

窗口加载

PrivateSubForm_Load()Dimd%

Ford=1To16

Combo1.AddItem("COM"&CStr(d))Next

Combo1.ListIndex=0

Combo2.AddItem"6"

Combo2.AddItem"7"

Combo2.AddItem"8"

Combo2.ListIndex=2

Combo3.AddItem"110"

Combo3.AddItem"330"

Combo3.AddItem"1200"

Combo3.AddItem"2400"

Combo3.AddItem"4800"

Combo3.AddItem"9600"

Combo3.AddItem"19200"

Combo3.AddItem"38400"

Combo3.AddItem"56000"

Combo3.AddItem"57600"

Combo3.AddItem"115200"

Combo3.ListIndex=5

Combo4.AddItem"n"

Combo4.AddItem"o"

Combo4.AddItem"e"

Combo4.ListIndex=0

Combo5.AddItem"1"

Combo5.AddItem"2"

Combo5.ListIndex=0

Ford=0To254

Combo6.AddItemd

Next

Combo6.ListIndex=1

 

Option1.value=True

Option3.value=True

Option7.value=True

Option9.value=True

IfMSComm1.PortOpen=FalseThen

Command1.Caption="打开串口"

Else

Command1.Caption="关闭串口"

EndIf

EndSub'串口接收程序

PrivateSubMSComm1_OnComm()

DimHexchrAsString,hexstringAsString,iAsInteger,jAsInteger,hexdispAsString

IfOption8.valueThen

六进制

hexstring=MSComm1.Input显示

i=Len(hexstring)

Forj=1Toi

Hexchr=Mid(hexstring,j,1)

IfHex(Asc(Hexchr))<16Then

Text2.Text=Text2.Text&"0"&Hex(Asc(Hexchr))&""Else

Text2.Text=Text2.Text&Hex(Asc(Hexchr))&""

EndIf

Nextj

Text2.Text=Text2.Text&CStr(Chr(13))&CStr(Chr(10))

Else

Text2.Text=Text2.Text&MSComm1.Input&CStr(Chr(13))&CStr(Chr(10))'ASCII码显示

EndIf

EndSub'手动发送选择

PrivateSubOption1_Click()

IfOption1.value=TrueThen

Timer1.Enabled=False

Command4.Enabled=True

Else

Timer1.Enabled=True

Command4.Enabled=False

EndIf

EndSub'DeltaASCII发送协议

PrivateSubOption10_Click()

Combo6.Enabled=True

Text6.Enabled=True

Text7.Enabled=True

Text8.Enabled=True

Label10.Enabled=True

Label11.Enabled=True

Label12.Enabled=True

Label13.Enabled=True

Option6.Enabled=False

Option7.Enabled=False

Option11.value=True

Combo2.ListIndex=1

Combo5.ListIndex=1

Text1.Enabled=False

Label14.Enabled=False

Frame7.Visible=True

EndSub'自动发送选择

PrivateSubOption2_Click()

IfOption2.value=TrueThen

Timer1.Enabled=True

Command4.Enabled=False

Else

Timer1.Enabled=False

Command4.Enabled=True

EndIf

EndSub

PrivateSubOption3_Click()

'Non

选项

Combo6.Enabled=False

Text6.Enabled=False

Text7.Enabled=False

Text8.Enabled=False

Label10.Enabled=False

Label11.Enabled=False

Label12.Enabled=False

Label13.Enabled=False

Option6.Enabled=True

Option7.Enabled=True

Combo2.ListIndex=2

Combo5.ListIndex=0

Text1.Enabled=True

Label14.Enabled=True

Frame7.Visible=False

EndSub

PrivateSubOption4_Click()

Combo6.Enabled=True

Text6.Enabled=True

Text7.Enabled=True

Text8.Enabled=True

Label10.Enabled=True

Label11.Enabled=True

Label12.Enabled=True

Label13.Enabled=True

Option6.Enabled=False

Option7.Enabled=False

Combo2.ListIndex=1

Combo5.ListIndex=1

Text1.Enabled=False

Label14.Enabled=False

Frame7.Visible=False

EndSub

PrivateSubOption5_Click()

'RTU

选项

Combo6.Enabled=True

Text6.Enabled=True

Text7.Enabled=True

Text8.Enabled=True

Label10.Enabled=True

Label11.Enabled=True

Label12.Enabled=True

Label13.Enabled=True

Option6.Enabled=FalseOption7.Enabled=FalseCombo2.ListIndex=2Combo5.ListIndex=1

Text1.Enabled=False

Label14.Enabled=False

Frame7.Visible=False

EndSub

'发送时间间隔调整输入

PrivateSubText5_Change()

DimnumberAsString

DimnumAsInteger

DimnumcycAsInteger

num=Len(Text5.Text)

Fornumcyc=1Tonumnumber=Mid(Text5.Text,numcyc,1)SelectCaseInStr("0123456789",number)Case0

MsgBox"输入时间间隔错误,请重新输入ExitSub

",,"错误信息"

EndSelect

Next

Timer1.Interval=Text5.Text

EndSub

'自动发送定时器

PrivateSubTimer1_Timer()

IfMSComm1.PortOpenThen

Callsentsub

EndIf

EndSub

'状态刷新定时器

PrivateSubTimer2_Timer()

StatusBar1.Panels

(1).Text="

StatusBar1.Panels

(2).Text="

StatusBar1.Panels(3).Text="EndSub

'串口发送子程序

串口选择:

串口设置:

串口状态:

&CStr(Combo1.Text)

&CStr(MSComm1.Settings)

&CStr(MSComm1.PortOpen)

 

PrivateSubsentsub()

Dimoptioncase%

IfOption3.valueThenoptioncase=1

IfOption4.valueThenoptioncase=2

IfOption5.valueThenoptioncase=3

IfOption10.valueThenoptioncase=4SelectCaseoptioncase

Case1

IfOption6.valueThenText1text=Text1.TextCallHexsent

Else

Text1text=Text1.Text

CallASCIIsent

EndIf

Case2

Callincorporate

容合并成字符串

将输入的十进制从机地址、命令、资料地址和资料内

CallASCIIcheck

CallASCIIsent

Case3

Callincorporate

容合并成字符串

将输入的十进制从机地址、命令、资料地址和资料内

CallRTUcheck

CallHexsent

Case4

Callincorporate1

内容合并成字符串

将输入的十进制从机地址、命令、资料地址和资料

CalldeltaASCII

CallASCIIsentEndSelect

EndSub'十六进制发送

PrivateSubHexsent()

Dimhexchrlen%,HexchrAsString,hexcyc%,hexmidAsByte,hexmiddleAsStri

ng

Dimhexchrgroup()AsByte,iAsIntegerhexchrlen=Len(Text1text)Forhexcyc=1Tohexchrlen内数值是否合适

检查Text1文本框

Hexchr=Mid(Text1text,hexcyc,1)

IfInStr("0123456789ABCDEFabcdef",Hexchr)=0ThenMsgBox"无效的数值,请重新输入",,"错误信息"ExitSub

 

EndIf

Next

ReDimhexchrgroup(1Tohexchrlen\2)AsByte

将文本框内数值

Forhexcyc=1TohexchrlenStep2分成两个、两个

i=i+1

Hexchr=Mid(Text1text,hexcyc,2)hexmid=Val("&H"&CStr(Hexchr))hexchrgroup(i)=hexmid'MSComm1.Output=CStr(hexmid)Next

MSComm1.Output=hexchrgroup

EndSub'ASC码发送

PrivateSubASCIIsent()

MSComm1.Output=Text1text

EndSub

'ASC校验,此段程序计算出LRC校验值,并加上字头和字尾

PrivateSubASCIIcheck()

Dima%,b%,chrnum%,LrcbyteAsString

Dimchecksum%,char%,AscLrc%,Lrc%

Lrc=(&HFF-AscLrc)+1

IfLrc<16Then数,

Lrcbyte="0"+CStr(Hex(Lrc))

Else

取255的余数

取二次补此段程序是判断Hex(lrc)是否是一位如果是的话,前面加0;否则不加零

Lrcbyte=CStr(Hex(Lrc))

EndIf

Text1text=CStr(Chr(58))&CStr(Text1text)&Lrcbyte&CStr(Chr(13))&CStr(Chr(10))

EndSub

'DeltaASCII校验,此段程序计算出LRC校验值,并加上字头和字尾

PrivateSubdeltaASCII()

Dima%,b%,chrnum%,LrcbyteAsString

Dimchecksum%,char%,Lrc%

chrnum=Len(Text1text)

Fora=1Tochrnum

两个两个的取字符

'全部加起来

char=Asc(Mid(Text1text,a,1))'checksum=checksum+char

Next

Lrc=(checksum+&H3)Mod&H100

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 笔试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1