MODBUS的CRC校验和程序

更新时间:2023-12-06 15:00:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

MODBUS的CRC校验和程序(VB编制)

modbus协议做为一种通用协议得到了广泛的应用,它有两种传输模式:ASCII和RTU。ASCII模式采用LRC校验,RTU模式采用CRC校验。

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域附加在消息的最后,添加时先是低字节然后是高字节。故CRC的高位字节是发送消息的最后一个字节。

下面是用VB实现的CRC校验和程序:

Function CRC16(data() As Byte) As String ’CRC计算函数 Dim CRC16Lo As Byte, CRC16Hi As Byte ’CRC寄存器 Dim CL As Byte, CH As Byte ’多项式码&HA001 Dim SaveHi As Byte, SaveLo As Byte Dim I As Integer Dim Flag As Integer CRC16Lo = &HFF CRC16Hi = &HFF CL = &H1 CH = &HA0

For I = 0 To UBound(data)

CRC16Lo = CRC16Lo Xor data(I) ’每一个数据与CRC寄存器进行异或 For Flag = 0 To 7 SaveHi = CRC16Hi SaveLo = CRC16Lo

CRC16Hi = CRC16Hi \\ 2 ’高位右移一位 CRC16Lo = CRC16Lo \\ 2 ’低位右移一位

If ((SaveHi And &H1) = &H1) Then ’如果高位字节最后一位为1

CRC16Lo = CRC16Lo Or &H80 ’则低位字节右移后前面补1 End If ’否则自动补0

If ((SaveLo And &H1) = &H1) Then ’如果LSB为1,则与多项式码进行异或 CRC16Hi = CRC16Hi Xor CH CRC16Lo = CRC16Lo Xor CL

End If Next Flag Next I

Dim ReturnData(1) As Byte

ReturnData(0) = CRC16Hi ’CRC高位 ReturnData(1) = CRC16Lo ’CRC低位

