发表于:2008-03-06 16:20:00
53楼
Option Explicit
Dim outdata() As Byte '定义发送数组,用来存放转换后的命令数据
Dim Rcvlen As Integer '定义接收到的数据的长度
Dim Rcv() As Byte '定义接收数组,用来存放接收到的数据
Dim inString As String '定义输入命令字符串
Dim RcvFinFlag As Boolean '定义接收完成标志
Dim ReadFlag As Boolean '定义“读命令”标志
Dim ReSendFlag As Boolean
Dim CheckFlag As Boolean
Dim No1 As Integer '定义重发次数设定
Dim No2 As Integer '定义重发次数计数器
Dim FinalDataLen As Integer '定义接收到数据的最终长度变量
Dim SaveString As String '定义输入命令暂存字符串变量
Private Sub Cmdopen_Click()
If Not MSComm1.PortOpen Then MSComm1.PortOpen = True '如果串口没有打开,打开串口
inString = "00FFWWAD00100202A11111" '输入开机命令字符串
Call send(inString) '调用发送子程序,形成命令帧并发送
Timer1.Enabled = True '打开“定时读取温度值”定时器
End Sub
Private Sub Cmdset_Click()
inString = "00FFWWAD0010020251" '输入命令报文的固定部分
Dim temp As Variant
Dim t As Variant
Dim j As Integer
temp = Trim(Txtset.Text) '取设定文本框输入的数据,以字符串的形式处理
If Not IsNumeric(temp) Then '如果输入的数据有格式错误
MsgBox "请按如下范围和格式输入数据:-100.0~300.0", vbExclamation, "输入数据范围错误"
Exit Sub
End If
Dim Temperature As Integer
If temp > 300 Or temp < -100 Then '如果超出规定范围之内
MsgBox "请输入-100.0~300.0之间的数", vbExclamation, "输入数据格式错误"
Exit Sub
Else
Temperature = CInt(10 * Val(temp)) '将输入的数据转换为以0.1度为单位的整数
t = Hex(Temperature) '将输入的温度值转换为十六进制数
j = Len(t) '求转换后的十六进制数的位数
t = String(4 - j, "0") & t '十六进制数高位添0,处理为4位的格式
End If
ReadFlag = False '复位读标志
inString = inString & t '转换后的温度设定值附加到instring的末尾
If Timer1.Enabled = True Then '若在开机状态下
Timer1.Enabled = False '发送温度设置命令前,暂时关闭定时器1
Call send(inString) '发送设定值到PLC
Timer1.Enabled = True '发送温度设置命令后,重新开始定时器1
Timer1.Enabled = True '若在关机状态下
Else
Call send(inString) '直接发送设定值到PLC
End If
End Sub
Private Sub Cmdstop_Click()
MSComm1.PortOpen = False
End
End Sub
'初始化
Private Sub Form_Load()
With MSComm1
.CommPort = 1 '选择串口1
.Settings = "9600,n,8,1" '设置通讯格式
.InputMode = comInputModeBinary '以二进制格式读取缓冲区
.RThreshold = 1 '接收到的字符数大于等于1时产生接收事件
.InputLen = 0 '读出接收缓冲区所有的内容
.OutBufferCount = 0 '清空发送缓冲区
.InBufferCount = 0 '清空接收缓冲区
End With
If Not MSComm1.PortOpen = True Then MSComm1.PortOpen = True '打开串口1
Timer1.Interval = 4000 '设置定时读取温度值的中断时间
Timer1.Enabled = False '初始化定时读取温度值定时器
Timer2.Interval = 1000 '设置超时判定定时器的中断时间
Timer2.Enabled = False '初始化超时判断定时器
No1 = 3 '初始化重新发送次数3次
No2 = 0 '初始化重发次数为0
RcvFinFlag = True '初始化接收完成标志
ReSendFlag = True '初始化重发标志
End Sub
Private Sub MSComm1_OnComm()
Dim RcvTemp() As Byte '定义存放每次接收到的数据的暂存数组
Dim i As Integer
Dim t As Variant
ReDim Preserve Rcv(100) As Byte '预设接收字符数组Rcv
If RcvFinFlag Then '如果报文接收处理完成
Exit Sub
Else
Select Case MSComm1.CommEvent 'MSComm控件产生通讯事件或通讯错误
Case comEventFrame '检测到一个因双方的通讯格式不同引发的错误
MsgBox "双方通讯格式不一致", vbExclamation, "提示" '弹出错误
Timer1.Enabled = False '关闭定时发送定时器
Timer2.Enabled = False '关闭超时判断定时器
Exit Sub
Case comEvReceive '若接收到字符
RcvTemp = MSComm1.Input '将接收缓冲区的内容送入暂存数组
For i = LBound(RcvTemp) To UBound(RcvTemp) '
Rcvlen = Rcvlen + 1 '接收字符个数加1,Rcvlen的初始值为-1
If Rcvlen > 100 Then '如果接收数据超过接收数组上限
Rcvlen = -1 '复位接收到的数据的长度变量
Call ErrorHandle '进行错误处理
Exit Sub
End If
Rcv(Rcvlen) = RcvTemp(i) '将接收到的各字节送入接收字节数组
Next
ReDim Preserve Rcv(Rcvlen) As Byte '重新定义并保存接收字符数组
If Rcvlen >= 1 Then
For i = LBound(Rcv) + 1 To UBound(Rcv)
If Rcv(i) = &HA And Rcv(i - 1) = &HD Then '如果接收到回车换行符
RcvFinFlag = True '报文接收完成标志置位
FinalDataLen = i '保存接收到的最终数据长度
ReDim Preserve Rcv(FinalDataLen) As Byte '重新定义并保存接收字符数组
Rcvlen = -1 '初始化接收到的数据的长度变量
Exit For
End If
Next
End If
End Select
End If
If RcvFinFlag = True Then '若报文接收结束
If ReadFlag Then '若为定时读取数据命令
If Rcv(0) = &H2 Then '且报文以STX开始
t = RcvDataChk(Rcv) '调用接收数据检查子程序
If t Then '若接收到的数据正确
Call RcvDataDisplay(Rcv, FinalDataLen) '显示
Call confirm(&H6) '向 PLC发送ACK开始的确认报文
ReadFlag = False '“读命令”复位标志
No2 = 0 '重发计数次数复位
Timer2.Enabled = False '关闭通讯超时定时器
Else
Call confirm(&H15) '向PLC发送NAK开始的无法确认报文
Call ErrorHandle '进行错误处理
End If
Else
If Rcv(0) = &H15 Then Call confirm(&H15) '
Call ErrorHandle '
End If
Else
If Rcv(0) = &H6 And FinalDataLen = 6 Then '若PLC正确执行写命令
Timer2.Enabled = False '关闭通讯超时定时器
No2 = 0 '复位重发次数
Exit Sub
Else
Call ErrorHandle '
End If
End If
End If
End Sub
Private Sub Timer1_Timer()
ReadFlag = True '置位“读命令”标志
inString = "00FFWRAD010001" '输入定时读取D100的命令字符串
Call send(inString) '调用发送子程序,形成命令帧发送
End Sub
Private Sub Timer2_Timer()
Call ErrorHandle
End Sub
Private Sub send(inString As String)
Dim length, i As Integer
If RcvFinFlag = True Then '前以命令执行完毕,接收完成标志为True
SaveString = inString '保存命令字符串
Rcvlen = -1 '接收数据存放数组的下标初始化
RcvFinFlag = False '接收完成标志复位
length = Len(inString) '求形参传递过来的字符串的长度
ReDim Preserve outdata(0 To length) As Byte '重新定义发送数据数组,其元素个数为length+1
outdata(0) = &H5 '命令报文以控制代码“ENQ”开始
For i = 1 To length '字符串转换为ASCII码,送入发送数组
outdata(i) = Asc(Mid(inString, i, 1)) '
Next
Call FCScheck(outdata) '产生校验和,形成发送帧
length = UBound(outdata) '
ReDim Preserve outdata(0 To length + 2) As Byte '重新定义发送数据数组
outdata(length + 1) = &HD '因为是传输格式4,最后添加回车换行符
outdata(length + 2) = &HA '
MSComm1.Output = outdata '发送命令帧
Timer2.Enabled = True '开启超时判断定时器
Else
MsgBox "前以命令尚未执行完毕", vbExclamation, "操作提示"
End If
End Sub
Private Sub FCScheck(outdata() As Byte)
Dim BufLen As Integer, Buf As String '定义字符串长度变量和字符串变量
Dim i As Integer
Dim CheckSum As Long '定义校验和变量
BufLen = UBound(outdata) '求outdata数组可用的最大下标
CheckSum = 0 '初始化校验和
For i = LBound(outdata) + 1 To UBound(outdata) '求和时不包括开始的控制代码
CheckSum = (CheckSum + outdata(i)) And &HFF '对outdata数组的元素求和,只保留低位字节
Next i
Buf = IIf(Len(Hex(CheckSum)) = 1, "0" & Hex(CheckSum), Hex(CheckSum))
'若校验和只有1位,则高位添零,补足位2位
ReDim Preserve outdata(BufLen + 2) As Byte
outdata(BufLen + 1) = Asc(Mid(Buf, 1, 1)) '校验和转换为ASCII码,低位在前
outdata(BufLen + 2) = Asc(Mid(Buf, 2, 1))
End Sub
Private Sub ErrorHandle()
If No2 >= 0 And No2 < No1 Then '若重发次数小于重发设定值,则重发命令
No2 = No2 + 1 '重发次数加1
RcvFinFlag = True '置位接收完成标志,准备重发
Call send(SaveString) '重发
Exit Sub
Else
Timer1.Enabled = False '关闭定时读取温度值定时器
Timer2.Enabled = False '关闭超时判断定时器
MsgBox "请检查硬件连接及报文设置", vbExclamation, "通讯超时或通讯过程出错"
No2 = 0 '复位重发次数
ReadFlag = False '读命令标志复位
RcvFinFlag = True '接收完成标志置位
Exit Sub
End If
End Sub
Private Sub txtset_keypress(KeyAscii As Integer)
Dim temp As String
temp = (Chr(KeyAscii))
If Not (temp Like "[0-9;.;-]" Or KeyAscii = 8 Or KeyAscii = 13) Then
MsgBox "只允许输入0~9、小数点、负号或退格和火车", vbExclamation, "键盘输入字符错误"
Exit Sub
End If
End Sub
Private Function RcvDataChk(Cdata() As Byte) As Boolean
Dim i As Integer
Dim EndNo As Integer
CheckFlag = False '校验标志初始化
For i = 0 To UBound(Cdata)
If Cdata(i) = &H3 Then '如果找到接收到的报文中的ETX(文本结束符)
EndNo = i '保存ETX所在的数组元素的下标
Exit For
End If
Next
Dim Ddata() As Byte '定义新数组
ReDim Ddata(EndNo) As Byte
For i = 0 To EndNo '将接收到的报文中需要求和的部分存入该数组
Ddata(i) = Cdata(i)
Next
Call FCScheck(Ddata) '调用求校验和子程序。校验和存放在数组末尾
If Ddata(EndNo + 1) = Cdata(EndNo + 1) And Ddata(EndNo + 2) = Cdata(EndNo + 2) Then
CheckFlag = True '如果求校验和与接收到的相等,将校验标志置位
End If
RcvDataChk = CheckFlag '返回校验结果
End Function
Private Sub RcvDataDisplay(Rcv() As Byte, Rcvlen As Integer)
If Rcv(0) = &H2 Then '如果是发送读命令后PLC返回的报文
Dim Shwtemp As Variant '定义用于显示字符串的变量
Dim i As Integer
Shwtemp = "" '显示字符串变量初始化
For i = 5 To Rcvlen 'I=5开始上传的数据
If Rcv(i) <> &H3 Then '如果不是文本结束的代码ETX
Shwtemp = Shwtemp & Chr(Rcv(i)) '接收到的字符送入显示字符串变量
Else
Exit For
End If
Next
Txtview = Format((Val("&h" & Shwtemp) / 10), "# #0.0") '按小数点后一位的格式显示温度值
End If
End Sub
'读命令接收数据响应子程序
Private Sub confirm(CodeByte As Byte)
ReDim outdata(6) As Byte
outdata(0) = CodeByte '报文以控制代码ACK或NAK开始
outdata(1) = &H30 'PLC的站号设置为0
outdata(2) = &H30
outdata(3) = &H46 'FX系列PLC的标识FFH
outdata(4) = &H46
outdata(5) = &HD ' 因为送传送格式4,以回车换行符结束
outdata(6) = &HA
MSComm1.Output = outdata '发送确认或不能确认的报文
End Sub