Modbus 通讯协议编程(VB源代码)
- 格式:pdf
- 大小:94.30 KB
- 文档页数:18
Modbus 通讯协议编程协议名称:Modbus 通讯协议编程1. 引言Modbus 通讯协议是一种常用的工业通信协议,用于在不同设备之间进行数据传输和通信。
本协议旨在详细描述 Modbus 通讯协议的编程实现方法和规范,以确保设备之间的数据传输和通信的稳定性和可靠性。
2. 范围本协议适用于 Modbus 通讯协议的编程实现,包括但不限于 Modbus RTU、Modbus ASCII 和 Modbus TCP/IP 等通信方式。
3. 定义在本协议中,以下术语的定义如下:- Modbus 主站:指发起通信请求的设备。
- Modbus 从站:指响应通信请求的设备。
- 寄存器:指存储和传输数据的内存单元。
- 线圈:指用于控制设备的开关量。
4. Modbus RTU4.1 帧格式Modbus RTU 使用二进制编码,每个数据帧由以下部分组成:- 从站地址:1 字节,用于标识 Modbus 从站。
- 功能码:1 字节,用于指示操作类型。
- 数据:n 字节,根据功能码的不同而变化。
- CRC 校验:2 字节,用于校验数据的完整性。
4.2 功能码Modbus RTU 支持以下功能码:- 读取线圈状态(功能码 01):用于读取线圈的状态。
- 读取输入状态(功能码 02):用于读取输入状态。
- 读取保持寄存器(功能码 03):用于读取保持寄存器的值。
- 读取输入寄存器(功能码 04):用于读取输入寄存器的值。
- 强制单线圈(功能码 05):用于设置线圈的状态。
- 写单个保持寄存器(功能码 06):用于设置保持寄存器的值。
- 写多个线圈(功能码 15):用于设置多个线圈的状态。
- 写多个保持寄存器(功能码 16):用于设置多个保持寄存器的值。
5. Modbus ASCII5.1 帧格式Modbus ASCII 使用 ASCII 编码,每个数据帧由以下部分组成:- 起始符号(冒号):1 字节。
- 从站地址:2 字节,用于标识 Modbus 从站。
Modbus 通讯协议编程协议名称:Modbus 通讯协议编程一、引言Modbus 通讯协议是一种用于工业自动化领域的通信协议,常用于连接不同设备之间的数据交换。
本协议旨在规范Modbus通讯协议的编程实现,确保各种设备之间的数据传输准确、可靠和高效。
二、协议版本本协议基于Modbus通讯协议的最新版本进行编程实现,目前版本为Modbus协议v2.0。
三、通讯方式1. Modbus RTUModbus RTU是一种串行通讯方式,使用二进制编码进行数据传输。
通讯速率可根据实际需求进行配置,常见的包括9600bps、19200bps、38400bps等。
2. Modbus ASCIIModbus ASCII是一种基于ASCII码的串行通讯方式,使用可见字符进行数据传输。
通讯速率可根据实际需求进行配置,常见的包括9600bps、19200bps、38400bps等。
3. Modbus TCP/IPModbus TCP/IP是一种基于以太网的通讯方式,使用TCP/IP协议进行数据传输。
通讯速率可根据实际需求进行配置,常见的包括10Mbps、100Mbps、1000Mbps等。
四、数据格式1. Modbus RTU 数据格式Modbus RTU 数据帧由起始符、地址、功能码、数据、CRC校验码组成。
具体格式如下:起始符:1个字节,固定为0xFF。
地址:1个字节,表示设备地址。
功能码:1个字节,表示读取或者写入数据的功能。
数据:根据功能码的不同,数据长度可变。
CRC校验码:2个字节,用于检验数据帧的完整性。
2. Modbus ASCII 数据格式Modbus ASCII 数据帧由起始符、地址、功能码、数据、LRC校验码组成。
具体格式如下:起始符:1个字符,固定为冒号(:)。
地址:2个字符,表示设备地址。
功能码:2个字符,表示读取或者写入数据的功能。
数据:根据功能码的不同,数据长度可变。
LRC校验码:2个字符,用于检验数据帧的完整性。
基于MODBUS协议的串行通讯例程(VB)'可通过MODBUS协议获取:主电压、电流、运行状态、DIP开关设置、硬接线状态、统计数据等;'并可通过MODBUS协议控制:启动、急停、软停、双重参数调节、慢速正/反转、节能等;'本例程为通过通讯获取软启动器当前运行状态'编制:董斌 dbboss@Private Const Read_Coil_Status = &H1Private Const Read_Input_Status = &H2Private Const Read_Holding_Registers = &H3Private Const Read_Input_Registers = &H4Private Const Force_Single_Coil = &H5Private Const Single_Registers = &H6Private Const Diagnostics = &H8Private Const Force_Multiple_Coil = &HFPrivate Const Force_Multiple_Register = &H10Public Function CRC16(data() As Byte) As tCRCDim CRC16Hi As ByteDim CRC16Lo As ByteDim Result As tCRCCRC16Hi = &HFFCRC16Lo = &HFFDim i As IntegerDim iIndex As LongFor i = 0 To UBound(data)iIndex = CRC16Lo Xor data(i)CRC16Lo = CRC16Hi Xor GetCRCLo(iIndex) '低位处理CRC16Hi = GetCRCHi(iIndex) '高位处理Next iWith Result.bytLow = CRC16Lo 'CRC低位.bytHigh = CRC16Hi 'CRC高位End WithCRC16 = ResultEnd Function'CRC低位字节值表Private Function GetCRCLo(Ind As Long) As ByteGetCRCLo = Choose(Ind + 1, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80 , &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80 , &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81 , &H40, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80 , &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80 , &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80 , &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, &H80 , &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80 , &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80 , &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80 , &H41, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &H C0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &H C0, &H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &H C0, &H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &H C0, &H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &H C0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &H C0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H0, &H C1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &H C0, &H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &H C0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &H C0, &H80, &H41, &H0, &HC1, &H81, &H40)End Function'CRC高位字节值表Private Function GetCRCHi(Ind As Long) As ByteGetCRCHi = Choose(Ind + 1, &H0, &HC0, &HC1, &H1, &HC3, &H3, &H2, &HC2, &HC6, &H6, &H7, &HC7, &H5, &HC5, &HC4, &H4, &HCC, &HC, &HD, &HC D, &HF, &HCF, &HCE, &HE, &HA, &HCA, &HCB, &HB, &HC9, &H9, &H8, &HC8, &HD8, &H18, &H19, &HD9, &H1B, &HDB, &HDA, &H1A, &H1E, &HDE, &HDF, &H1 F, &HDD, &H1D, &H1C, &HDC, &H14, &HD4, &HD5, &H15, &HD7, &H17, &H16, &HD6, &HD2, &H12, &H13, &HD3, &H11, &HD1, &HD0, &H10, &HF0, &H30, &H3 1, &HF1, &H33, &HF3, &HF2, &H32, &H36, &HF6, &HF7, &H37, &HF5, &H35, &H34, &HF4, &H3C, &HFC, &HFD, &H3D, &HFF, &H3F, &H3E, &HFE, &HFA, &H3 A, &H3B, &HFB, &H39, &HF9, &HF8, &H38, &H28, &HE8, &HE9, &H29, &HEB, &H2B, &H2A, &HEA, &HEE, &H2E, &H2F, &HEF, &H2D, &HED, &HEC, &H2C, &HE 4, &H24, &H25, &HE5, &H27, &HE7, &HE6, &H26, &H22, &HE2, &HE3, &H23, &HE1, &H21, &H20, &HE0, &HA0, &H60, _&H61, &HA1, &H63, &HA3, &HA2, &H62, &H66, &HA6, &HA7, &H67, &HA5, &H65, &H64, &HA4, &H6C, &HAC, &HAD, &H6D, &HAF, &H6F, &H6E, &HAE, &H AA, &H6A, &H6B, &HAB, &H69, &HA9, &HA8, &H68, &H78, &HB8, &HB9, &H79, &HBB, &H7B, &H7A, &HBA, &HBE, &H7E, &H7F, &HBF, &H7D, &HBD, &HBC, &H 7C, &HB4, &H74, &H75, &HB5, &H77, &HB7, &HB6, &H76, &H72, &HB2, &HB3, &H73, &HB1, &H71, &H70, &HB0, &H50, &H90, &H91, &H51, &H93, &H53, &H52, &H92, &H96, &H56, &H57, &H97, &H55, &H95, &H94, &H54, &H9C, &H5C, &H5D, &H9D, &H5F, &H9F, &H9E, &H5E, &H5A, &H9A, &H9B, &H5B, &H99, &H 59, &H58, &H98, &H88, &H48, &H49, &H89, &H4B, &H8B, &H8A, &H4A, &H4E, &H8E, &H8F, &H4F, &H8D, &H4D, &H4C, &H8C, &H44, &H84, &H85, &H45, &H 87, &H47, &H46, &H86, &H82, &H42, &H43, &H83, &H41, &H81, &H80, &H40) End FunctionPublic Function Get_Logic_Status(objRS485_Comm As MSComm, nSerialLink As Byte, _tTimeOut As Double) As tLogic_Status'取软启动器逻辑状态Dim CRC As tCRC, blnTimeOut As BooleanDim outByte() As Byte, T As Double, inByte() As Long, i As Long, r As IntegerDim inChr As String, wStatus As Long, Result As tLogic_StatusReDim outByte(0 To 5) As ByteoutByte(0) = nSerialLink '从设备号outByte(1) = Read_Input_Registers '功能调用号outByte(2) = &H0 '数据所在开始地址(高字节)outByte(3) = &H0 '数据所在开始地址(低字节)outByte(4) = &H0 '读取数据个数(高字节)outByte(5) = &H1 '读取数据个数(低字节)CRC = CRC16(outByte()) '计算CRCReDim Preserve outByte(0 To 7) As ByteWith CRCoutByte(6) = .bytLowoutByte(7) = .bytHighEnd WithobjRS485_Comm.InBufferCount = 0 '清通讯口输入缓存区objRS485_Comm.Output = outByte '输出DoDoEventsLoop Until objRS485_Comm.OutBufferCount = 0 '输出直至完毕T = TimerDoIf Timer - T > tTimeOut Then '超时?blnTimeOut = TrueEnd IfLoop Until (objRS485_Comm.InBufferCount >= 7) Or blnTimeOut '超时或接收数据长度〉7时结束接收If objRS485_Comm.InBufferCount > 0 Then '处理接收数据objRS485_Comm.InputLen = 1i = 0DoinChr = objRS485_Comm.InputReDim Preserve inByte(0 To i) As LonginByte(i) = AscB(inChr)i = i + 1Loop Until objRS485_Comm.InBufferCount = 0wStatus = CLng(inByte(3) * 256) + inByte(4)With Result.blnInsulation = wStatus And &H40& '绝缘报警.blnMotorRunningSlowSpeedReverse = wStatus And &H80& '慢速反转.blnMotorRunningSlowSpeedForward = wStatus And &H100& '慢速正转.blnMotorRunningEnergySaveON = wStatus And &H200& '节能.blnDualAdjON = wStatus And &H400& '双重参数调节.blnMotorRunning = wStatus And &H800& '电机运行.blnMotorStartProcess = wStatus And &H1000& '电机起动过程中.blnMotorSoftStopProcess = wStatus And &H2000& ’电机软停过程中.blnMotorStopped = wStatus And &H4000& ‘电机停止.blnTripped = wStatus And &H8000& ‘跳闸End WithGet_Logic_Status = ResultEnd IfobjRS485_Comm.InBufferCount = 0End Function‘本程序在Windows9x/2k/xp vb6.0 下调试通过。
modbus协议代码Modbus协议是一种常用的通信协议,用于在工业控制系统中实现设备间的数据交换。
本文将介绍Modbus协议的基本原理和代码实现。
一、Modbus协议概述Modbus协议是一种主从式的通信协议,通过串行通信或以太网实现。
它定义了一种简单而有效的方式,用于在不同设备之间传输数据。
Modbus协议主要有两种模式:RTU和ASCII。
RTU模式使用二进制编码,而ASCII模式使用ASCII码编码。
Modbus协议主要包含读取和写入功能码,可以实现对设备寄存器的读取和写入操作。
二、Modbus协议数据结构Modbus协议的数据结构包括功能码、数据地址、数据长度和CRC校验等。
功能码用于标识读取或写入操作,数据地址用于指定设备寄存器的地址,数据长度用于指定读取或写入的数据长度,CRC校验用于保证数据的完整性。
三、Modbus协议读取操作在Modbus协议中,读取操作使用功能码03H。
通过发送读取请求,可以获取设备的寄存器值。
读取操作的数据结构包括从站地址、功能码、起始地址、寄存器数量和CRC校验等。
发送读取请求后,从站会返回对应地址的寄存器值。
四、Modbus协议写入操作在Modbus协议中,写入操作使用功能码06H或10H。
通过发送写入请求,可以向设备的寄存器写入数据。
写入操作的数据结构包括从站地址、功能码、数据地址、写入数据和CRC校验等。
发送写入请求后,从站会将数据写入对应地址的寄存器。
五、Modbus协议代码实现以下是使用Python语言实现Modbus协议读取操作的代码示例:```pythonimport serialimport struct# 创建串口对象ser = serial.Serial('COM1', 9600, timeout=0.5)# 读取寄存器的函数def read_registers(slave_addr, reg_addr, reg_num):# 构造读取请求request = struct.pack('>BBHH', slave_addr, 0x03, reg_addr, reg_num)# 发送请求并接收响应ser.write(request)response = ser.read(5 + 2 * reg_num)# 解析响应数据values = struct.unpack('>' + 'H' * reg_num, response[3:-2])return values# 读取从站地址为1,起始地址为0,寄存器数量为5的寄存器值values = read_registers(1, 0, 5)print(values)```以上代码示例中,首先创建了一个串口对象并打开COM1端口。
Private Sub btnSend_Click(Index As Integer)Select Case IndexCase 0 '03命令测试' MsgBox (cmbSlId(0).Text)Label9.Caption = CommandQuery03(cmbSlId(0).Text, Val(cmbRegAdd(0).Text) - 1, cmbDataType(0).Text)lblSlaveStatus(0).Caption = "读从机,等待!"Case 1 '16命令测试If Not IsNumeric(txtWriteData.Text) ThenMsgBox ("请输入数据!")txtSend(1).Text = ""txtSend(1).SetFocusExit SubEnd IftxtSend(1).Text = CommandQuery16(cmbSlId(1).Text, Val(cmbRegAdd(1).Text), cmbDataType(1).Text, txtWriteData.Text)lblSlaveStatus(1).Caption = "写从机,等待响应"txtReceive(1).Text = ""Case 2 '08命令测试If Not IsNumeric(txtDignoseAsk.Text) ThenMsgBox ("请输入数据!")txtSend(2).Text = ""txtDignoseAsk.Text = ""txtDignoseAsk.SetFocusExit SubEnd IftxtSend(2).Text = CommandQuery08(cmbSlId(2).Text, cmbDataType(2).Text, txtDignoseAsk.Text)lblSlaveStatus(2).Caption = "诊断从机,等待响应"txtReceive(2).Text = ""txtDignoseBack.Text = ""Case ElseEnd SelectEnd SubPrivate Sub Command1_Click()On Error Resume NextSetPortWith MSComm1If .PortOpen = True Then.PortOpen = FalseCommand1.Caption = "打开串口"Shape1.FillColor = &HFF&Label11.Caption = "串口已关闭"Label6.Caption = "串口状态:关闭"ElseCommand1.Caption = "关闭串口".PortOpen = TrueLabel6.Caption = "串口状态:打开"Shape1.FillColor = &HC000&Label11.Caption = "串口已打开"If Err.Number <> 0 ThenLabel11.Caption = "串口已使用"Err.ClearEnd IfEnd IfEnd With'MsgBox (MSComm1.PortOpen)End SubPrivate Sub SetPort()Dim curPortOpen As BooleanDim intCommPort As IntegerDim strCheckBit As StringDim strSettings As StringDim i As IntegerSelect Case Combo1.TextCase "COM1"intCommPort = 1Case "COM2"intCommPort = 2Case "COM3"intCommPort = 3Case "COM4"intCommPort = 4Case "COM5"intCommPort = 5Case "COM6"intCommPort = 6Case "COM7"intCommPort = 7Case "COM8"intCommPort = 8Case "COM9"intCommPort = 9Case ElseintCommPort = 1End SelectSelect Case Combo2.TextCase "NONE"strCheckBit = "n"Case "EVEN"strCheckBit = "e"Case "ODD"strCheckBit = "o"Case ElsestrCheckBit = "n"End Select'9600,n,8,1strSettings = Combo4.Text & "," & strCheckBit & "," & Combo5.Text & "," & Combo3.Text curPortOpen = MSComm1.PortOpenmPort = intCommPortMSComm1.Settings = strSettingsIf (Command1.Caption = "打开连接") ThenShape1.FillColor = &HFF&MSComm1.PortOpen = TrueCommand1.Caption = "关闭连接"ElseIf (MSComm1.PortOpen = True) ThenMSComm1.PortOpen = FalseEnd IfEnd IfEnd SubPrivate Sub MSComm1_Click()End SubPrivate Sub Form_Load()Combo5.AddItem (8)Combo5.AddItem (7)Combo3.AddItem (1)Combo3.AddItem (2)Dim i As IntegercmbDataType(0).AddItem ("16位整数")cmbDataType(0).AddItem ("32位整数")cmbDataType(0).AddItem ("64位整数")cmbDataType(0).AddItem ("32位浮点型")For i = 1 To 255cmbSlId(0).AddItem (i)Next icmbSlId(0).Text = 1cmbDataType(0).Text = "16位整数"For m = 1 To 1000cmbRegAdd(0).AddItem (m)Next mcmbRegAdd(0).Text = 100End SubPublic Function CommandQuery03(strSlaveAdd As String, strBeginAdd As String, strDataType As String) As StringDim longCRC As LongDim i As IntegerDim strQuery As StringbyteModbusQurey(0) = strSlaveAdd 'Slave AddbyteModbusQurey(1) = 3 'FunctionbyteModbusQurey(2) = 0 'RegAddHibyteModbusQurey(3) = strBeginAdd 'RegAddLobyteModbusQurey(4) = 0 'RegNumHiSelect Case strDataType 'RegNumLoCase "16位整数"byteModbusQurey(5) = 1Case "32位整数"byteModbusQurey(5) = 2Case "64位整数"byteModbusQurey(5) = 4Case "32位浮点数"byteModbusQurey(5) = 2Case ElsebyteModbusQurey(5) = 1'cmbDataType(0).Text = "16位整数"End SelectlongCRC = CRC16(byteModbusQurey, 6)byteModbusQurey(6) = (longCRC And &HFF00) \ 256 'CRCHibyteModbusQurey(7) = longCRC And &HFF 'CRCLostrQuery = ""For i = 0 To 7strQuery = strQuery + ByteDataToString(byteModbusQurey(i)) + " "Next iCommandQuery03 = strQueryintQueryNum = 8Call SendCommand(byteModbusQurey, intQueryNum)'lblSlaveStatus(0).Caption = "读取从机,等待响应"End Function'****************************************************************************** ********************************'Private Function CommandQuery16(strSlaveAdd As String, strBeginAdd As String, strDataType As String,cryWriteData as currency) As String'功能: 16号命令'参数: 无'返回: 无'修改历史:'****************************************************************************** ********************************Public Function CommandQuery16(strSlaveAdd As String, strBeginAdd As String, strDataType As String, strWriteData As String) As StringDim longCRC As LongDim i As IntegerDim strQuery As StringDim byteByteData() As ByteDim cryData As CurrencyDim intByteDataLength As IntegerDim byteTmp(1) As BytebyteModbusQurey(0) = strSlaveAdd 'Slave AddbyteModbusQurey(1) = 16 'FunctionbyteModbusQurey(2) = 0 'RegAddHibyteModbusQurey(3) = strBeginAdd 'RegAddLobyteModbusQurey(4) = 0 'RegNumHiSelect Case strDataType 'RegNumLoCase "16位整数"byteModbusQurey(5) = 1Case "32位整数"byteModbusQurey(5) = 2Case "64位整数"byteModbusQurey(5) = 4Case "32位浮点数"byteModbusQurey(5) = 2Case ElsebyteModbusQurey(5) = 1cmbDataType(1).Text = "16位整数"End SelectbyteModbusQurey(6) = byteModbusQurey(5) * 2 'ByteNumcryData = strWriteDatabyteByteData = DataToByteArray(cryData, strDataType)'按字倒序intByteDataLength = UBound(byteByteData) + 1For i = 0 To intByteDataLength \ 2 - 1 Step 2byteTmp(0) = byteByteData(i)byteTmp(1) = byteByteData(i + 1)byteByteData(i) = byteByteData(intByteDataLength - 2 - i)byteByteData(i + 1) = byteByteData(intByteDataLength - 2 + 1 - i)byteByteData(intByteDataLength - 2 - i) = byteTmp(0)byteByteData(intByteDataLength - 2 + 1 - i) = byteTmp(1)Next i'写入的数据加入请求数组For i = 0 To intByteDataLength - 1byteModbusQurey(7 + i) = byteByteData(i)Next ilongCRC = CRC16(byteModbusQurey, 7 + byteModbusQurey(6))byteModbusQurey(7 + byteModbusQurey(6)) = (longCRC And &HFF00) \ 256 'CRCHibyteModbusQurey(8 + byteModbusQurey(6)) = longCRC And &HFF'CRCLostrQuery = ""For i = 0 To 8 + byteModbusQurey(6)strQuery = strQuery + ByteDataToString(byteModbusQurey(i)) + " "Next iCommandQuery16 = strQueryintQueryNum = 9 + byteModbusQurey(6)Call SendCommand(byteModbusQurey, intQueryNum)End Function'****************************************************************************** ********************************'Private Function CommandQuery08(strSlaveAdd As String, strDataType As String, cryDignoseData As Currency) As String'功能: 08号命令'参数: 无'返回: 无'修改历史:'****************************************************************************** ********************************Public Function CommandQuery08(strSlaveAdd As String, strDataType As String, cryDignoseData As Currency) As StringDim longCRC As LongDim i As IntegerDim strQuery As StringDim byteByteData() As ByteDim cryData As CurrencyDim intByteDataLength As IntegerDim byteTmp(1) As BytebyteModbusQurey(0) = strSlaveAdd 'Slave AddbyteModbusQurey(1) = 8 'FunctionbyteModbusQurey(2) = 0 'Subfunction HibyteModbusQurey(3) = 0 'Subfunction LocryData = cryDignoseDatabyteByteData = DataToByteArray(cryData, strDataType)byteModbusQurey(4) = byteByteData(0)byteModbusQurey(5) = byteByteData(1)longCRC = CRC16(byteModbusQurey, 6)byteModbusQurey(6) = (longCRC And &HFF00) \ 256 'CRCHibyteModbusQurey(7) = longCRC And &HFF 'CRCLostrQuery = ""For i = 0 To 7strQuery = strQuery + ByteDataToString(byteModbusQurey(i)) + " "Next iCommandQuery08 = strQueryintQueryNum = 8Call SendCommand(byteModbusQurey, intQueryNum)End FunctionPublic Function CRC16(data() As Byte, length As Integer) As Long Dim i As Integer, j As IntegerDim Bit As BooleanDim Temp As ByteDim CRC As LongDim Generator As LongCRC = 65535Generator = 40961For i = 0 To length - 1Temp = data(i)CRC = CRC Xor TempFor j = 1 To 8Bit = CRC And 1CRC = CRC \ 2If Bit = True ThenCRC = CRC Xor GeneratorEnd IfNext jNext iCRC16 = CRC \ 256CRC = (CRC - CRC16 * 256) * 256CRC16 = CRC + CRC16End FunctionPublic Function ByteDataToString(data As Byte) As StringDim OutString As StringDim datahi As ByteDim datalo As Bytedatahi = data \ 16datalo = data And &HFSelect Case datahiCase 0OutString = "0"Case 1OutString = "1"Case 2OutString = "2"Case 3OutString = "3"Case 4OutString = "4"Case 5OutString = "5"Case 6OutString = "6"Case 7OutString = "7"Case 8OutString = "8"Case 9OutString = "9"Case 10OutString = "A"Case 11OutString = "B"Case 12OutString = "C"Case 13OutString = "D"Case 14OutString = "E"Case 15OutString = "F"Case ElseOutString = "0"End SelectSelect Case dataloCase 0OutString = OutString + "0" Case 1OutString = OutString + "1" Case 2OutString = OutString + "2" Case 3OutString = OutString + "3" Case 4OutString = OutString + "4" Case 5OutString = OutString + "5" Case 6OutString = OutString + "6"Case 7OutString = OutString + "7"Case 8OutString = OutString + "8"Case 9OutString = OutString + "9"Case 10OutString = OutString + "A"Case 11OutString = OutString + "B"Case 12OutString = OutString + "C"Case 13OutString = OutString + "D"Case 14OutString = OutString + "E"Case 15OutString = OutString + "F"Case ElseOutString = OutString + "0"End SelectByteDataToString = OutStringEnd FunctionPublic Sub SendCommand(byteQueryArr() As Byte, length As Integer) Dim byteQ() As ByteDim i As IntegerReDim byteQ(length) As ByteFor i = 0 To length - 1byteQ(i) = byteQueryArr(i)Next iIf MSComm1.PortOpen = True ThenMSComm1.OutBufferCount = 0 '清空发送缓冲区MSComm1.Output = byteQintResponseNum = 0MSComm1.InBufferCount = 0 '清空接收缓冲区MSComm1.RThreshold = 1'等待接收数据timerReceiveTimeOut.Interval = 3000 '三秒无数据报错误timerReceiveTimeOut.Enabled = True '启动定时器End IfEnd SubPublic Sub delay(ms As Integer)Dim Savetime As DoubleSavetime = timeGetTime '记下开始时的时间While timeGetTime < Savetime + ms '循环等待DoEvents '转让控制权,以便让操作系统处理其它的事件。
Modbus 通讯协议编程协议名称:Modbus 通讯协议编程一、引言Modbus 通讯协议是一种常用于工业自动化领域的通信协议,用于在不同设备之间进行数据传输和通信。
本协议旨在规范Modbus通信协议的编程实施,确保设备之间的数据交换和通信的准确性和稳定性。
二、协议版本本协议基于Modbus协议版本1.0进行编程实施。
三、通信方式1. Modbus RTU通信方式:- 通信速率:9600 bps- 数据位:8位- 停止位:1位- 奇偶校验:无校验2. Modbus ASCII通信方式:- 通信速率:9600 bps- 数据位:7位- 停止位:1位- 奇偶校验:偶校验四、数据帧格式1. Modbus RTU数据帧格式:- 起始位:1位- 设备地址:1位- 功能码:1位- 数据:n位- CRC校验:16位- 结束位:1位2. Modbus ASCII数据帧格式:- 起始符:1位冒号(:)- 设备地址:2位十六进制数- 功能码:2位十六进制数- 数据:n位十六进制数- LRC校验:2位十六进制数- 结束符:2位回车换行符(\r\n)五、通信功能码1. 读取线圈状态(读取线圈开关状态):- 功能码:01H- 请求数据长度:4字节2. 读取输入状态(读取输入开关状态): - 功能码:02H- 请求数据长度:4字节- 响应数据长度:n字节3. 读取保持寄存器(读取设备内部数据): - 功能码:03H- 请求数据长度:4字节- 响应数据长度:n字节4. 读取输入寄存器(读取设备输入数据): - 功能码:04H- 请求数据长度:4字节- 响应数据长度:n字节5. 写单个线圈(控制单个线圈开关状态): - 功能码:05H- 请求数据长度:4字节- 响应数据长度:4字节6. 写单个寄存器(写入设备内部数据): - 功能码:06H- 响应数据长度:4字节7. 强制多个线圈(控制多个线圈开关状态):- 功能码:0FH- 请求数据长度:n字节- 响应数据长度:4字节8. 写多个寄存器(写入设备内部数据):- 功能码:10H- 请求数据长度:n字节- 响应数据长度:4字节六、通信流程1. 主机向从机发送请求数据帧。
如何用VB实现Modbus串行通讯在一些应用中可能需要使用诸如VB来进行上位机监控程序的开发,而Modbus协议是这类应用中首选的通讯协议;Modbus协议以其简单易用,在工业领域里已广泛的为其他第三方设备所支持。
这里对VB和Twido PLC间的通讯进行说明。
对于大部分应用,Twido PLC作为从站,它不需要编制通讯程序,只要把通讯口的参数设置好即可,例如下图表示此Twido通过编程口和上位机连接,其站号地址为2;波特率、数据位、校验、停止位和上位机设置保持一致。
VB程序通过利用MSComm控件很容易就能够实现。
1.通讯口初始化:MSComm1.Settings = "9600,n,8,1"mPort = 1MSComm1.SThreshold = 0If Not MSComm1.PortOpen Then MSComm1.PortOpen = True2.CRC校验码的计算方法,如以下函数,可以得到字节数组变量cmdstring指向的字符串的CRC校验码。
Function crc16_1(ByRef cmdstring() As Byte, ByVal j As Integer)Dim data As IntegerDim i As IntegerAddressreg_crc = &HFFFFFor i = 0 To jAddressreg_crc = Addressreg_crc Xor cmdstring(i)For j = 0 To 7data = Addressreg_crc And &H1If data ThenAddressreg_crc = Int(Addressreg_crc / 2)Addressreg_crc = Addressreg_crc And &H7FFFAddressreg_crc = Addressreg_crc Xor &HA001ElseAddressreg_crc = Addressreg_crc / 2Addressreg_crc = Addressreg_crc And &H7FFFEnd IfNext jNext iIf Addressreg_crc < 0 ThenAddressreg_crc = Addressreg_crc - &HFFFF0000End IfHiByte = Addressreg_crc And &HFFLoByte = (Addressreg_crc And &HFF00) / &H100End Function3.读多个字的命令(本例是从2号站读%MW10起始的4个字):Dim SendStr(7) As ByteDim RcvStr() As ByteSendStr(0) = 2 ,从站号是2SendStr(1) = &H3 ,读多个字的命令代码SendStr(2) = 0 ,起始地址高字节SendStr(3) = 10,起始地址低字节SendStr(4) = &H0,数据长度高字节SendStr(5) = 4 ,数据长度低字节Call crc16(SendStr(), 5) ,CRC计算SendStr(6) = HiByteSendStr(7) = LoByte,读命令发送后,当接收5 + SendStr(5) * 2 个字节时产生中断CmdLenth = 5 + SendStr(5) * 2MSComm1.RThreshold = CmdLenthMSComm1.Output = SendStr ,发送命令4.写多个字的命令(本例是写2号站%MW20起始的3个字):Dim WriteStr() As Bytek = 6 ,写6个字节ReDim WriteStr(8 + k)WriteStr(0) = 2 ,从站号是2WriteStr(1) = &H10 ,写多个字的命令代码WriteStr(2) = 0 ,起始地址高字节WriteStr(3) = 20 ,起始地址低字节WriteStr(4) = &H0 ,数据长度高字节<字的个数> WriteStr(5) = k / 2 ,数据长度低字节<字的个数>WriteStr(6) = k ,数据长度<字节的个数> WriteStr(7) = &H12,写的第1个字的高字节WriteStr(8) = &H34,写的第1个字的低字节WriteStr(9) = &H56,写的第2个字的高字节WriteStr(10) = &H78,写的第2个字的低字节WriteStr(11) = &H9A,写的第3个字的高字节WriteStr(12) = &HBC,写的第3个字的低字节Call crc16(WriteStr(), 6 + k)WriteStr(9 + (k / 2 - 1) * 2) = HiByteWriteStr(10 + (k / 2 - 1) * 2) = LoByteMSComm1.InBufferCount = 0MSComm1.Output = WriteStr,写命令发送后,当接收到8个字节时中断CmdLenth = 8MSComm1.RThreshold = CmdLenth5.通讯事件中断产生时的数据处理:Private Sub MSComm1_OnComm()Dim inx() As ByteSelect Case mEventCase comEvReceive ,判断为接收事件MSComm1.InputLen = CmdLenth ,接收数据的长度inx = MSComm1.Input ,接收数据MSComm1.InBufferCount = 0For k = 3 To CmdLenth - 3tmpstr = tmpstr & "/" & Hex(inx(k))NextText1.Text = tmpstr ,以十六进制显示所接收长度的数据BeepEnd SelectEnd Sub。
用VB写的modbusrtu模式通讯源码‘用VB 写的modbus rtu模式通讯源码,已在台达PLC上调试通过Private Sub CmdOpen_Click()On Error Resume NextIf (MSComm1.PortOpen) Then ‘打开/关闭串口MSComm1.PortOpen = FalseElseMSComm1.PortOpen = TrueEnd IfIf (MSComm1.PortOpen) ThenCmdOpen.Caption = "关闭串口"Shape5.FillStyle = vbFSSolidElseCmdOpen.Caption = "打开串口"Shape5.FillStyle = vbFSTransparentEnd IfIf Err ThenMsgBox Error$, 48, "错误码信息"Exit SubEnd IfEnd SubPrivate Sub Combo1_Click()/doc/df18763589.html,mPort = Combo1.ListIndex + 1End SubPrivate Sub Combo2_Click()Call SettingEnd SubPrivate Sub Combo3_Click()Call SettingEnd SubPrivate Sub Combo4_Click()Call SettingEnd SubPrivate Sub Combo5_Click()Call SettingEnd SubPrivate Sub Command1_Click()‘S hape1.FillStyle = vbFSSolidDim Y0_status As ByteDim Sendstr As StringDim i As Integer, j As IntegerSendstr = "01 01 05 00 00 10 "HexSend (Sendstr)Sleep (30)HexSend (Sendstr)End SubPrivate Function HexSend(Sendstr As String) As Integer Dim outbuf() As ByteDim Temp(0) As ByteDim crc As String, Sendstrls As StringDim sendlen As IntegerDim i As Integer, j As IntegerIf Sendstr = "" ThenMsgBox "发送数据不能为空!"HexSend = 0Exit FunctionEnd IfS endstrls = Trim(Sendstr) ‘去掉空格sendlen = Len(Sendstrls) + 1 ‘取长度j = 0ReDim outbuf(1 To sendlen \ 3) As ByteFor i = 1 To sendlen Step 3j = j + 1outbuf(j) = Val("&H" & CStr(Mid(Sendstrls, i, 2)))Next icrc = Crc16(outbuf)ReDim Preserve outbuf(1 T o (sendlen \ 3 + 2)) As Byte ‘加上CRC校验码outbuf(sendlen \ 3 + 1) = Val("&H" & CStr(Mid(crc, 1, 2)))outbuf(sendlen \ 3 + 2) = Val("&H" & CStr(Mid(crc, 3, 2)))For i = 1 To (sendlen \ 3 + 2)Temp(0) = outbuf(i)MSComm1.Output = TempNext iFor i = 1 To 2000Next iHexSend = 1End FunctionPrivate Function Setting()MSComm1.Settings = CStr(Combo2.Text) & "," & CStr(Combo3.Text) & "," & CStr(Combo4.Text) & "," & CStr(Combo5.Text) End FunctionPrivate Sub Command2_Click()‘If (MSComm1.RThreshold = 0) Then‘MSComm1.RTh reshold = 1‘Else‘MSComm1.RThreshold = 0‘End IfLabel11.Caption = "接收个数:" & CStr(ReceCount) & " " & "接收帧数:" & CStr(Framecount) End SubPrivate Sub Form_Load()Combo1.AddItem ("COM1")Combo1.AddItem ("COM2")Combo1.AddItem ("COM3")Combo1.AddItem ("COM4")Combo1.AddItem ("COM5")Combo1.ListIndex = 0Combo2.AddItem ("2400")Combo2.AddItem ("4800")Combo2.AddItem ("9600")Combo2.AddItem ("11520")Combo2.ListIndex = 0Combo3.AddItem ("E")Combo3.AddItem ("O")Combo3.AddItem ("N")Combo3.ListIndex = 2Combo4.AddItem ("6")Combo4.AddItem ("7")Combo4.AddItem ("8")Combo4.ListIndex = 2Combo5.AddItem ("1")Combo5.AddItem ("2")Combo5.ListIndex = 0ReceCount = 0End SubPrivate Function Crc16(data() As Byte) As StringDim CRC16Lo As Byte, CRC16Hi As Byte ‘CRC寄存器Dim CL As Byte, CH As Byte ‘多项式码&HA001Dim CrcLo As String, CrcHi As StringDim SaveHi As Byte, SaveLo As ByteDim i As IntegerDim Flag As IntegerCRC16Lo = &HFFCRC16Hi = &HFFCL = &H1CH = &HA0For i = 1 To UBound(data)CRC16Lo = CRC16Lo Xor data(i) ‘每一个数据与CRC寄存器进行异或For Flag = 0 To 7SaveHi = CRC16HiSaveLo = CRC16LoCRC16Hi = CRC16Hi \ 2 ‘高位右移一位CRC16Lo = CRC16Lo \ 2 ‘低位右移一位If ((SaveHi And &H1) = &H1) Then ‘如果高位字节最后一位为1CRC16Lo = CRC16Lo Or &H80 ‘则低位字节右移后前面补1 End If ‘否则自动补0If ((SaveLo And &H1) = &H1) Then ‘如果LSB为1,则与多项式码进行异或CRC16Hi = CRC16Hi Xor CHCRC16Lo = CRC16Lo Xor CLEnd IfNext FlagNext iIf Len(Hex(CRC16Hi)) = 1 ThenCrcHi = "0" + Hex(CRC16Hi)ElseCrcHi = Hex(CRC16Hi)End IfIf Len(Hex(CRC16Lo)) = 1 ThenCrcLo = "0" + Hex(CRC16Lo)ElseCrcLo = Hex(CRC16Lo)End IfCrc16 = CrcLo & CrcHiEnd FunctionPrivate Sub MSComm1_OnComm()Dim inpu() As ByteDim i As IntegerDim tempstr As String, Strdata As StringSelect Case /doc/df18763589.html,mEvent Case comEvReceive ‘接收事件tempstr = MSComm1.Inputinpu() = tempstrFramecount = Framecount + 1 ‘帧个数加1If (Framecount = 1) Thenframepoint(Framecount) = UBound(inpu) + 1 ‘第一帧帧尾Elseframepoint(Framecount) = framepoint(Framecount - 1) + UBound(inpu) + 1 ‘第二帧开始指针End IfFor i = 0 To UB ound(inpu) ‘将字符转换为数组If (Len(Hex(inpu(i))) = 1) ThenStrdata = Strdata & "0" & Hex(inpu(i)) & " "ElseStrdata = Strdata & Hex(inpu(i)) & " "End IfNext iFor i = ReceCount + 1 To UBound(inpu) + 1 ‘数据进入缓冲区Recebuf(i) = inpu(i - 1)NextReceCount = ReceCount + UBound(inpu) + 1TextReceive.Text = TextReceive.Text & StrdataStrdata = ""Case comEvSendEnd SelectEnd SubPrivate Function RtuCheck(data() As Byte) As IntegerDim CrcHi As Byte, CrcLo As ByteDim Checkdata() As ByteDim i As IntegerDim crc As StringCrcHi = data(UBound(data))CrcLo = data(UBound(data) - 1)ReDim Checkdata(1 To (UBound(data) - 1)) As ByteFor i = 1 To (UBound(data) - 1) ‘附值Checkdata(i) = data(i - 1)Nextcrc = Crc16(Checkdata)If (CrcLo = Val("&H" & CStr(Mid(crc, 1, 2))) And CrcHi = Val("&H" & CStr(Mid(crc, 3, 2)))) Then RtuCheck = 1ElseRtuCheck = 0End IfEnd Function。
'Global Data DefinitionsDim MyHandle As Long 'Handle to ConnectionDim MyStatus As Integer 'Status returned from mbMasterV7 Control Dim Slave As Integer 'Slave, Cmd, Address, & LengthDim Cmd As IntegerDim Address As LongDim Length As IntegerDim LoopbackMsg(20) As BytePublic Sub show_status(ErrCode As Integer)If (ErrCode = 0) ThenSTATUS.Text = "正常通行ing"ElseIf (ErrCode < 255) Then' STATUS.Text = "Slave Device Exception Response"STATUS.Text = "从设备没有响应"ElseIf (ErrCode = 256) ThenSTATUS.Text = "无效连接"ElseIf (ErrCode = 257) ThenSTATUS.Text = "消息超时"ElseIf (ErrCode = 258) ThenSTATUS.Text = "无效地址"ElseIf (ErrCode = 259) ThenSTATUS.Text = "无效从设备地址"ElseIf (ErrCode = 260) ThenSTATUS.Text = "无效数据长度"ElseIf (ErrCode = 261) ThenSTATUS.Text = "不支持modbus命令格式"ElseIf (ErrCode = 263) ThenSTATUS.Text = "从设备超时"ElseIf (ErrCode = 264) ThenSTATUS.Text = "无效传输模式"ElseIf (ErrCode = 265) ThenSTATUS.Text = "CRC校验错误"ElseIf (ErrCode = 266) ThenSTATUS.Text = "没有建立连接"ElseIf (ErrCode = 267) ThenSTATUS.Text = "无效从设备响应"ElseIf (ErrCode = 271) ThenSTATUS.Text = "演示时间到"ElseIf (ErrCode = 272) ThenSTATUS.Text = "无效modbus/TCP 命令"End IfEnd Sub' Hide the contrtol when the form loadsPrivate Sub Form_Activate()MbMasterV71.HideControlEnd Sub' Handler for the CONNECT SERIAL ButtonPrivate Sub ConnectSerial_Click()' Connect to COMM PortMbMasterV71.BaudRate = 9600 '9600 BaudMbMasterV71.Parity = 0 '0=NOPARITY, 1=ODDPARITY, 2=EVENPARITY, 3=MARKPARITY, 4=SPACEPARITYMbMasterV71.DataBits = 8 '8 DataBitsMbMasterV71.StopBits = 0 '0=ONESTOPBIT, 1=ONE5STOPBITS, 2=TWOSTOPBITSMbMasterV71.TimeOut = 2000 '2000 msecMbMasterV71.TransmissionMode = 1 '0=ASCII, 1=RTUMyHandle = MbMasterV71.ConnectSerial(1) ' Connect to COMM Port 1If MyHandle > 0 Then' Connection was successful' (This example only allows a single connection)' Disable All Connection Buttons' Enable the Read, Write & Disconnect ButtonsConnectSerial.Enabled = FalseConnectTAPI.Enabled = FalseConnectTCP.Enabled = FalseDisconnect.Enabled = True' LoopBackTst.Enabled = TrueSTATUS.Text = "正在连接ing"READMODBUS.Enabled = TrueWRITEMODBUS.Enabled = TrueElse'Connection Attempt Failed'(Another application must have control of the COM Port)STATUS.Text = "串口忙,请稍候"End IfEnd Sub' Handler for the CONNECT TAPI ButtonPrivate Sub ConnectTAPI_Click()Dim nTAPIDevices As LongDim TAPIDevice As String'Go through the motions of getting the TAPI Device ListnTAPIDevices = MbMasterV71.NumberOfTAPIDevices()TAPIDevice = MbMasterV71.GetTAPIDeviceName(0)'Setup the phone number to dialMbMasterV71.PhoneNumber = "645-5966"'Dial the callMyHandle = MbMasterV71.DialTAPIDevice(0)If MyHandle > 0 Then'Call should be in progress now'Don't enable the Read & Write Buttons'until we get the CallEstablished EventSTATUS.Text = "正在连接ing"ConnectSerial.Enabled = FalseConnectTAPI.Enabled = FalseConnectTCP.Enabled = FalseDisconnect.Enabled = False'LoopBackTst.Enabled = FalseREADMODBUS.Enabled = FalseWRITEMODBUS.Enabled = FalseElseSTATUS.Text = "没有连接"End IfEnd Sub' Handler for the CONNECT TCP ButtonPrivate Sub ConnectTCP_Click()' Select the Device to connec to' In this case use the IP Loopback address to' connect to the local machineMbMasterV71.TCPDevice = "127.0.0.1"MyHandle = MbMasterV71.ConnectModbusTCP(502)If MyHandle > 0 Then'Connection should be in progress now'Don't enable the Read & Write Buttons'until we get the CallEstablished EventSTATUS.Text = "正在连接ing"ConnectSerial.Enabled = FalseConnectTAPI.Enabled = FalseConnectTCP.Enabled = FalseDisconnect.Enabled = False' LoopBackTst.Enabled = FalseREADMODBUS.Enabled = FalseWRITEMODBUS.Enabled = FalseElseSTATUS.Text = "没有连接"End IfEnd Sub' ConnectionEstablished Event Handler' Initiated from either ConnectModbusTCP() or DialTAPIDevice()Private Sub MbMasterV71_ConnectionEstablished(ByVal hConnect As Long)'Enable the Disconnect Button'Enable Read & Write ButtonsConnectSerial.Enabled = FalseConnectTAPI.Enabled = FalseConnectTCP.Enabled = FalseDisconnect.Enabled = True'LoopBackTst.Enabled = TrueSTATUS.Text = "正常通信ing"READMODBUS.Enabled = TrueWRITEMODBUS.Enabled = TrueEnd Sub' ConnectionDropped Event HandlerPrivate Sub MbMasterV71_ConnectionDropped(ByVal hConnect As Long) 'Either the TCP or TAPI connection attempt failed'or something has happened to abort the connection after it'has been established for a while.'In either case we're now disconnected so enable the'buttons accordinglyMyHandle = -1ConnectSerial.Enabled = TrueConnectTAPI.Enabled = TrueConnectTCP.Enabled = TrueDisconnect.Enabled = False'LoopBackTst.Enabled = FalseSTATUS.Text = "没有连接"READMODBUS.Enabled = FalseWRITEMODBUS.Enabled = FalseEnd Sub' Handler for the DISCONNECT ButtonPrivate Sub Disconnect_Click()'Tell the control to DisconnectMyStatus = MbMasterV71.Disconnect(MyHandle)MyHandle = -1'ReEnable the Connect Buttons for new connection attemptConnectSerial.Enabled = TrueConnectTAPI.Enabled = TrueConnectTCP.Enabled = TrueDisconnect.Enabled = False'LoopBackTst.Enabled = FalseSTATUS.Text = "没有连接"READMODBUS.Enabled = FalseWRITEMODBUS.Enabled = FalseEnd SubPrivate Sub Frame2_DragDrop(Source As Control, X As Single, Y As Single)End Sub' Handler for the READ ButtonPrivate Sub READMODBUS_Click()'Get the Slave Node Address, Modbus Command, Address & Length' from the appropriate Edit controlsSlave = NODEADDRESS.TextCmd = POINTTYPE.TextAddress = READADDRESS.TextLength = READLENGTH.Text'We must remember these parameters so we can use'them in the ReadResponse method to make sure we'get what we ask for''Initiate the Read RequestMyStatus = MbMasterV71.PollModbus(MyHandle, Slave, Cmd, Address, Length)'Check the status to make sure the request went out.If MyStatus = 0 ThenSTATUS.Text = "串口忙,请稍侯"Elseshow_status (MyStatus)End IfEnd Sub'Process the Slave Read Response Message'Modbus_Master SlaveReadResponse Event HandlerPrivate Sub MbMasterV71_SlaveReadResponse(ByVal hConnect As Long)Dim MyData As LongDim i As Integer' Read the data returned from the slave' and update the text controlsFor i = 0 To Length - 1MyStatus = MbMasterV71.ReadResults(hConnect, Slave, Cmd, Address + i, MyData) show_status (MyStatus)If MyStatus = 0 Then'Text1(i).Text = MyDataIf i = 0 Or i = 8 Or i = 16 ThenText1(i).Text = MyData * 系数End IfIf i = 2 Or i = 10 Or i = 18 ThenText1(i).Text = MyData * 系数End IfIf i = 4 Or i = 12 Or i = 20 Or i = 6 Or i = 14 Or i = 22 ThenText1(i).Text = MyData * 系数End IfIf i = 28 Or i = 30 Then '3P,3QText1(i).Text = MyData * 系数End IfIf i = 29 Then 'cosqText1(i).Text = MyData * 系数End IfIf i = 27 Then 'FText1(i).Text = MyData * 系数End IfIf i = 1 Or i = 5 Or i = 7 Or i = 9 Or i = 13 Or i = 15 Or i = 17 Or i = 21 Or i = 23 Or i = 25 Or i = 23 Or i = 26 Or i = 31 ThenText1(i).Text = 0 '没有测量的量默认为0End If' Else' Text1(i).Text = "Error"End IfNext iEnd SubPrivate Sub WRITEMODBUS_Click()Dim IsRegister As BooleanDim i As IntegerDim junk As IntegerSlave = NODEADDRESS.TextAddress = WRITEADDRESS.TextLength = WRITELENGTH.T extIf Length > 200 ThenLength = 200End IfFor i = 0 To Length - 1junk = MbMasterV71.FillWriteBuffer(MyHandle, i, PATTERN.Text)Next iIf POINTTYPE.Text < 2 ThenIf Length = 1 ThenCmd = 5 'write single coilElseCmd = 15 'write multiple coilsEnd IfElseIf Length = 1 ThenCmd = 6 'write single registerElseCmd = 16 'write multiple registersEnd IfEnd IfMyStatus = MbMasterV71.WRITEMODBUS(MyHandle, Slave, Cmd, Address, Length) ' Make sure the write request was transmittedIf MyStatus = 0 ThenSTATUS.Text = "串口忙,请稍侯"Elseshow_status (MyStatus)End IfEnd Sub'Process the Slave Write Response Message'Modbus_Master SlaveWriteResponse Event HandlerPrivate Sub MbMasterV71_SlaveWriteResponse(ByVal hConnect As Long) 'read the results of the write requestMyStatus = MbMasterV71.WriteResults(hConnect, Slave, Cmd, Address, Length) ' and update the status displayshow_status (MyStatus)End SubPrivate Sub LoopBackTst_Click()Slave = NODEADDRESS.TextCmd = 8MyStatus = MbMasterV71.FillUserMsgBuffer(MyHandle, 0, Slave)MyStatus = MbMasterV71.FillUserMsgBuffer(MyHandle, 1, Cmd)MyStatus = MbMasterV71.FillUserMsgBuffer(MyHandle, 2, 0)MyStatus = MbMasterV71.FillUserMsgBuffer(MyHandle, 3, 0)MyStatus = MbMasterV71.FillUserMsgBuffer(MyHandle, 4, 0)MyStatus = MbMasterV71.FillUserMsgBuffer(MyHandle, 5, 0)MyStatus = MbMasterV71.SendUserMsg(MyHandle, 6)'Check the status to make sure the request went out.If MyStatus = 0 ThenSTATUS.Text = "串口忙,请稍侯"Elseshow_status (MyStatus)End IfEnd SubPrivate Sub MbMasterV71_UserMsgResponse(ByVal hConnect As Long, ByVal NumberOfBytes As Long)Dim temp As IntegerIf NumberOfBytes > 0 ThenFor i = 0 To NumberOfBytes - 1MyStatus = MbMasterV71.ReadUserMsgResponse(hConnect, i, temp)LoopbackMsg(i) = tempNext iSTATUS.Text = LoopbackMsgEnd IfEnd Sub。
VB编写的Modbus RTU协议通讯源程序modbus rtu协议可以算是一种事实上的工业标准协议,为许多仪表、PLC等所支持。
以前有几个用户问如何使用VB编程来与我们的KND-K3系列PLC通讯,于是整了一个demo程序。
这次把这个demo共享,希望能给大家一点帮助。
1)模块文件:modCRC,其中包含了CRC校验的函数。
'data 待校验的数组名称'no 数组中元素个数'btLoCRC 算出的CRC高字节'btHiCRC 算出的CRC低字节Public Function CalCRC16Fast(data() As Byte, no As Integer, btLoCRC As Byte, btHiCRC As Byte) As StringDim CL As Byte, CH As Byte '多项式码&HA001Dim SaveHi As Byte, SaveLo As ByteDim i As IntegerDim Flag As IntegerbtHiCRC = &HFFbtLoCRC = &HFFCL = &H1CH = &HA0For i = 0 To (no - 1)btHiCRC = btHiCRC Xor data(i) '每一个数据与CRC寄存器进行异或For Flag = 0 To 7SaveHi = btLoCRCSaveLo = btHiCRCbtLoCRC = btLoCRC \ 2 '高位右移一位btHiCRC = btHiCRC \ 2 '低位右移一位If ((SaveHi And &H1) = &H1) Then '如果高位字节最后一位为1btHiCRC = btHiCRC Or &H80 '则低位字节右移后前面补1End If '否则自动补0If ((SaveLo And &H1) = &H1) Then '如果LSB为1,则与多项式码进行异或btLoCRC = btLoCRC Xor CHbtHiCRC = btHiCRC Xor CLEnd IfNext FlagNext iDim ReturnData(1) As ByteReturnData(0) = btHiCRC 'CRC高位ReturnData(1) = btLoCRC 'CRC低位CalCRC16Fast = ReturnDataEnd FunctionPublic Function CalCRC16Tbl(data() As Byte, no As Integer, btLoCRC As Byte, btHiCRC As Byte) As StringDim btLoCRC As ByteDim btHiCRC As BytebtLoCRC = &HFFbtHiCRC = &HFFDim i As IntegerDim iIndex As LongFor i = 0 To (no - 1)iIndex = btHiCRC Xor data(i)btHiCRC = btLoCRC Xor GetCRCLo(iIndex) '低位处理btLoCRC = GetCRCHi(iIndex) '高位处理Next iDim ReturnData(1) As ByteReturnData(0) = btHiCRC 'CRC高位ReturnData(1) = btLoCRC 'CRC低位CalCRC16Tbl = ReturnDataEnd Function'CRC低位字节值表Function GetCRCLo(Ind As Long) As ByteGetCRCLo = Choose(Ind + 1, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, _&H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, _&H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H0, &HC1, _&H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, &H80, &H41, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H0, &HC1, _&H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, _&H1, &HC0, &H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, _&H81, &H40, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, _&H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, _&H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, _&H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, _&H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, _&H1, &HC0, &H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, _&H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40)End Function'CRC高位字节值表Function GetCRCHi(Ind As Long) As ByteGetCRCHi = Choose(Ind + 1, _&H0, &HC0, &HC1, &H1, &HC3, &H3, &H2, &HC2, &HC6, &H6, &H7, &HC7, &H5, &HC5, &HC4, &H4, &HCC, &HC, &HD, &HCD, &HF, &HCF, &HCE, &HE, &HA, &HCA, &HCB, &HB, &HC9, &H9, &H8, &HC8, &HD8, &H18, &H19, &HD9, &H1B, &HDB, &HDA, &H1A, &H1E, &HDE, &HDF, &H1F, &HDD, &H1D, &H1C, &HDC, &H14, &HD4, &HD5, &H15, &HD7, &H17, &H16, &HD6, &HD2, &H12, &H13, &HD3, &H11, &HD1, &HD0, &H10, &HF0, &H30, &H31, &HF1, &H33, &HF3, &HF2, &H32, &H36, &HF6, &HF7, &H37, &HF5, &H35, &H34, &HF4, &H3C, &HFC, &HFD, &H3D, &HFF, &H3F, &H3E, &HFE, &HFA, &H3A, &H3B, &HFB, &H39, &HF9, &HF8, &H38, &H28, &HE8, &HE9, &H29, &HEB, &H2B, &H2A, &HEA, &HEE, &H2E, &H2F, &HEF, &H2D, &HED, &HEC, &H2C, &HE4, &H24, &H25, &HE5, &H27, &HE7, &HE6, &H26, &H22, &HE2, &HE3, &H23, &HE1, &H21, &H20, &HE0, &HA0, &H60, _ &H61, &HA1, &H63, &HA3, &HA2, &H62, &H66, &HA6, &HA7, &H67, &HA5, &H65, &H64, &HA4, &H6C, &HAC, &HAD, &H6D, &HAF, &H6F, &H6E, &HAE, &HAA, &H6A, &H6B, &HAB, &H69, &HA9, &HA8, &H68, &H78, &HB8, &HB9, &H79, &HBB, &H7B, &H7A, &HBA, &HBE, &H7E, &H7F, &HBF, &H7D, &HBD, &HBC, &H7C, &HB4, &H74, &H75, &HB5, &H77, &HB7, &HB6, &H76, &H72, &HB2, &HB3, &H73, &HB1, &H71, &H70, &HB0, &H50, &H90, &H91, &H51, &H93, &H53, &H52, &H92, &H96, &H56, &H57, &H97, &H55, &H95, &H94, &H54, &H9C, &H5C, &H5D, &H9D, &H5F, &H9F, &H9E, &H5E, &H5A, &H9A, &H9B, &H5B, &H99, &H59, &H58, &H98, &H88, &H48, &H49, &H89, &H4B, &H8B, &H8A, &H4A, &H4E, &H8E, &H8F, &H4F, &H8D, &H4D, &H4C, &H8C, &H44, &H84, &H85, &H45, &H87, &H47, &H46, &H86, &H82, &H42, &H43, &H83, &H41, &H81, &H80, &H40)End Function2)窗体:FORM1,上面放置的控件如下:Begin VB.Form frmComCaption = "Form1"ClientHeight = 8235ClientLeft = 3885ClientTop = 2250ClientWidth = 6810LinkTopic = "Form1"ScaleHeight = 8235ScaleWidth = 6810Begin VB.TextBox txtReceive ‘注:放置接收上来的IB0数据Height = 495Left = 1200TabIndex = 2Top = 2280Width = 1335EndBegin mandButton Command1Caption = "读取IB0"Height = 495Left = 2760TabIndex = 1Top = 2280Width = 1695EndBegin mandButton cmdSDOCaption = "置位Q1.1"Height = 495Left = 2160TabIndex = 0Top = 3720Width = 1575EndBegin MSCommLib.MSComm ComK3Left = 480Top = 1080_ExtentX = 1005_ExtentY = 1005_V ersion = 393216DTREnable = -1 'TrueEnd①Form_Load事件,在此主要是实现了打开并初始化串口Private Sub Form_Load()With ComK3.CommPort = 1.Settings = "19200,N,8,1".InputMode = comInputModeBinary '二进制收发.InBufferSize = 512.OutBufferSize = 512If (Not .PortOpen) Then .PortOpen = True End WithEnd Sub②Form_UnLoad事件,在此主要是关闭串口Private Sub Form_Unload(Cancel As Integer)If (ComK3.PortOpen) ThenComK3.PortOpen = FalseEnd IfEnd Sub③“置位Q1.1”按钮单击事件'设置Q1.1为1Private Sub cmdSDO_Click()Dim btSend(8) As BytebtSend(0) = &H1 '目标站号btSend(1) = &H5 '功能码btSend(2) = &H0 'Q1.1地址(0009)高字节btSend(3) = &H9 'Q1.1地址(0009)低字节btSend(4) = &HFF '强制值高字节btSend(5) = &H0 '强制值低字节Dim crcDim btCRCHi As Byte, btCRCLo As Bytecrc = CalCRC16Fast(btSend, 6, btCRCLo, btCRCHi)btSend(6) = btCRCHibtSend(7) = btCRCLoComK3.Output = CV ar(btSend)End Sub④“读取IB0”按钮单击事件'查表知I0.0的modbus地址为0000,从I0.0开始读取连续8位Private Sub Command1_Click()'发请求Dim btSend(8) As BytebtSend(0) = &H1 '目标站号btSend(1) = &H2 '功能码btSend(2) = &H0 'I0.0地址(0000)高字节btSend(3) = &H0 'i0.0地址(0000)低字节btSend(4) = &H0 '读取个数高字节btSend(5) = &H8 '读取个数低字节Dim crcDim btCRCHi As Byte, btCRCLo As Bytecrc = CalCRC16Fast(btSend, 6, btCRCLo, btCRCHi)btSend(6) = btCRCHibtSend(7) = btCRCLoComK3.Output = CV ar(btSend)'注意下面编写的接收过程很简单,要编写实际应用的监控程序来说需要更完善Dim btReceive As V ariantWith ComK3DoDoEventsLoop Until .InBufferCount = 6.InputLen = 6btReceive = .InputIf btReceive(1) = &H2 Then '若正确,返回帧的第2个字节为功能码.实际上,此处应首先进行CRC校验txtReceive.Text = Hex$(btReceive(3))End IfEnd WithEnd Sub。
Modbus 通讯协议编程协议名称:Modbus 通讯协议编程一、引言Modbus 通讯协议是一种常用的工业领域通信协议,用于在不同设备之间进行数据传输和通信。
本协议旨在规范 Modbus 通讯协议的编程实现,确保各设备之间的数据交换和通信的可靠性和一致性。
二、协议概述1. Modbus 协议是一种基于主从架构的通信协议,其中主设备负责发起通信请求,从设备则负责响应请求并提供数据。
2. Modbus 协议支持多种物理层和传输层,包括串行通信(如 RS-232、RS-485)和以太网通信(如 TCP/IP)等。
3. Modbus 协议定义了一系列功能码,用于实现不同的数据读写操作,如读取寄存器、写入寄存器等。
三、通信流程1. 主设备向从设备发送请求,请求包括从设备地址、功能码、数据等信息。
2. 从设备接收到请求后,根据功能码执行相应的操作,并准备响应数据。
3. 从设备将响应数据打包成响应包,并发送给主设备。
4. 主设备接收到响应包后,解析响应数据并进行相应处理。
四、协议细节1. 设备地址:Modbus 协议中,每个设备都有一个唯一的地址,用于区分不同的设备。
设备地址范围为 1-247,其中地址 0 保留用于广播通信。
2. 功能码:Modbus 协议定义了一系列功能码,用于实现不同的读写操作。
常见的功能码包括读取保持寄存器(功能码03)、写入单个保持寄存器(功能码06)等。
3. 数据格式:Modbus 协议使用大端字节序(Big-Endian)来表示数据,即高位字节在前,低位字节在后。
4. 异常处理:在通信过程中,如果出现错误或异常情况,从设备可以返回一个异常响应,其中包括异常码和异常描述。
五、编程实现1. 主设备编程实现:a. 建立与从设备的通信连接。
b. 构建请求包,包括从设备地址、功能码、数据等信息。
c. 发送请求包给从设备。
d. 接收从设备返回的响应包。
e. 解析响应包,获取响应数据。
f. 根据需要进行数据处理和业务逻辑实现。
MODBUS通讯协议及编程一、引言MODBUS通讯协议是一种常用于工业自动化领域的通讯协议,它基于主从架构,用于实现设备之间的数据交换。
本协议旨在详细描述MODBUS通讯协议的规范和编程实现方法。
二、协议概述1. 协议介绍MODBUS通讯协议是一种串行通讯协议,使用简单、灵活且可靠。
它定义了一组功能码,用于实现数据读写、设备控制等操作。
MODBUS协议支持多种物理层传输介质,包括串口、以太网等。
2. 协议结构MODBUS通讯协议基于请求-响应机制,请求由主站发送给从站,从站接收请求并返回响应。
协议包括以下几个重要部分:- 地址标识:用于区分不同的从站设备。
- 功能码:定义了不同的操作类型,如读取寄存器、写入寄存器等。
- 数据域:包含了具体的数据信息,如寄存器地址、数据值等。
- 校验码:用于校验数据的完整性。
三、功能码详解1. 读取线圈状态(功能码01)该功能码用于读取从站设备的线圈状态,返回线圈的开关状态。
2. 读取输入状态(功能码02)该功能码用于读取从站设备的输入状态,返回输入信号的状态。
3. 读取保持寄存器(功能码03)该功能码用于读取从站设备的保持寄存器的值。
4. 读取输入寄存器(功能码04)该功能码用于读取从站设备的输入寄存器的值。
5. 强制单个线圈(功能码05)该功能码用于强制从站设备的某个线圈状态。
6. 写入单个保持寄存器(功能码06)该功能码用于写入从站设备的某个保持寄存器的值。
7. 写入多个线圈(功能码15)该功能码用于同时写入从站设备的多个线圈的状态。
8. 写入多个保持寄存器(功能码16)该功能码用于同时写入从站设备的多个保持寄存器的值。
四、编程实现1. 主站编程主站负责发送请求并接收响应。
编程实现主要包括以下步骤:- 建立通讯连接:通过串口或以太网等方式与从站建立通讯连接。
- 构建请求帧:根据需要构建相应的请求帧,包括地址标识、功能码、数据域等。
- 发送请求帧:将请求帧发送给从站设备。
MODBUS通讯协议及编程协议名称:MODBUS通讯协议及编程一、引言MODBUS通讯协议是一种常用的工业领域通信协议,用于在不同设备之间进行数据交换。
本协议旨在详细描述MODBUS通讯协议的标准格式和编程要求,以确保设备之间的通信正常稳定。
二、协议格式1. MODBUS通讯协议采用主从结构,主设备向从设备发送请求,从设备进行响应。
2. 请求帧格式:- 起始符:1字节,值为0xFF。
- 设备地址:1字节,范围为0-255,用于标识从设备。
- 功能码:1字节,用于指定请求的操作类型。
- 数据域:可变长度,包含具体的请求数据。
- CRC校验:2字节,用于检测数据传输的准确性。
3. 响应帧格式:- 起始符:1字节,值为0xFF。
- 设备地址:1字节,范围为0-255,用于标识从设备。
- 功能码:1字节,用于指定响应的操作类型。
- 数据域:可变长度,包含具体的响应数据。
- CRC校验:2字节,用于检测数据传输的准确性。
三、功能码1. 读取线圈状态(功能码:0x01):- 请求数据域:起始地址(2字节)、数量(2字节)。
- 响应数据域:线圈状态(可变长度)。
2. 读取输入状态(功能码:0x02):- 请求数据域:起始地址(2字节)、数量(2字节)。
- 响应数据域:输入状态(可变长度)。
3. 读取保持寄存器(功能码:0x03):- 请求数据域:起始地址(2字节)、数量(2字节)。
- 响应数据域:寄存器值(可变长度)。
4. 读取输入寄存器(功能码:0x04):- 请求数据域:起始地址(2字节)、数量(2字节)。
- 响应数据域:寄存器值(可变长度)。
5. 写单个线圈(功能码:0x05):- 请求数据域:线圈地址(2字节)、线圈状态(2字节)。
- 响应数据域:无。
6. 写单个保持寄存器(功能码:0x06):- 请求数据域:寄存器地址(2字节)、寄存器值(2字节)。
- 响应数据域:无。
7. 写多个线圈(功能码:0x0F):- 请求数据域:起始地址(2字节)、数量(2字节)、字节数(1字节)、线圈状态(可变长度)。
modbus vb源程序可以读写寄存器自己的项目做完了,最近比较闲,就帮别人用VB写了一个很简单的modbus程序,可以实现实时数据采集显示,以及能对寄存器进行设置。
程序很简单,想用的可以完善,现在只能实时采集显示一个地址的数据,只要修改一下,就可以实时采集多个地址的数据。
现在也只能一次对一个寄存器进行设置,也可以更加完善。
想用的朋友就自己改改吧。
下面是运行界面,采集的模块的地址为75,是一个温湿度采集模块。
有3个寄存器,显示的数据上是温度,湿度,露点温度。
modbusPrivate Sub Command1_Click() '设置按钮Dim bisend() As ByteDim crcDim btLoCRC As Byte, btHiCRC As ByteDim Data As IntegerIf MSComm1.PortOpen = True ThenIf Combo5.ListIndex = 0 ThenReDim bisend(7) '重新定义数组长度bisend(0) = "&h" + Hex(Val(Text1.Text)) '地址码bisend(1) = "&h" + Hex(3) '功能码读寄存器bisend(2) = "&h" + Hex(0) '起始地址高位bisend(3) = "&h" + Hex(0) '起始地址低位bisend(4) = "&h" + Hex(0) '寄存器个数高位bisend(5) = "&h" + Hex(Combo6.ListIndex + 1) '寄存器个数低位crc = CRC16(bisend, 6, btLoCRC, btHiCRC)bisend(6) = "&h" + Hex(btLoCRC) 'CRC高位bisend(7) = "&h" + Hex(btHiCRC) 'CRC低位'发送数据MSComm1.Output = bisendElseReDim bisend(10) '一次只能写一个寄存器bisend(0) = "&h" + Hex(Val(Text1.Text)) '地址码bisend(1) = "&h" + Hex(16) '功能码写寄存器bisend(2) = "&h" + Hex(0) '起始地址高位bisend(3) = "&h" + Hex(0) '起始地址低位bisend(4) = "&h" + Hex(0) '寄存器个数高位bisend(5) = "&h" + Hex(1) '寄存器个数低位bisend(6) = "&h" + Hex(2) '字节数Data = Val(Trim(Text3.Text))bisend(7) = "&h" + Hex(Data \ 256) '要写入寄存器的值的高字节bisend(8) = "&h" + Hex(Data Mod 256) '要写入寄存器的值的低字节crc = CRC16(bisend, 9, btLoCRC, btHiCRC)bisend(9) = "&h" + Hex(btLoCRC) 'CRC高位bisend(10) = "&h" + Hex(btHiCRC) 'CRC低位MSComm1.Output = bisendEnd IfElseMsgBox "串口没有打开"End IfEnd SubPrivate Sub Command2_Click() '实时采集按钮Timer1.Enabled = Not Timer1.Enabled '进行状态切换End SubPrivate Sub Command3_Click()'初始化,并打开串口With MSComm1If .PortOpen = False Then.CommPort = Combo7.ListIndex + 1 '打开串口1.Settings = Combo1.Text + "," + Combo2.Text + "," + Combo3.Text + Combo4.Text.InputMode = 1.InputLen = 50 '一次性从接收缓冲区中读取所有数据(8个字节为一组!!).InBufferCount = 0 '清空接收缓冲区.OutBufferCount = 0 '清空发送缓冲区.RThreshold = 5 + (Combo6.ListIndex + 1) * 2.InBufferSize = 1024.OutBufferSize = 1024.PortOpen = TrueElseMsgBox "串口已经打开"End IfEnd WithEnd SubPrivate Sub Command4_Click() '关闭串口按钮If MSComm1.PortOpen = True Then MSComm1.PortOpen = False End IfEnd SubPrivate Sub Form_Load()Dim i As Integer'波特率设置Combo1.AddItem "4800", 0 Combo1.AddItem "9600", 1 Combo1.AddItem "115200", 2'校验位设置Combo2.AddItem "N", 0Combo2.AddItem "E", 1Combo2.AddItem "O", 2'数据位设置Combo3.AddItem "7", 0Combo3.AddItem "8", 1'停止位设置Combo4.AddItem "1", 0Combo4.AddItem "2", 1'功能码选择Combo5.AddItem "读寄存器03", 0 Combo5.AddItem "写寄存器16", 1 '寄存器个数设置Combo6.AddItem "1", 0Combo6.AddItem "2", 1Combo6.AddItem "3", 2Combo6.AddItem "4", 3Combo6.AddItem "5", 4 Combo6.AddItem "6", 5Combo6.AddItem "7", 6Combo6.AddItem "8", 7Combo6.AddItem "9", 8Combo6.AddItem "10", 9 Combo6.AddItem "11", 10 Combo6.AddItem "12", 11 Combo6.AddItem "13", 12 Combo6.AddItem "14", 13 Combo6.AddItem "15", 14 Combo6.AddItem "16", 15 Combo6.AddItem "17", 16 Combo6.AddItem "18", 17 Combo6.AddItem "19", 18Combo6.AddItem "20", 19Combo6.AddItem "21", 20Combo6.AddItem "22", 21'串口选择Combo7.AddItem "串口1", 0Combo7.AddItem "串口2", 1Combo7.AddItem "串口3", 2Combo7.AddItem "串口4", 3'初始赋值Combo1.ListIndex = 1Combo2.ListIndex = 1Combo3.ListIndex = 1Combo4.ListIndex = 0Combo5.ListIndex = 0Combo6.ListIndex = 2Combo7.ListIndex = 0'初始化串口End SubPrivate Sub Form_Unload(Cancel As Integer)If MSComm1.PortOpen = True ThenMSComm1.PortOpen = FalseEnd IfEnd SubPrivate Sub MSComm1_OnComm()Dim INByte() As ByteDim Buf As StringDim btLoCRC As Byte, btHiCRC As ByteDim Data As IntegerIf mEvent = comEvReceive Then '接收到数据以后INByte = MSComm1.InputIf INByte(1) = 3 Then '读寄存器'CRC校验crc = CRC16(INByte, UBound(INByte) - LBound(INByte) - 1, btLoCRC, btHiCRC)If INByte(UBound(INByte) - 1) = btLoCRC And INByte(UBound(INByte)) = btHiCRC Then '校验正确'////////////////////////////////////For i = 3 To UBound(INByte) - 2 Step 2Data = "&h" + Hex(INByte(i)) + Hex(INByte(i + 1))' Buf = Buf + Hex(INByte(i)) + Chr(32)Buf = Buf + Str(Data) '转换为十进制显示Next iList1.AddItem BufEnd IfEnd IfMSComm1.InBufferCount = 0 '请缓存End IfEnd SubPrivate Sub Timer1_Timer()'定时发送命令Dim tbisend(7) As ByteDim crc '定时1sDim btLoCRC As Byte, btHiCRC As ByteDim Buf As StringIf MSComm1.PortOpen = True Thentbisend(0) = "&h" + Hex(Val(Text1.Text)) '地址码tbisend(1) = "&h" + Hex(3) '功能码读寄存器tbisend(2) = "&h" + Hex(0) '起始地址高位tbisend(3) = "&h" + Hex(0) '起始地址低位tbisend(4) = "&h" + Hex(0) '寄存器个数高位tbisend(5) = "&h" + Hex(Combo6.ListIndex + 1) '寄存器个数低位crc = CRC16(tbisend, 6, btLoCRC, btHiCRC)tbisend(6) = "&h" + Hex(btLoCRC) 'CRC高位tbisend(7) = "&h" + Hex(btHiCRC) 'CRC低位'发送数据MSComm1.Output = tbisendEnd IfEnd Sub////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////Function CRC16(Data() As Byte, no As Integer, CRC16Lo As Byte, CRC16Hi As Byte) As StringDim CL As Byte, CH As Byte '多项式码&HA001Dim SaveHi As Byte, SaveLo As ByteDim i As IntegerDim Flag As IntegerCRC16Lo = &HFFCRC16Hi = &HFFCL = &H1CH = &HA0For i = 0 To no - 1CRC16Lo = CRC16Lo Xor Data(i) '每一个数据与CRC寄存器进行异或For Flag = 0 To 7SaveHi = CRC16HiSaveLo = CRC16LoCRC16Hi = CRC16Hi \ 2 '高位右移一位CRC16Lo = CRC16Lo \ 2 '低位右移一位If ((SaveHi And &H1) = &H1) Then '如果高位字节最后一位为1CRC16Lo = CRC16Lo Or &H80 '则低位字节右移后前面补1End If '否则自动补0If ((SaveLo And &H1) = &H1) Then '如果LSB为1,则与多项式码进行异或CRC16Hi = CRC16Hi Xor CHCRC16Lo = CRC16Lo Xor CLEnd IfNext FlagNext iDim ReturnData(1) As ByteReturnData(0) = CRC16Hi 'CRC高位ReturnData(1) = CRC16Lo 'CRC低位CRC16 = ReturnDataEnd Function'CRC低位字节值表Function GetCRCLo(ind As Long) As ByteGetCRCLo = Choose(ind + 1, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _&H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, _&H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, _&H0, &HC1, &H81, &H40, &H1, &HC1, &H80, &H41, &H0, &HC1, _&H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, &H80, &H41, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H0, &HC1, _&H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H81, &H41, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, _&H1, &HC0, &H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, _&H81, &H40, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81,&H40, _&H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, _&H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, _&H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, _&H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, _&H1, &HC0, &H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, _&H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, _&H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _&H80, &H41, &H0, &HC1, &H81, &H40)End Function'CRC高位字节值表Function GetCRCHi(ind As Long) As ByteGetCRCHi = Choose(ind + 1, &H0, &HC0, &HC1, &H1, &HC3, &H3, &H2, &HC2, &HC6, &H6, &H7, &HC7, &H5, &HC5, &HC4, &H4, &HCC, &HC, &HD, &HCD, _&HF, &HCF, &HCE, &HE, &HA, &HCA, &HCB, &HB, &HC9, &H9, _&H8, &HC8, &HD8, &H18, &H19, &HD9, &H1B, &HDB, &HDA, &H1A, _&H1E, &HDE, &HDF, &H1F, &HDD, &H1D, &H1C, &HDC, &H14, &HD4, _&HD5, &H15, &HD7, &H17, &H16, &HD6, &HD2, &H12, &H13, &HD3, _&H11, &HD1, &HD0, &H10, &HF0, &H30, &H31, &HF1, &O33, &HF3, _&HF2, &H32, &H36, &HF6, &HF7, &H37, &HF5, &H35, &H34, &HF4, _&H3C, &HFC, &HFD, &H3D, &HFF, &H3F, &H3E, &HFE, &HFA, &H3A, _&H3B, &HFB, &H39, &HF9, &HF8, &H38, &H28, &HE8, &HE9, &H29, _&HEB, &H2B, &H2A, &HEA, &HEE, &H2E, &H2F, &HEF, &H2D, &HED, _&HEC, &H2C, &HE4, &H24, &H25, &HE5, &H27, &HE7, &HE6, &H26, _&H22, &HE2, &HE3, &H23, &HE1, &H21, &H20, &HE0, &HA0, &H60, _&H61, &HA1, &H63, &HA3, &HA2, &H62, &H66, &HA6, &HA7, &H67, _&HA5, &H65, &H64, &HA4, &H6C, &HAC, &HAD, &H6D, &HAF, &H6F, _&H6E, &HAE, &HAA, &H6A, &H6B, &HAB, &H69, &HA9, &HA8, &H68, _&H78, &HB8, &HB9, &H79, &HBB, &H7B, &H7A, &HBA, &HBE, &H7E, _&H7F, &HBF, &H7D, &HBD, &HBC, &H7C, &HB4, &H74, &H75, &HB5, _&H77, &HB7, &HB6, &H76, &H72, &HB2, &HB3, &H73, &HB1, &H71, _&H70, &HB0, &H50, &H90, &H91, &H51, &H93, &H53, &H52, &H92, _&H96, &H56, &H57, &H97, &H55, &H95, &H94, &H54, &H9C, &H5C, _&H5D, &H9D, &H5F, &H9F, &H9E, &H5E, &H5A, &H9A, &H9B, &H5B, _&H99, &H59, &H58, &H98, &H88, &H48, &H49, &H89, &H4B, &H8B, _&H8A, &H4A, &H4E, &H8E, &H8F, &H4F, &H8D, &H4D, &H4C, &H8C, _&H44, &H84, &H85, &H45, &H87, &H47, &H46, &H86, &H82, &H42, _&H43, &H83, &H41, &H81, &H80, &H40)End Function。
MODBUS通讯协议及编程一、协议概述MODBUS通讯协议是一种用于工业自动化领域的通讯协议,广泛应用于各种设备之间的数据传输。
本协议旨在规范MODBUS通讯协议的使用和编程方法,确保数据的可靠传输和设备的互操作性。
二、协议基本原理1. MODBUS通讯协议采用主从结构,其中主机为数据请求方,从机为数据响应方。
2. 主机通过发送请求帧来获取从机的数据,从机接收请求帧后进行响应。
3. 请求帧包含功能码、数据地址、数据长度等信息,从机根据请求帧的内容进行数据处理并返回响应帧。
4. 响应帧包含功能码、数据长度、数据内容等信息,主机接收响应帧后进行数据解析。
三、协议格式MODBUS通讯协议的数据帧格式如下:1. 请求帧格式:- 起始符:1个字节,固定为0xFF。
- 从机地址:1个字节,用于标识从机。
- 功能码:1个字节,用于标识请求的功能。
- 数据地址:2个字节,用于指定请求的数据地址。
- 数据长度:2个字节,用于指定请求的数据长度。
- CRC校验:2个字节,用于校验数据的完整性。
2. 响应帧格式:- 起始符:1个字节,固定为0xFF。
- 从机地址:1个字节,用于标识从机。
- 功能码:1个字节,用于标识响应的功能。
- 数据长度:1个字节,用于指定响应的数据长度。
- 数据内容:根据功能码和数据长度确定。
- CRC校验:2个字节,用于校验数据的完整性。
四、协议功能码MODBUS通讯协议定义了一系列功能码,用于标识不同的数据操作和功能需求。
常见的功能码包括:1. 读取线圈状态(0x01):用于读取从机的线圈状态。
2. 读取输入状态(0x02):用于读取从机的输入状态。
3. 读取保持寄存器(0x03):用于读取从机的保持寄存器。
4. 读取输入寄存器(0x04):用于读取从机的输入寄存器。
5. 写单个线圈(0x05):用于设置从机的单个线圈状态。
6. 写单个寄存器(0x06):用于设置从机的单个寄存器值。
7. 写多个线圈(0x0F):用于设置从机的多个线圈状态。
Modbus-vb源程序(可以读写寄存器)modbus vb源程序可以读写寄存器自己的项目做完了,最近比较闲,就帮别人用VB写了一个很简单的modbus程序,可以实现实时数据采集显示,以及能对寄存器进行设置。
程序很简单,想用的可以完善,现在只能实时采集显示一个地址的数据,只要修改一下,就可以实时采集多个地址的数据。
现在也只能一次对一个寄存器进行设置,也可以更加完善。
想用的朋友就自己改改吧。
下面是运行界面,采集的模块的地址为75,是一个温湿度采集模块。
有3个寄存器,显示的数据上是温度,湿度,露点温度。
modbusPrivate Sub Command1_Click() '设置按钮Dim bisend() As ByteDim crcDim btLoCRC As Byte, btHiCRC As Byte Dim Data As IntegerIf MSComm1.PortOpen = True ThenIf Combo5.ListIndex = 0 ThenReDim bisend(7) '重新定义数组长度 bisend(0) = "&h" + Hex(Val(Text1.Text)) '地址码bisend(1) = "&h" + Hex(3) '功能码读寄存器bisend(2) = "&h" + Hex(0) '起始地址高位bisend(3) = "&h" + Hex(0) '起始地址低位bisend(4) = "&h" + Hex(0) '寄存器个数高位bisend(5) = "&h" + Hex(Combo6.ListIndex + 1) '寄存器个数低位 crc = CRC16(bisend, 6, btLoCRC, btHiCRC)bisend(6) = "&h" + Hex(btLoCRC)'CRC高位bisend(7) = "&h" + Hex(btHiCRC) 'CRC低位'发送数据MSComm1.Output = bisendElseReDim bisend(10) '一次只能写一个寄存器bisend(0) = "&h" + Hex(Val(Text1.Text)) '地址码bisend(1) = "&h" + Hex(16) '功能码写寄存器bisend(2) = "&h" + Hex(0) '起始地址高位bisend(3) = "&h" + Hex(0) '起始地址低位bisend(4) = "&h" + Hex(0) '寄存器个数高位bisend(5) = "&h" + Hex(1) '寄存器个数低位bisend(6) = "&h" + Hex(2) '字节数Data = Val(Trim(Text3.Text))bisend(7) = "&h" + Hex(Data \ 256) '要写入寄存器的值的高字节bisend(8) = "&h" + Hex(Data Mod 256) '要写入寄存器的值的低字节crc = CRC16(bisend, 9, btLoCRC, btHiCRC)bisend(9) = "&h" + Hex(btLoCRC) 'CRC高位bisend(10) = "&h" + Hex(btHiCRC) 'CRC低位MSComm1.Output = bisendEnd IfElseMsgBox "串口没有打开"End IfEnd SubPrivate Sub Command2_Click() '实时采集按钮Timer1.Enabled = Not Timer1.Enabled '进行状态切换End SubPrivate Sub Command3_Click()'初始化,并打开串口With MSComm1If .PortOpen = False Then.CommPort = Combo7.ListIndex + 1 '打开串口1.Settings = Combo1.Text + "," +Combo2.Text + "," + Combo3.Text +Combo4.Text.InputMode = 1.InputLen = 50 '一次性从接收缓冲区中读取所有数据(8个字节为一组!!).InBufferCount = 0 '清空接收缓冲区.OutBufferCount = 0 '清空发送缓冲区.RThreshold = 5 + (Combo6.ListIndex +1) * 2.InBufferSize = 1024.OutBufferSize = 1024.PortOpen = TrueElseMsgBox "串口已经打开"End IfEnd WithEnd SubPrivate Sub Command4_Click() '关闭串口按钮If MSComm1.PortOpen = True ThenMSComm1.PortOpen = FalseEnd IfEnd SubPrivate Sub Form_Load()Dim i As Integer'波特率设置Combo1.AddItem "4800", 0Combo1.AddItem "9600", 1Combo1.AddItem "115200", 2'校验位设置Combo2.AddItem "N", 0Combo2.AddItem "E", 1Combo2.AddItem "O", 2'数据位设置Combo3.AddItem "7", 0Combo3.AddItem "8", 1'停止位设置Combo4.AddItem "1", 0Combo4.AddItem "2", 1'功能码选择Combo5.AddItem "读寄存器03", 0 Combo5.AddItem "写寄存器16", 1 '寄存器个数设置Combo6.AddItem "1", 0Combo6.AddItem "2", 1Combo6.AddItem "3", 2Combo6.AddItem "4", 3Combo6.AddItem "5", 4Combo6.AddItem "6", 5Combo6.AddItem "7", 6Combo6.AddItem "8", 7Combo6.AddItem "9", 8Combo6.AddItem "10", 9Combo6.AddItem "11", 10Combo6.AddItem "12", 11Combo6.AddItem "13", 12Combo6.AddItem "14", 13Combo6.AddItem "15", 14 Combo6.AddItem "16", 15Combo6.AddItem "17", 16Combo6.AddItem "18", 17Combo6.AddItem "19", 18Combo6.AddItem "20", 19Combo6.AddItem "21", 20 Combo6.AddItem "22", 21'串口选择Combo7.AddItem "串口1", 0 Combo7.AddItem "串口2", 1 Combo7.AddItem "串口3", 2 Combo7.AddItem "串口4", 3'初始赋值Combo1.ListIndex = 1 Combo2.ListIndex = 1 Combo3.ListIndex = 1 Combo4.ListIndex = 0 Combo5.ListIndex = 0Combo6.ListIndex = 2Combo7.ListIndex = 0'初始化串口End SubPrivate Sub Form_Unload(Cancel As Integer)If MSComm1.PortOpen = True ThenMSComm1.PortOpen = FalseEnd IfEnd SubPrivate Sub MSComm1_OnComm()Dim INByte() As ByteDim Buf As StringDim btLoCRC As Byte, btHiCRC As ByteDim Data As IntegerIf mEvent = comEvReceive Then '接收到数据以后INByte = MSComm1.InputIf INByte(1) = 3 Then '读寄存器'CRC校验crc = CRC16(INByte, UBound(INByte) - LBound(INByte) - 1, btLoCRC, btHiCRC)If INByte(UBound(INByte) - 1) = btLoCRC And INByte(UBound(INByte)) = btHiCRC Then'校验正确'////////////////////////////////////For i = 3 To UBound(INByte) - 2 Step 2Data = "&h" + Hex(INByte(i)) + Hex(INByte(i + 1))' Buf = Buf + Hex(INByte(i)) + Chr(32) Buf = Buf + Str(Data) '转换为十进制显示Next iList1.AddItem BufEnd IfEnd IfMSComm1.InBufferCount = 0 '请缓存End IfEnd SubPrivate Sub Timer1_Timer()'定时发送命令Dim tbisend(7) As ByteDim crc '定时1sDim btLoCRC As Byte, btHiCRC As ByteDim Buf As StringIf MSComm1.PortOpen = True Thentbisend(0) = "&h" + Hex(Val(Text1.Text)) '地址码tbisend(1) = "&h" + Hex(3) '功能码读寄存器tbisend(2) = "&h" + Hex(0) '起始地址高位tbisend(3) = "&h" + Hex(0) '起始地址低位tbisend(4) = "&h" + Hex(0) '寄存器个数高位tbisend(5) = "&h" + Hex(Combo6.ListIndex + 1) '寄存器个数低位 crc = CRC16(tbisend, 6, btLoCRC, btHiCRC)tbisend(6) = "&h" + Hex(btLoCRC)'CRC高位tbisend(7) = "&h" + Hex(btHiCRC)'CRC低位'发送数据MSComm1.Output = tbisendEnd IfEnd Sub/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////Function CRC16(Data() As Byte, no As Integer,CRC16Lo As Byte, CRC16Hi As Byte) As StringDim CL As Byte, CH As Byte '多项式码&HA001Dim SaveHi As Byte, SaveLo As ByteDim i As IntegerDim Flag As IntegerCRC16Lo = &HFFCRC16Hi = &HFFCL = &H1CH = &HA0For i = 0 To no - 1CRC16Lo = CRC16Lo Xor Data(i) '每一个数据与CRC寄存器进行异或For Flag = 0 To 7SaveHi = CRC16HiSaveLo = CRC16LoCRC16Hi = CRC16Hi \ 2 '高位右移一位CRC16Lo = CRC16Lo \ 2 '低位右移一位If ((SaveHi And &H1) = &H1) Then'如果高位字节最后一位为1CRC16Lo = CRC16Lo Or &H80'则低位字节右移后前面补1End If '否则自动补0If ((SaveLo And &H1) = &H1) Then'如果LSB为1,则与多项式码进行异或CRC16Hi = CRC16Hi Xor CHCRC16Lo = CRC16Lo Xor CLEnd IfNext FlagNext iDim ReturnData(1) As ByteReturnData(0) = CRC16Hi 'CRC高位ReturnData(1) = CRC16Lo 'CRC低位CRC16 = ReturnDataEnd Function'CRC低位字节值表Function GetCRCLo(ind As Long) As ByteGetCRCLo = Choose(ind + 1, &H0, &HC1, &H81,&H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _&H0, &HC1,&H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0,_&H80, &H41, &H1,&HC0, &H80, &H41, &H0, &HC1, &H81, &H40, _&H0, &HC1, &H81,&H40, &H1, &HC1, &H80, &H41, &H0, &HC1, _&H81, &H40, &H1,&HC0, &H80, &H41, &H1, &HC0, &H80, &H41, _ &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H0, &HC1, _ &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H81, &H41, _ &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _ &H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, _ &H1, &HC0, &H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, _ &H81, &H40, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, _ &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _ &H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, _ &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, _ &H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, _ &H0, &HC1, &H81,&H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _ &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, _ &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, _ &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, _ &H1, &HC0, &H80, &H41, &H0, &HC1, &H81, &H40, &H1, &HC0, _ &H80, &H41, &H0, &HC1, &H81, &H40, &H0, &HC1, &H81, &H40, _ &H1, &HC0, &H80, &H41, &H1, &HC0, &H80, &H41, &H0, &HC1, _ &H81, &H40, &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, _ &H0, &HC1, &H81, &H40, &H1, &HC0, &H80, &H41, &H1, &HC0, _ &H80, &H41, &H0, &HC1, &H81, &H40)End Function'CRC高位字节值表Function GetCRCHi(ind As Long) As ByteGetCRCHi = Choose(ind + 1, &H0, &HC0, &HC1, &H1, &HC3, &H3, &H2, &HC2, &HC6, &H6, &H7, &HC7, &H5, &HC5, &HC4, &H4, &HCC, &HC, &HD, &HCD, _&HF, &HCF, &HCE, &HE, &HA, &HCA, &HCB, &HB, &HC9, &H9, _&H8, &HC8, &HD8, &H18, &H19, &HD9, &H1B, &HDB, &HDA, &H1A, _ &H1E, &HDE, &HDF, &H1F, &HDD, &H1D, &H1C, &HDC, &H14, &HD4, _&HD5, &H15, &HD7, &H17, &H16, &HD6, &HD2, &H12, &H13, &HD3, _&H11, &HD1, &HD0, &H10, &HF0, &H30, &H31, &HF1, &O33, &HF3, _&HF2, &H32, &H36, &HF6, &HF7, &H37, &HF5, &H35, &H34, &HF4, _&H3C, &HFC, &HFD, &H3D, &HFF, &H3F, &H3E, &HFE, &HFA,&H3A, _&H3B, &HFB, &H39, &HF9, &HF8, &H38, &H28, &HE8, &HE9, &H29, _&HEB, &H2B, &H2A, &HEA, &HEE, &H2E, &H2F, &HEF, &H2D, &HED, _&HEC, &H2C, &HE4, &H24, &H25, &HE5, &H27, &HE7, &HE6, &H26, _&H22, &HE2, &HE3, &H23, &HE1, &H21, &H20, &HE0, &HA0, &H60, _&H61, &HA1, &H63, &HA3, &HA2, &H62, &H66, &HA6, &HA7, &H67, _&HA5, &H65, &H64, &HA4, &H6C, &HAC, &HAD, &H6D, &HAF, &H6F, _&H6E, &HAE, &HAA, &H6A, &H6B, &HAB, &H69, &HA9, &HA8, &H68, _&H78, &HB8, &HB9, &H79, &HBB, &H7B, &H7A, &HBA, &HBE, &H7E, _&H7F, &HBF, &H7D, &HBD, &HBC, &H7C, &HB4, &H74, &H75, &HB5, _&H77, &HB7, &HB6, &H76, &H72, &HB2, &HB3, &H73, &HB1, &H71, _&H70, &HB0, &H50, &H90, &H91, &H51, &H93, &H53, &H52, &H92, _&H96, &H56, &H57, &H97, &H55, &H95, &H94, &H54, &H9C, &H5C, _&H5D, &H9D, &H5F, &H9F, &H9E, &H5E, &H5A, &H9A, &H9B, &H5B, _&H99, &H59, &H58, &H98, &H88, &H48, &H49, &H89, &H4B, &H8B, _&H8A, &H4A,&H4E, &H8E, &H8F, &H4F, &H8D, &H4D, &H4C, &H8C, _&H44, &H84, &H85, &H45, &H87, &H47, &H46, &H86, &H82, &H42, _&H43, &H83, &H41, &H81, &H80, &H40)End Function。
最近,本人为了实现电脑与Delta V FD-M变频器通讯,特意用VB6.0编了一个Modbus协议通讯软件。
这只是一个测试版,但Modbus的ASCII协议和RTU协议都已经实现。
现在将源程序上传,希望可以帮助到有需要的朋友,谢谢!另外,假如你觉得有更好的想法,欢迎指教。
如果对本程序有任何意见和建议,也可以一起讨论,共同进步。
大家多多支持俺啊。
附:VB6源程序Option ExplicitPrivate Text1text As StringPrivate RTUCRC As String'串口选择Private Sub Combo1_Click()mPort = Combo1.ListIndex + 1End Sub'数据位改变< span style="color: #008000;">Private Sub Combo2_Click()Call settingEnd Sub'波特率改变< span style="color: #008000;">Private Sub Combo3_Click()Call settingEnd Sub'奇偶校验改变< span style="color: #008000;">Private Sub Combo4_Click()Call settingEnd Sub'停止位改变< span style="color: #008000;">Private Sub Combo5_Click()Call settingPrivate Sub setting()MSComm1.Settings = CStr(Combo3.Text) & ","& CStr(Combo4.Text) & ","& CStr(C ombo2.Text) _& ","& CStr(Combo5.Text)End Sub'打开关闭串口< span style="color: #008000;">Private Sub Command1_Click()On Error Resume NextIf MSComm1.PortOpen = False ThenMSComm1.PortOpen = TrueElseMSComm1.PortOpen = FalseEnd IfIf MSComm1.PortOpen Then'打开关闭按钮显示文字及combo1使能Command1.Caption = "关闭串口"Combo1.Enabled = FalseElseCommand1.Caption = "打开串口"Combo1.Enabled = TrueEnd IfIf Err Then'打开串口失败,则显示出错信息MsgBox Error$, 48, "错误信息"Exit SubEnd IfEnd Sub'10转16进制< span style="color: #008000;">Private Sub Command2_Click(Index As Integer)On Error Resume NextText4.Text = Hex(Text3.Text)If Err Then''则显示出错信息< span style="color: #008000;">MsgBox Error$, 48, "错误信息"Exit SubEnd If'16转10进制< span style="color: #008000;">Private Sub Command3_Click()Dim a As Longa = Val("&H"& CStr(Text4.Text))Text3.Text = aEnd Sub'手动串口发送< span style="color: #008000;">Private Sub Command4_Click()If MSComm1.PortOpen = False ThenMsgBox"请先打开串口< span style="color: #800000;">", , "错误信息" Exit SubEnd IfCall sentsubEnd Sub'清除接收窗< span style="color: #008000;">Private Sub Command5_Click()Text2.Text = ""End SubPrivate Sub Command6_Click()Unload MeEnd SubPrivate Sub Command7_Click()On Error Resume NextDim STP As StringSTP = CStr(Chr(2)) & "010001"& CStr(Chr(3)) & "25"MSComm1.Settings = "9600,N,7,2"MSComm1.PortOpen = TrueMSComm1.Output = STPMSComm1.PortOpen = FalseIf Err Then'打开串口失败,则显示出错信息MsgBox Error$, 48, "错误信息"Exit SubEnd IfEnd SubPrivate Sub Command8_Click()On Error Resume NextDim FWD As StringFWD = CStr(Chr(2)) & "010101"& CStr(Chr(3)) & "26" MSComm1.Settings = "9600,N,7,2"MSComm1.PortOpen = TrueMSComm1.Output = FWDMSComm1.PortOpen = FalseIf Err Then'打开串口失败,则显示出错信息MsgBox Error$, 48, "错误信息"Exit SubEnd IfEnd SubPrivate Sub Command9_Click()On Error Resume NextDim REV As StringREV = CStr(Chr(2)) & "010201"& CStr(Chr(3)) & "27" MSComm1.Settings = "9600,N,7,2"MSComm1.PortOpen = TrueMSComm1.Output = REVMSComm1.PortOpen = FalseIf Err Then'打开串口失败,则显示出错信息MsgBox Error$, 48, "错误信息"Exit SubEnd IfEnd Sub'窗口加载Private Sub Form_Load()Dim d%For d = 1To16Combo1.AddItem ("COM"& CStr(d))NextCombo1.ListIndex = 0Combo2.AddItem "6"Combo2.AddItem "7"Combo2.AddItem "8"Combo2.ListIndex = 2Combo3.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 = 5Combo4.AddItem "n" Combo4.AddItem "o" Combo4.AddItem "e" Combo4.ListIndex = 0Combo5.AddItem "1" Combo5.AddItem "2" Combo5.ListIndex = 0For d = 0To254Combo6.AddItem dNextCombo6.ListIndex = 1Text1.Text = "010*********" Text2.Text = ""Text3.Text = ""Text4.Text = ""Text5.Text = "1000"Text6.Text = "06"Text7.Text = "0"Text8.Text = "1"Option1.value = TrueOption3.value = TrueOption7.value = TrueOption9.value = TrueIf MSComm1.PortOpen = False ThenCommand1.Caption = "打开串口"ElseCommand1.Caption = "关闭串口"End IfEnd Sub'串口接收程序< span style="color: #008000;">Private Sub MSComm1_OnComm()Dim Hexchr As String, hexstring As String, i As Integer, j As Integer, hexdisp As Str ingIf Option8.value Thenhexstring = MSComm1.Input '十六进制显示< span style="color: #008000;">i = Len(hexstring)For j = 1To iHexchr = Mid(hexstring, j, 1)If Hex(Asc(Hexchr)) < 16ThenText2.Text = Text2.Text & "0"& Hex(Asc(Hexchr)) & " "ElseText2.Text = Text2.Text & Hex(Asc(Hexchr)) & " "End IfNext jText2.Text = Text2.Text & CStr(Chr(13)) & CStr(Chr(10))ElseText2.Text = Text2.Text & MSComm1.Input & CStr(Chr(13)) & CStr(Chr(10)) 'ASCII 码显示< span style="color: #008000;">End IfEnd Sub'手动发送选择< span style="color: #008000;">Private Sub Option1_Click()If Option1.value = True ThenTimer1.Enabled = FalseCommand4.Enabled = TrueElseTimer1.Enabled = TrueCommand4.Enabled = FalseEnd IfEnd Sub'Delta ASCII发送协议Private Sub Option10_Click()Combo6.Enabled = TrueText6.Enabled = TrueText7.Enabled = TrueText8.Enabled = TrueLabel10.Enabled = TrueLabel11.Enabled = TrueLabel12.Enabled = TrueLabel13.Enabled = TrueOption6.Enabled = FalseOption7.Enabled = FalseOption11.value = TrueCombo2.ListIndex = 1Combo5.ListIndex = 1Text1.Enabled = FalseLabel14.Enabled = FalseFrame7.Visible = TrueEnd Sub'自动发送选择< span style="color: #008000;"> Private Sub Option2_Click()If Option2.value = True ThenTimer1.Enabled = TrueCommand4.Enabled = FalseElseTimer1.Enabled = FalseCommand4.Enabled = TrueEnd IfEnd SubPrivate Sub Option3_Click() 'Non选项< span style="color: #008000;"> Combo6.Enabled = FalseText6.Enabled = FalseText7.Enabled = FalseText8.Enabled = FalseLabel10.Enabled = FalseLabel11.Enabled = FalseLabel12.Enabled = FalseLabel13.Enabled = FalseOption6.Enabled = TrueOption7.Enabled = TrueCombo2.ListIndex = 2Combo5.ListIndex = 0Text1.Enabled = TrueLabel14.Enabled = TrueFrame7.Visible = FalseEnd SubPrivate Sub Option4_Click() 'ASCII选项< span style="color: #008000;"> Combo6.Enabled = TrueText6.Enabled = TrueText7.Enabled = TrueText8.Enabled = TrueLabel10.Enabled = TrueLabel11.Enabled = TrueLabel12.Enabled = TrueLabel13.Enabled = TrueOption6.Enabled = FalseOption7.Enabled = FalseCombo2.ListIndex = 1Combo5.ListIndex = 1Text1.Enabled = FalseLabel14.Enabled = FalseFrame7.Visible = FalseEnd SubPrivate Sub Option5_Click() 'RTU选项< span style="color: #008000;"> Combo6.Enabled = TrueText6.Enabled = TrueText7.Enabled = TrueText8.Enabled = TrueLabel10.Enabled = TrueLabel11.Enabled = TrueLabel12.Enabled = TrueLabel13.Enabled = TrueOption6.Enabled = FalseOption7.Enabled = FalseCombo2.ListIndex = 2Combo5.ListIndex = 1Text1.Enabled = FalseLabel14.Enabled = FalseFrame7.Visible = FalseEnd Sub'发送时间间隔调整输入< span style="color: #008000;">Private Sub Text5_Change()Dim number As StringDim num As IntegerDim numcyc As Integernum = Len(Text5.Text)For numcyc = 1To numnumber = Mid(Text5.Text, numcyc, 1)Select Case InStr("0123456789", number)Case0MsgBox"输入时间间隔错误,请重新输入", , "错误信息"Exit SubEnd SelectNextTimer1.Interval = Text5.TextEnd Sub'自动发送定时器< span style="color: #008000;">Private Sub Timer1_Timer()If MSComm1.PortOpen ThenCall sentsubEnd IfEnd Sub'状态刷新定时器< span style="color: #008000;">Private Sub Timer2_Timer()StatusBar1.Panels(1).Text = "串口选择:< span style="color: #800000;">" & CStr(Comb o1.Text)StatusBar1.Panels(2).Text = "串口设置:< span style="color: #800000;">" & CStr(MSC omm1.Settings)StatusBar1.Panels(3).Text = "串口状态:< span style="color: #800000;">" & CStr(MSC omm1.PortOpen)End Sub'串口发送子程序Private Sub sentsub()Dim optioncase%If Option3.value Then optioncase = 1If Option4.value Then optioncase = 2If Option5.value Then optioncase = 3If Option10.value Then optioncase = 4Select Case optioncaseCase1If Option6.value ThenText1text = Text1.TextCall HexsentElseText1text = Text1.TextCall ASCIIsentEnd IfCase2Call incorporate '将输入的十进制从机地址、命令、资料地址和资料内容合并成字符串Call ASCIIcheckCall ASCIIsentCase3Call incorporate '将输入的十进制从机地址、命令、资料地址和资料内容合并成字符串Call RTUcheckCall HexsentCase4Call incorporate1 '将输入的十进制从机地址、命令、资料地址和资料内容合并成字符串Call deltaASCIICall ASCIIsentEnd SelectEnd Sub'十六进制发送< span style="color: #008000;">Private Sub Hexsent()Dim hexchrlen%, Hexchr As String, hexcyc%, hexmid As Byte, hexmiddle As String Dim hexchrgroup() As Byte, i As Integerhexchrlen = Len(Text1text)For hexcyc = 1To hexchrlen '检查Text1文本框内数值是否合适Hexchr = Mid(Text1text, hexcyc, 1)If InStr("0123456789ABCDEFabcdef", Hexchr) = 0ThenMsgBox"无效的数值,请重新输入< span style="color: #800000;">", , "错误信息" Exit SubEnd IfNextReDim hexchrgroup(1To hexchrlen \ 2) As ByteFor hexcyc = 1To hexchrlen Step2'将文本框内数值分成两个、两个i = i + 1Hexchr = Mid(Text1text, hexcyc, 2)hexmid = Val("&H"& CStr(Hexchr))hexchrgroup(i) = hexmid'MSComm1.Output = CStr(hexmid)NextMSComm1.Output = hexchrgroupEnd Sub'ASC码发送< span style="color: #008000;">Private Sub ASCIIsent()MSComm1.Output = Text1textEnd Sub'ASC校验,此段程序计算出LRC校验值,并加上字头和字尾Private Sub ASCIIcheck()Dim a%, b%, chrnum%, Lrcbyte As StringDim checksum%, char%, AscLrc%, Lrc%chrnum = Len(Text1text)For a = 1To chrnum Step2char= Val("&H"& CStr(Mid(Text1text, a, 2))) '两个两个的取字符< span style="color: #008000;">checksum = checksum + char'全部加起来< span style="color: #008000;">NextAscLrc = checksum Mod&H100 '取255的余数< span style="color: #008000;">Lrc = (&HFF - AscLrc) + 1'取二次补If Lrc < 16Then'此段程序是判断Hex(lrc)是否是一位数,Lrcbyte = "0"+ CStr(Hex(Lrc)) '如果是的话,前面加0;否则不加零ElseLrcbyte = CStr(Hex(Lrc))End IfText1text = CStr(Chr(58)) & CStr(Text1text) & Lrcbyte & CStr(Chr(13)) & CStr(Chr(1 0))End Sub'DeltaASCII校验,此段程序计算出LRC校验值,并加上字头和字尾Private Sub deltaASCII()Dim a%, b%, chrnum%, Lrcbyte As StringDim checksum%, char%, Lrc%chrnum = Len(Text1text)For a = 1To chrnumchar= Asc(Mid(Text1text, a, 1)) '两个两个的取字符< span style="color: #008000;"> checksum = checksum + char'全部加起来< span style="color: #008000;">NextLrc = (checksum + &H3) Mod&H100 '取255的余数< span style="color: #008000;"> If Lrc < 16Then'此段程序是判断Hex(lrc)是否是一位数,Lrcbyte = "0"+ CStr(Hex(Lrc)) '如果是的话,前面加0;否则不加零ElseLrcbyte = CStr(Hex(Lrc))End IfText1text = CStr(Chr(2)) & CStr(Text1text) & CStr(Chr(3)) & LrcbyteEnd Sub'RTU校验< span style="color: #008000;">Private Sub RTUcheck()Dim CRC() As ByteDim d(5) As ByteDim string1 As StringDim j As Integer, chrlength As Integer, temp As Stringstring1 = Text1textchrlength = Len(string1)For j = 0To chrlength / 2- 1temp = Mid(string1, j * 2+ 1, 2)d(j) = Val("&H"& temp)NextRTUCRC = CRC16(d) '调用CRC16计算函数, CRC(0)为高位, CRC(1)为低位Text1text = Text1text & RTUCRCEnd SubPrivate Sub incorporate() '将输入的十进制从机地址、命令、资料地址和资料内容合并成字符串Dim wholechar As String, wc%, wcyc%, wchar As StringDim SID As String, Cmd As String, InfoAdd As String, data As StringDim SIDnum%, Cmdnum%, InfoAddNum%, Datanum%On Error Resume Nextwholechar = CStr(Combo6.Text) & CStr(Text6.Text) & CStr(Text7.Text) & CStr(Text8.T ext)wc = Len(wholechar)For wcyc = 1To wcwchar = Mid(wholechar, wcyc, 1)If InStr("0123456789", wchar) = 0ThenMsgBox"输入错误,请重新输入< span style="color: #800000;">", , "错误提示"Exit SubEnd IfNextSIDnum = Len(CStr(Hex(Combo6.Text)))Select Case SIDnumExit SubCase1SID = "0"& CStr(Hex(Combo6.Text)) Case2SID = CStr(Hex(Combo6.Text))End SelectCmdnum = Len(CStr(Hex(Text6.Text))) Select Case CmdnumCase0Exit SubCase1Cmd = "0"& CStr(Hex(Text6.Text)) Case1Cmd = CStr(Hex(Text6.Text))End SelectInfoAddNum = Len(CStr(Hex(Text7.Text))) Select Case InfoAddNumCase0Exit SubCase1InfoAdd = "000"& CStr(Hex(Text7.Text)) Case2InfoAdd = "00"& CStr(Hex(Text7.Text)) Case3InfoAdd = "0"& CStr(Hex(Text7.Text)) Case4InfoAdd = CStr(Hex(Text7.Text))End SelectDatanum = Len(CStr(Hex(Text8.Text))) Select Case DatanumCase0Exit Subdata = "000"& CStr(Hex(Text8.Text))Case2data = "00"& CStr(Hex(Text8.Text))Case3data = "0"& CStr(Hex(Text8.Text))Case4data = CStr(Hex(Text8.Text))End SelectIf Err Then'显示出错信息< span style="color: #008000;">MsgBox Error$, 48, "错误信息"Exit SubEnd IfText1text = CStr(SID) & CStr(Cmd) & CStr(InfoAdd) & CStr(data)End SubPrivate Sub incorporate1() '将输入的十进制从机地址、命令、资料地址和资料内容合并成字符串Dim wholechar As String, wc%, wcyc%, wchar As StringDim SID As String, Cmd As String, InfoAdd As String, data As StringDim SIDnum%, Cmdnum%, InfoAddNum%, Datanum%On Error Resume Nextwholechar = CStr(Combo6.Text) & CStr(Text7.Text) & CStr(Text8.Text)wc = Len(wholechar)For wcyc = 1To wcwchar = Mid(wholechar, wcyc, 1)If InStr("0123456789", wchar) = 0ThenMsgBox"输入错误,请重新输入< span style="color: #800000;">", , "错误提示"Exit SubEnd IfNextSIDnum = Len(CStr(Hex(Combo6.Text)))Select Case SIDnumCase0Case1SID = "0"& CStr(Hex(Combo6.Text)) Case2SID = CStr(Hex(Combo6.Text))End Select'Cmdnum = Len(CStr(Hex(Text6.Text)))'Select Case Cmdnum'Case 0' Exit Sub'Case 1' Cmd = "0" & CStr(Hex(Text6.Text))'Case 1' Cmd = CStr(Hex(Text6.Text))'End SelectInfoAddNum = Len(CStr(Hex(Text7.Text))) Select Case InfoAddNumCase0Exit SubCase1InfoAdd = "0"& CStr(Hex(Text7.Text)) Case2InfoAdd = CStr(Hex(Text7.Text))End SelectDatanum = Len(CStr(Hex(Text8.Text))) Select Case DatanumCase0Exit SubCase1data = "000"& CStr(Hex(Text8.Text)) Case2data = "00"& CStr(Hex(Text8.Text)) Case3data = "0"& CStr(Hex(Text8.Text))Case4data = CStr(Hex(Text8.Text))End SelectIf Err Then'显示出错信息< span style="color: #008000;">MsgBox Error$, 48, "错误信息"Exit SubEnd IfIf Option11.value ThenCmd = "08"Text1text = CStr(SID) & CStr(Cmd) & CStr(InfoAdd)ElseCmd = "07"Text1text = CStr(SID) & CStr(Cmd) & CStr(InfoAdd) & CStr(data)End IfEnd SubPrivate Function CRC16(data() As Byte) As StringDim CRC16Lo As Byte, CRC16Hi As Byte'CRC寄存器< span style="color: #00800 0;">Dim CL As Byte, CH As Byte'多项式码&HA001Dim CRCLo As String, CRCHi As StringDim SaveHi As Byte, SaveLo As ByteDim i As IntegerDim Flag As IntegerCRC16Lo = &HFFCRC16Hi = &HFFCL = &H1CH = &HA0For i = 0To UBound(data)CRC16Lo = CRC16Lo Xor data(i) '每一个数据与CRC寄存器进行异或For Flag = 0To7SaveHi = CRC16HiSaveLo = CRC16LoCRC16Hi = CRC16Hi \ 2'高位右移一位< span style="color: #008000;">CRC16Lo = CRC16Lo \ 2'低位右移一位< span style="color: #008000;">If((SaveHi And&H1) = &H1) Then'如果高位字节最后一位为1< span style="color: #008000;">CRC16Lo = CRC16Lo Or&H80 '则低位字节右移后前面补1< span style="color: #008 000;">End If'否则自动补0< span style="color: #008000;">If((SaveLo And&H1) = &H1) Then'如果LSB为1,则与多项式码进行异或CRC16Hi = CRC16Hi Xor CHCRC16Lo = CRC16Lo Xor CLEnd IfNext FlagNext iIf Len(Hex(CRC16Hi)) = 1ThenCRCHi = "0"+ Hex(CRC16Hi)ElseCRCHi = Hex(CRC16Hi)End IfIf Len(Hex(CRC16Lo)) = 1ThenCRCLo = "0"+ Hex(CRC16Lo)ElseCRCLo = Hex(CRC16Lo)End IfCRC16 = CRCLo + CRCHiEnd Function。