热情软件屋

 

通过串口发送数据,返回值有时对,有时不对


编号:QA004687
建立日期: 2003年5月20日 最后修改日期:2003年12月6日
所属类别:

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
    

回答:

    宋霈霖的意见:
    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

 
把这个问题推荐给朋友
   
   
您的意见类别
您的名字
您的电子邮件
您的建议(请尽可能详细)
 
 

版权所有 1997-2008 热情软件屋
如果您有任何建议和意见, 请给我发个电子邮件 askpro@china-askpro.com
Web Designed by ZebraStudio