asd = Right(\

End Function

多线程串行通讯VB源码

Option Explicit

Private WithEvents oTest1 As TestExe.clsTest Private WithEvents oTest2 As TestExe.clsTest

Private Declare Function GetTickCount Lib \ Dim mi, i As Integer

Public Sub timedelay(ByVal t As Long) Dim tt As Double tt = GetTickCount() Do

DoEvents

Loop Until GetTickCount() - tt > t End Sub

Private Sub Command1_Click()

Set oTest1 = New TestExe.clsTest oTest1.lMillisecs = 100 oTest1.StartSub (1000)

Set oTest2 = New TestExe.clsTest oTest2.lMillisecs = 100 oTest2.StartSub (1000)

Command1.Enabled = False Command2.Enabled = True End Sub

Private Sub Command2_Click() oTest1.StopSub oTest2.StopSub

Command1.Enabled = True Command2.Enabled = False End Sub

Private Sub Command3_Click() oTest1.StopSub oTest2.StopSub

Set oTest1 = Nothing Set oTest2 = Nothing End Sub

Private Sub Form_Unload(Cancel As Integer) oTest1.StopSub oTest2.StopSub

Set oTest1 = Nothing Set oTest2 = Nothing End Sub

Private Sub oTest1_Progress(ByVal lProgress As Long) List1.AddItem lProgress

List1.ListIndex = List1.ListCount - 1 End Sub

Private Sub oTest2_Progress(ByVal lProgress As Long) recieve_message

List2.AddItem lProgress

List2.ListIndex = List2.ListCount - 1 End Sub

Private Sub send_message()

If MSComm1.PortOpen = False Then '置位 MSComm1.CommPort = 1 MSComm1.PortOpen = True End If

MSComm1.Settings = \ MSComm1.InputLen = 0

MSComm1.InputMode = comInputModeText MSComm1.Output = Text1.Text Do

timedelay 100 DoEvents

Loop While MSComm1.InBufferCount <= 2 Label1.Caption = MSComm1.Input If Label1.Caption = \

MsgBox \通讯错误,请确认线路是否连接\错误\ Else

'Label1.Caption = \开始运行\ End If

MSComm1.InputLen = 0 MSComm1.PortOpen = False End Sub

Private Sub recieve_message() '接收应答信息,初始化MSComm Dim str As String

If MSComm1.PortOpen = False Then '置位 MSComm1.CommPort = 1

MSComm1.PortOpen = True End If

MSComm1.Settings = \ MSComm1.InputLen = 0

MSComm1.InputMode = comInputModeText 'MSComm1.InputMode = comInputModeBinary Do

timedelay 100 DoEvents

Loop While MSComm1.InBufferCount <= 2 str = MSComm1.Input Label1.Caption = str Debug.Print str End Sub

Private Sub sent_msg_Click() send_message End Sub

Private Sub show_msg_Click() recieve_message End Sub

如何用VB实现Modbus串行通讯

在一些应用中可能需要使用诸如VB来进行上位机监控程序的开发,而Modbus协议是这类应用中首选的通讯协议;Modbus协议以其简单易用,在工业领域里已广泛的为其他第三方设备所支持。这里对VB和Twido PLC间的通讯进行说明。

对于大部分应用,Twido PLC作为从站,它不需要编制通讯程序,只要把通讯口的参数设置好即可,例如下图表示此Twido通过编程口和上位机连接,其站号地址为2;波特率、数据位、校验、停止位和上位机设置保持一致。

VB程序通过利用MSComm控件很容易就能够实现。 1. 通讯口初始化:

MSComm1.Settings = \ MSComm1.CommPort = 1 MSComm1.SThreshold = 0

If Not MSComm1.PortOpen Then MSComm1.PortOpen = True

2. CRC校验码的计算方法,如以下函数,可以得到字节数组变量cmdstring指向的字符串的CRC校验码。 字串7

Function crc16_1(ByRef cmdstring() As Byte, ByVal j As Integer) Dim data As Integer Dim i As Integer

Addressreg_crc = &HFFFF For i = 0 To j

Addressreg_crc = Addressreg_crc Xor cmdstring(i) For j = 0 To 7

data = Addressreg_crc And &H1 If data Then

Addressreg_crc = Int(Addressreg_crc / 2) Addressreg_crc = Addressreg_crc And &H7FFF Addressreg_crc = Addressreg_crc Xor &HA001 Else

Addressreg_crc = Addressreg_crc / 2

Addressreg_crc = Addressreg_crc And &H7FFF End If Next j Next i

If Addressreg_crc < 0 Then

Addressreg_crc = Addressreg_crc - &HFFFF0000 End If

HiByte = Addressreg_crc And &HFF

LoByte = (Addressreg_crc And &HFF00) / &H100 End Function

3. 读多个字的命令(本例是从2号站读%MW10起始的4个字): 字串2 Dim SendStr(7) As Byte Dim RcvStr() As Byte SendStr(0) = 2 ,从站号是2

SendStr(1) = &H3 ,读多个字的命令代码 SendStr(2) = 0 ,起始地址高字节 SendStr(3) = 10,起始地址低字节 SendStr(4) = &H0,数据长度高字节 SendStr(5) = 4 ,数据长度低字节

Call crc16(SendStr(), 5) ,CRC计算 SendStr(6) = HiByte SendStr(7) = LoByte

,读命令发送后,当接收 5 + SendStr(5) * 2 个字节时产生中断 CmdLenth = 5 + SendStr(5) * 2 MSComm1.RThreshold = CmdLenth MSComm1.Output = SendStr ,发送命令

起始位地址 0000 DO寄存器数据(16进制) 02 备 注 DO2输出状态为“1”,DO1输出状态为“0” 主机发送的报文格式: 主机发送 从机地址 功能码 起始BIT位 读数据长度 CRC码 从机(PDM)响应返回的报文格式: 从机响应 从机地址 功能码 数据长度 DO状态数据 CRC码 字节数 1 1 1 1 2 返回的信息 01 01 01 02 D049 备 注 来自从机01 读开关量输出状态 1个字节(8个BIT位) DO寄存器内容 由从机计算得到CRC码 字节数 1 1 2 2 2 发送的信息 01 01 0000 0002 BDCB 备 注 发送至地址为01的从机 读开关量输出状态 起始BIT位地址为0000 读取2路继电器输出状态位 由主机计算得到CRC码 2.3 功能码“03”:读多路寄存器输入 例如:主机要读取地址为01,起始地址为0116的3个从机寄存器数据。 从机(PDM)数据寄存器的地址和数据为: 寄存器地址 0116 0117 0118 主机发送的报文格式: 寄存器数据(16进制) 1784 1780 178A 对应PDM电量 UA UB UC 主机发送 从机地址 功能码 起始地址 数据长度 CRC码 从机(PDM)响应返回的报文格式: 从机响应 从机地址 功能码 读取字 寄存器数据1 寄存器数据2 寄存器数据3 CRC码 2.4 功能码“05”:写1路开关量输出(“遥控”) 例1:开关量输出点DO1,其当前状态为“分”,主机要控制该路继电器“合”。 控制命令为: “FF00”为控制继电器“合”; “0000”为控制继电器“分”; 主机发送的报文格式: 主机发送 字节数 发送的信息 备 注 字节数 1 1 1 2 2 2 2 返回的信息 01 03 06 1784 1780 178A 5847 备 注 来自从机01 读取寄存器 3个寄存器共6个字节 地址为0116内存的内容 地址为0117内存的内容 地址为0118内存的内容 由从机计算得到CRC码 字节数 1 1 2 3 2 发送的信息 01 03 0116 0003 E5F3 备 注 发送至地址为01的从机 读取寄存器 起始地址为0116 读取3个寄存器(共6个字节) 由主机计算得到CRC码 从机地址 功能码 输出BIT位 控制命令 CRC码 1 1 2 2 2 01 05 0000 FF00 8C3A 发送至地址为01的从机 写开关量输出状态 对应输出继电器BIT位(DO1) 控制该路继电器输出为“合”状态位 由主机计算得到CRC码 从机(PDM)响应返回的报文格式: 与主机发送的报文格式及数据内容完全相同。 例2:开关量输出点DO2,其当前状态为“合”,主机要控制该路继电器“分”。 主机发送的报文格式: 主机发送 从机地址 功能码 输出BIT位 控制命令 CRC码 从机(PDM)响应返回的报文格式: 与主机发送的报文格式及数据内容完全相同。 2.5 功能码“06”:写单路寄存器 例如:主机要把数据07D0,保存到地址为002C的从机寄存器中去(从机地址码为01)。通讯数据保存结束后,地址为002C的PDM表原存储信息为: 地址 002C 原来存储数据(16进制) 04B0 字节数 1 1 2 2 2 发送的信息 01 05 0001 0000 9C0A 备 注 发送至地址为01的从机 写开关量输出状态 对应输出继电器BIT位(DO2) 控制该路继电器输出为“合”状态位 由主机计算得到CRC码 主机发送的报文格式: 主机发送 从机地址 功能码 起始地址 写入数据 CRC码 从机(PDM)响应返回的报文格式: 与主机发送的报文格式及数据内容完全相同。 2.6功能码“10”:写多路寄存器 主机利用这个功能码把多个数据保存到PDM表的数据存储器中去。Modbus通讯规约中的寄存器指的是16位(即2字节),并且高位在前。这样PDM的存储器都是二个字节。由于Modbus通讯规约允许每次最多保存60个寄存器,因此PDM一次也最多允许保存60个数据寄存器。 例如:主机要把0064,0010保存到地址为002C,002D的从机寄存器中去(从机地址码为01)。通讯数据保存结束后,地址为002C/002D的PDM表内存储信息为: 地址 002C 002D 主机发送的报文格式: 主机发送 从机地址 功能码 起始地址 保存数据字长度 字节数 1 1 2 2 发送信息 01 10 002C 0002 举例 发送至从机01 写多路寄存器 要写入的寄存器的起始地址 保存数据的字长度(共2字) 原来存储数据(16进制) 04B0 1388 字节数 1 1 2 2 2 发送的信息 01 06 002C 07D0 4BAF 举例 发送至地址为01的从机 写单路寄存器 要写入的寄存器地址 对应的新数据 由主机计算得到的CRC码 保存数据字节长 保存数据1 保存数据2 CRC码 1 2 2 2 04 04B0 1388 FC63 保存数据的字节长度(共4字节) 数据地址002C 数据地址002D 由主机计算得到的CRC码 从机(PDM)响应返回的报文格式: 从机响应 从机地址 功能码 起始地址 保存数据字长度 CRC码 三、错误校验码(CRC校验): 主机或从机可用校验码进行判别接收信息是否正确。由于电子噪声或一些其它干扰,信息在传输过程中有时会发生错误,错误校验码(CRC)可以检验主机或从机在通讯数据传送过程中的信息是否有误,错误的数据可以放弃(无论是发送还是接收),这样增加了系统的安全和效率。 MODBUS通讯协议的CRC(冗余循环码)包含2个字节,即16位二进制数。CRC码由发送设备(主机)计算,放置于发送信息帧的尾部。接收信息的设备(从机)再重新计算接收到信息的CRC,比较计算得到的CRC是否与接收到的相符,如果两者不相符,则表明出错。 在进行CRC计算时只用8个数据位,起始位及停止位,如有奇偶校验位也包括奇偶校验位,都不参与CRC计算。 ● CRC码的计算方法是: 1.预置1个16位的寄存器为十六进制FFFF(即全为1);称此寄存器为CRC寄存器; 2.把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低 8位相异或,把结果放于CRC寄存器; 3.把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检查右移后的移出位; 4.如果移出位为0:重复第3步(再次右移一位); 如果移出位为1:CRC寄存器与多项式A001(1010 0000 0000 0001)进行异或; 字节数 1 1 2 2 2 字节数 01 10 002C 0002 8001 举例 来自从机01 写多路寄存器 起始地址为002C 保存2个字长度的数据 由从机计算得到的CRC码

本文来源:https://www.bwwdw.com/article/c2pt.html

Top