通过串口发送数据,返回值有时对,有时不对
编号:QA004687
建立日期: 2003年5月20日 最后修改日期:2003年12月6日
所属类别:
Q
Bright:
操作系统:WIN
编程工具:VB
问题:我通过串口向一个仪表发送数据,为4个字节,然后仪表会反馈给我一个8个字节的数据,每隔200ms发送一次,我每次发送之前和读取返回值之后都会清空发送和接受缓冲区,但返回值有时对,有时不对。那位专家可以帮帮我,很急!!!
注:有两个协议
协议一:发送时,为4个字节,接收数据为8个字节。
协议二:发送时,为8个字节,接收数据为10个字节。
在协议一时出现上述问题,协议二工作正常。
我通过串口助手检测,已经排除仪表本身的问题。
非常感谢您的帮助,我这次把源代码附上,请指教!
源代码:
'我通过串口向一个仪表发送数据,为4个字节,然后仪表会反馈给我一个8个字节的数据,每隔200ms发送一次,在用协议二时,返回值有时对,有时不对。哪位专家可以帮帮我,很急!!!
'注: 有两个协议,分别应用于两种仪表
' 协议一:发送时,为4个字节,接收数据为8个字节。
' 协议二:发送时,为8个字节,接收数据为10个字节。
'在协议一时出现上述问题,协议二工作正常。
'我通过串口助手检测,已经排除仪表本身的问题。
Private arrSend(8) As Byte
Private arrSendRead(4) As Byte
Private strFlag As String
Private Sub Form_Load()
With MSComm1
.CommPort = 1
.Settings = "9600,n,8,2" '设置通信口参数
.InBufferSize = 640 '设置MSComm1接收缓冲区为100字节
.OutBufferSize = 512 '设置MSComm1发送缓冲区为80字节
.InputMode = comInputModeBinary '设置接收数据模式为二进制形式
.InputLen = 1 '设置Input 一次从接收缓冲读取字节数为1
.SThreshold = 0 '设置Output 一次从发送缓冲读取字节数为1
.RThreshold = 10 '设置接收一个字节产生OnComm事件
.InBufferCount = 0 '清除接收缓冲区
.OutBufferCount = 0 '清除发送缓冲区
If .PortOpen = False Then '判断通信口是否打开
.PortOpen = True '打开通信口
If Err.Description Then '错误处理
MsgBox "串口通信无效!"
Exit Sub
End If
End If
End With
strFlag = "1" '协议一
strFlag = "2" '协议二
Timer1.Enabled = True
End Sub
Private Sub Timer1_Timer() '200ms触发一次
sendReadDec
End Sub
Private Sub sendReadDec()
On Error GoTo errhandle
MSComm1.OutBufferCount = 0
MSComm1.InBufferCount = 0
If strFlag = "1" Then '协议一
arrSend(0) = &H80 + Hex(1)
arrSend(1) = &H80 + Hex(1)
arrSend(2) = &H52
arrSend(3) = &HC
arrSend(4) = &H0
arrSend(5) = &H0
arrSend(6) = CByte("&H" & Right(CStr(Hex(arrSend(3) * 256 + 82 + 1)), 2))
arrSend(7) = (arrSend(3) * 256 + 82 + 1 - arrSend(6)) \ 256
MSComm1.RThreshold = 10
MSComm1.Output = arrSend
Else '协议二
arrSendRead(0) = &H80 + Hex(1)
arrSendRead(1) = &H80 + Hex(1)
arrSendRead(2) = &H52
arrSendRead(3) = &HC
MSComm1.RThreshold = 8
MSComm1.Output = arrSendRead
End If
Exit Sub
errhandle:
If Err.Number = 8018 Then
MsgBox "端口已经被其它应用程序打开,系统不能正常运行!", vbOKOnly + vbCritical, "端口错误"
End If
End Sub
Private Sub MSComm1_OnComm()
Dim arrReceive(10) As Byte
Dim lngData As Variant '用来从接收缓冲区读取数据
On Error Resume Next
With MSComm1
Select Case MSComm1.CommEvent
Case comEvReceive
RThreshold = 0 '关闭OnComm事件接收
lngData = .Input '读取一个接收字节
arrReceive(0) = lngData(0)
lngData = .Input
arrReceive(1) = lngData(0)
lngData = .Input
arrReceive(2) = lngData(0)
lngData = .Input
arrReceive(3) = lngData(0)
lngData = .Input
arrReceive(4) = lngData(0)
lngData = .Input
arrReceive(5) = lngData(0)
lngData = .Input
arrReceive(6) = lngData(0)
lngData = .Input
arrReceive(7) = lngData(0)
If strFlag = "1" Then
lngData = .Input
arrReceive(8) = lngData(0)
lngData = .Input
arrReceive(9) = lngData(0)
End If
End Select
End With
End Sub
A回答:
宋霈霖的意见:
1、可以将处理该事务的过程放入类模块中。
2、接收和发送过程本身是分开的,所以没有必要在 OnComm 事件发生时设置 Rthreshold 属性,直接在初始化时设置为 8 或 10 即可。
3、清除缓冲区中的数据: 发送后再清除已发送的数据,接收后再清除接收到的数据,尽量将其分开管理。
4、数组可定义为动态的,如: Dim var as Variant, lngData() as Byte
访问时:
var = MSComm1.Input
lngData = var
For i = LBound(lngData) To UBound(lngData)
Debug.Print lngData(i)
Next i
5、具体请参看 NSDN
ldg168的意见:
一、在Form_load()事件中改以下代码
.InputLen = 0 '设置Input 一次从接收缓冲读取所有字节
.SThreshold = 0 '0表示不产生发送事件,n表示当发送缓冲区中的字符小于n时产生发送事件
.RThreshold = 10 '设置接收一个字节产生OnComm事件
二、你的说明与你的Sub sendReadDec()过程不符,按说明在strFlag="1"时应该设置MSComm1.RThreshold = 8,strFlag="2"时应该设置MSComm1.RThreshold = 10。
三、应该设置一个模块级Boolean变量 Sending,在Sub sendReadDec()过程中加入Sending=True,在MSComm1_OnComm() 的接收事件中加入Sending=False,Timer1_Timer()中的代码改为 If Not Sending Then sendReadDec。
四、MSComm1_OnComm()的代码可以简化如下
Private Sub MSComm1_OnComm()
Dim arrReceive() As Byte
Dim bytData As Variant '用来从接收缓冲区读取数据
On Error Resume Next
With MSComm1
Select Case MSComm1.CommEvent
Case comEvReceive
bytData = .Input
Redim arrReceive(Ubound(bytData) As Byte
For i=0 To Ubound(bytData)
arrReceive(i)=bytData(i)
Debug.Print arrReceive(i)
Next i
End Select
End With
End Sub
如果允许,可将Timer1的延时设置长些,如400,600ms(200ms的倍数)等。
Lily Qian的意见:
It might because of the buffer is not clear when you send out the output string, put a delay before and after you sent out your output:
While MSComm1.OutBufferCount > 0: DoEvents: Sleep 1: Wend 'Make sure buffer is empty
MSComm1.Output = arrSendRead
While MSComm1..OutBufferCount > 0: DoEvents: Sleep 1: Wend ' delay until output string was send out
g66的意见:
产生这个问题的原因是串口通讯返回值都会有一个头码和一个尾码,头尾之间夹的就是数据串,如果不截掉头尾码,只按字节数取值,取出的肯定是乱码,偶尔会恰好取中,看起来就对了。
解决的办法是查明该通讯协议的返回值文本格式,每次读出缓冲区后截掉头尾码。
另外取值时直接取字符串而不要一个字符一个字符地取。
还要注意的一个问题是在Win2000下,系统有可能会在仪表返回的数值后自动加上一个其他的字符(莫明其妙),那么你在处理时要将那个字符与尾码合起来处理。
versed的意见:
发送时每个字节分开发,中间加适当延时(1-20ms),不要象原文这样一起发“MSComm1.Output = arrSend ”,发完后再延时一段时间读取数据;读取数据采用查询方式,不要使用事件驱动方式。使用通讯协议会好一些,但会增加外部设备编程的复杂程度。串口参数使用控件的缺省值既可,不必额外设置。---初学者
cssun的意见:
估计问题出在接收时序上,接收数据应当等到全部字节接收到后再从接收缓冲区读取数据。
mflying的意见:
我想问题出在RThreshold属性的设置上,程序中设置的是10,也就是说当接收缓冲区中的字节数从9到10进发生一次接收数据事件,在你的说明中指出了协议一接收数据为8个字节,那样第一次的回送数据要到第二个命令发出之后才能行到处理,而每次处理之后都清除了缓冲区,因面造成第二次回送数据的丢失(至少丢失了两个字节),对协议2每次回送数据是10个字节,不会出现上述问题。
ZQlooknow的意见:
lngData 应定义为 dim lngData(0) as byte
Yu Miao Bin的意见:
串口通信时, 每次收到一个OnComm 事件时,只是说明串口收到了数据,并不表示收到所有数据, 所以不能在OnComm函数里就对数据进行处理,而是应该将所接收到的数据保存到一个buffer中,然后待buffer中的数据达到期望接收的个数后,再进行处理。
Alan的意见:
注意mscomm的RThreshold属性,当inbuf接受到RThreshold个字节时,就产生oncomm事件。所以最好把RThreshold设为刚好要接受的字节数。或者(建议),在通信协议中定义起始字,通过判断起始字来确定后继的字节是正确的数据。
maxlau的意见:
在ONCOMM事件中 “RThreshold = 0 '关闭OnComm事件接收”是多余。
我想按照ldg168的意见和Lily Qian的意见,应该搞定了吧!欢迎联系!QQ:10115292
ycd的意见:
发送时间间隔不准。Time事件有相隔0ms情况。你把Time事件看成绝对200ms。
此问题由宋霈霖等回答。
附加关键字:编程, 源程序, programming, source code, Visual Basic, VB, 网络与通信, network, communicate, com, com1, com2。
| |
|
|
| |
|
|