当前位置:编程学习 > VB >>

MSCOMM的oncomm事件一次只接收8个字节!!求助啊!

为什么每次都只能接收8个字节,超过8个,他就先收8个,剩下的在下一次在接收

比如说12个字节,就先收8个,再收四个,出发了两次oncomm事件
再比如说17个字节,就先收8个,再收8个,再收一个.

大家分析一下 这与什么有关?

另外不同的波特率下 比如说9600 和 52000下,仍然会出现只收8字节的情况。为什么?

--------------------编程问答-------------------- 你的代码呢?能否贴上来? --------------------编程问答-------------------- 把接受缓冲区加大,另外一个字符一个字符的收。试试! --------------------编程问答-------------------- 回复2楼 

Private Sub Form_Load()
    MSComm1.CommPort = 1
    MSComm1.InBufferSize = 1024
    MSComm1.OutBufferSize = 512
    MSComm1.Settings = "19200,n,8,1"
    MSComm1.InputMode = comInputModeBinary '二进制接收
    MSComm1.InputLen = 0 '设置 InputLen 为 0 时,使用 Input 将使 MSComm 控件读取接收缓冲区中全部的内容
    MSComm1.PortOpen = True
    MSComm1.RThreshold = 1 '设置 Rthreshold 为 1,接收缓冲区收到每一个字符都会使 MSComm 控件产生 OnComm 事件
        
End Sub



Private Sub MsComm1_OnComm()
    
    Select Case MSComm1.CommEvent
        Case comEvReceive
            ReDim ReceiveFrame(MSComm1.InBufferCount)
            'Print MSComm1.InBufferCount
            If MSComm1.InBufferCount = 17 Then '在使用 Input 前,用户可以选择检查 InBufferCount 属性来确定缓冲区中是否已有需要数目的字符。
                ReceiveFrame() = MSComm1.Input 'Input属性:返回并删除接收缓冲区中的数据流
                If (ReceiveFrame(0) = Asc("@")) And (ReceiveFrame(3) = Asc("R")) And (ReceiveFrame(4) = Asc("D")) And (ReceiveFrame(15) = Asc("*")) And (ReceiveFrame(16) = &HD) Then
                    Call Display_Data
                End If
            Else
                MSComm1.InBufferCount = 0 'InBufferCount 是指调制解调器已接收,并在接收缓冲区等待被取走的字符数.可以把InBufferCount属性设置为0来清除接收缓冲区
                Print "清空缓存"
            End If
    End Select
End Sub --------------------编程问答-------------------- 具体情况 我再次描述如下:


单片机和PC机的VB的mSCOMM进行串口通信。
其中单片机为主机,VB为从。

单片机每隔200ms发送17字节的帧给VB,VB接收到17字节后,若符合协议,VB发送10字节的帧给单片机。

请问:(1)采用oncomm好呢,还是采用轮询方式好呢?
(2)若采用oncomm事件,令MSComm1.RThreshold = 1,那么 VB先收8个字节,再收8个字节,最后收1个字节,发生了三次oncomm事件。微软技术支持说是因为数据传输需要时间的缘故,见链接http://faq.csdn.net/read/215129.html 。
但是我发现把波特率更改了变大几倍之后,仍然是上面的“8字节现象”。不知道什么原因?

注:我也觉得微软技术支持说得是对的。因为我用虚拟串口在同一台电脑上面模拟发送和接收,就不会出现“8字节现象”。

现在我的做法是在oncomm程序里面的comEvReceive事件后面 加个延时,针对17个字节延时20毫秒,那么就可以完整接收17个字节了

不知道大家针对这个问题  有什么自己的看法?? 

--------------------编程问答-------------------- http://www.gkong.com/gkong_bbs/dispbbs.asp?ID=36135 --------------------编程问答-------------------- 不要固定设置接收8个字符,设置一个非固定长度的数组作为接收缓冲区:
    Dim m_buffer() As Byte
    If hm_comm.InBufferCount <= 17 Then Exit Sub
    m_buffer = hm_comm.Input  '接收数据
然后判断m_buffer的大小进行处理。
如果你的单片机200ms发送一次数据,单片机的处理时间可能可能比较紧张,最好采用轮询方式 --------------------编程问答-------------------- If hm_comm.InBufferCount <= 17 Then Exit Sub 
改为
If hm_comm.InBufferCount < 17 Then Exit Sub  --------------------编程问答--------------------
Option Explicit
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Sub Form_Load()
    MSComm1.CommPort = 1
    MSComm1.InBufferSize = 1024
    MSComm1.OutBufferSize = 512
    MSComm1.Settings = "19200,n,8,1"
    MSComm1.InputMode = comInputModeBinary '二进制接收
    MSComm1.InputLen = 0 '设置 InputLen 为 0 时,使用 Input 将使 MSComm 控件读取接收缓冲区中全部的内容
    MSComm1.PortOpen = True
    MSComm1.RThreshold = 1 '设置 Rthreshold 为 1,接收缓冲区收到每一个字符都会使 MSComm 控件产生 OnComm 事件
        
End Sub


Private Sub MsComm1_OnComm()
    Dim ReceiveFrame() As Byte
    Select Case MSComm1.CommEvent
        Case comEvReceive
            MSComm1.RThreshold = 0
            Sleep 20
            ReceiveFrame() = MSComm1.Input 'Input属性:返回并删除接收缓冲区中的数据流
            If (ReceiveFrame(0) = Asc("@")) And (ReceiveFrame(3) = Asc("R")) And (ReceiveFrame(4) = Asc("D")) And (ReceiveFrame(15) = Asc("*")) And (ReceiveFrame(16) = &HD) Then
                Call Display_Data
            End If
            MSComm1.RThreshold = 1
    End Select
End Sub
--------------------编程问答-------------------- Veron_04 ,8楼!!

你的这个程序是有问题的!!问题很大!!!

你把我的 原先程序中的MSComm1.InBufferCount = 0 '删除了  ,这是不对的。

接收到不完整的帧,那么接收到下一帧 也不会符合 “If (ReceiveFrame(0) = Asc("@")) And (ReceiveFrame(3) = Asc("R")) And (ReceiveFrame ”这个协议的。


你在程序中加入MSComm1.RThreshold = 0和MSComm1.RThreshold = 1是为了避免中断的嵌套,
这种做法是看起来有理论价值,但实际上似乎很有必要。我会好好考虑的。

--------------------编程问答-------------------- 看看你的单片机发送端是否有问题,发送数据的时候是不是两个数据之间间隔时间太长,如果太长就会触发两次ONCOMM事件。 --------------------编程问答-------------------- 10楼 ,尽管你说得可能是来自的经验。你说得倒也不错。不过,看样子你没有仔细看我在4楼发的帖子。 --------------------编程问答--------------------
引用 9 楼 unsv29 的回复:
Veron_04 ,8楼!!

你的这个程序是有问题的!!问题很大!!!

你把我的 原先程序中的MSComm1.InBufferCount = 0 '删除了  ,这是不对的。

接收到不完整的帧,那么接收到下一帧 也不会符合 “If (ReceiveFrame(0) = Asc("@")) And (ReceiveFrame(3) = Asc("R")) And (ReceiveFrame ”这个协议的。


你在程序中加入MSComm1.RThreshold = 0和MSComm1.RThreshold = 1是为了避免中断的嵌套,
这种做法是看起来有理论价值,但实际上似乎很有必要。我会好好考虑的。


你的通信是被动的,你要掌握好单片机发给你的数据的频率,使用了Input读取了串口缓冲区后,其实InBufferCount也就等于0了。给你一个代码参考一下吧,这个绝对正确:)
http://download.csdn.net/source/1262066 --------------------编程问答-------------------- MSComm1.RThreshold = 17

既然确定下位机发送 17 个字节,为什么不一次完整接收。

你收到 8 个字节,是因为第一个字节触发事件,到你完成接收,缓冲区恰好收到 8 个。当然你也可以采取延时等待的方法, 

Do Until MSComm1.InBufferCount >= 17
    DoEvents
Loop 
ReceiveFrame() = MSComm1.Input

但接收到 17 个字节再触发 OnComm 事件更直接。

如果想快,而且你确定你的代码所用的系统,包括硬件都是一致的,也可以 MSComm1.RThreshold = 10,你进入事件后,恰好收到 17 个。
--------------------编程问答--------------------
至于 MSComm1.InBufferCount = 0,如果你确定下位机发送的就是 17 个字节,你也照单全收了,就多此一举。因为它已经是 0 了。 --------------------编程问答-------------------- 各位有所不知,LZ的串口是虚拟的,它涉及建立虚拟串口的驱动.
我们一般使用实际的物理串口,为调试COM口通信,电脑往往用串口扩展卡增加物理串口. --------------------编程问答-------------------- 回12楼 你说得没有错 是被动的  ,但是如果中途受干扰呢?

或者单片机的帧发送出去前几个字节后,单片机断电了呢?? --------------------编程问答-------------------- 楼上各位 请搞清 !@!

正如12楼所说 VB是被动接受的  所以说 单片机是上位机   VB是下位机 。不要以为电脑总是下位机。 --------------------编程问答--------------------
引用 16 楼 unsv29 的回复:
回12楼 你说得没有错 是被动的  ,但是如果中途受干扰呢?

或者单片机的帧发送出去前几个字节后,单片机断电了呢??

那就解决干扰问题.
单片机会断电,那找个好的UPS电源来解决断电问题.
同样电脑也会断电,LZ的情况是要在非正常工况下如何来保证通信的正常.
--------------------编程问答-------------------- [url=http://www.vbgood.com/viewthread.php?tid=86156&extra=&page=1url]
各位回复时看下LZ的上贴. --------------------编程问答--------------------
引用 19 楼 zdingyun 的回复:
http://www.vbgood.com/viewthread.php?tid=86156&extra=&page=1
各位回复时看下LZ的上贴.
--------------------编程问答--------------------
RS232 串行通讯是真正双工的,没有所谓“上位”“下位”的严格区分。任何一方都可以发起通讯。

不知楼上强调下位机的目的何在?这和一次接收多少个字节有关系吗?

--------------------编程问答-------------------- 21楼可以分得清 楼上楼下 却分不清上位下位???

怎么回事呢??

of123,你知识都学杂了啊!! --------------------编程问答-------------------- MSComm1.InputLen = 1
电脑一个字节一个字节的收,你的状况没必要使用中断。

用语句来计数。

搞硬件,尽量采用简单的方法去处理才会可靠。 --------------------编程问答-------------------- 仔细看了LZ在3楼的代码,靠你的代码是无法完成正确接收来自下(上)位机的17字节数据的.
除非你初始化MSComm1控件时设置
MSComm1.RThreshold = 17
否则你只能在你的机器条件下如你所说的情况.
串口通信的基础是依据通信协议和数据帧约定做好算法,并依据此算法设计接收代码.
在次建议LZ,在有缺陷的代码上出问题,不找代码问题而去钻牛角尖,并听不进他人的意见,在此斗嘴.
下面给出一段经调试通过的符合LZ要求的代码:
Option Explicit
    Dim strRecHex As String

Private Sub Form_Load()
    MSComm1.CommPort = 1
    MSComm1.InBufferSize = 1024
    MSComm1.OutBufferSize = 512
    MSComm1.Settings = "19200,n,8,1"
    MSComm1.InputMode = comInputModeBinary '二进制接收
    MSComm1.InputLen = 0 '设置 InputLen 为 0 时,使用 Input 将使 MSComm 控件读取接收缓冲区中全部的内容
    MSComm1.PortOpen = True
    MSComm1.RThreshold = 1 '设置 Rthreshold 为 1,接收缓冲区收到每一个字符都会使 MSComm 控件产生 OnComm 事件
End Sub

Private Sub MSComm1_OnComm()
    Dim ReceiveFrame() As Byte
    Dim i As Integer
    Select Case MSComm1.CommEvent
        Case comEvReceive
            ReDim ReceiveFrame(MSComm1.InBufferCount)
            Debug.Print MSComm1.InBufferCount
            ReceiveFrame() = MSComm1.Input 'Input属性:返回并删除接收缓冲区中的数据流
            For i = 0 To UBound(ReceiveFrame)
                strRecHex = strRecHex & Right("0" & Hex(ReceiveFrame(i)), 2)
            Next
            'If (ReceiveFrame(0) = Asc("@")) And (ReceiveFrame(3) = Asc("R")) And (ReceiveFrame(4) = Asc("D")) And (ReceiveFrame(15) = Asc("*")) And (ReceiveFrame(16) = &HD) Then
            If Mid(strRecHex, 1, 2) = "40" And Mid(strRecHex, 7, 4) = "5244" And Mid(strRecHex, 31, 4) = "2A0D" Then
                Text1 = strRecHex
                'Call Display_Data
                strRecHex = ""
            ElseIf Len(strRecHex) > 34 Then
                strRecHex = ""
            End If
    End Select
End Sub
--------------------编程问答-------------------- 更正LS回复的一句话,应该为:
建议LZ,在有缺陷的代码上出问题时,应找代码问题而不要去钻牛角尖,也不要听不进他人的意见,在此斗嘴. --------------------编程问答--------------------
引用 16 楼 unsv29 的回复:
回12楼 你说得没有错 是被动的  ,但是如果中途受干扰呢?

或者单片机的帧发送出去前几个字节后,单片机断电了呢??


从我的感觉来说,感觉就是你的没有延迟,单片机发送数据的速率太快了,你的数据还没有处理完,就又有数据来了,所以,我个人的构思是:
1、一个一个字符的接受,触发OnComm事件
2、当触发了OnComm事件,马上把RThreshold设置为:0,这样可以避免再收到字符事触发OnComm事件,等待一小段时间,这个是必须的,等待一个完整的数据帧产过来,但具体等待多久,这个就得看实际情况了。
3、等待完毕后,就可以读缓冲区了。当然你需要对你读回来的数据进行处理判断。处理完毕后,将:RThreshold设置为:1,即可再次接收并触发事件了。

关键点在:单片机上传数据的时间间隔和通信等待时间的匹配。

以上是我的拙见。。呵呵 --------------------编程问答-------------------- 你可以等待短一点也无所谓,我给你的那个串口调试器源码有详细的使用,你看看应该有用的。我的代码是借鉴那个来的。 --------------------编程问答-------------------- 我要接收有头和尾的数据,头是35 尾是“H0D”,为什么我把sleep去了就不可以了 ,且加sleep 后,有些数据有时候可以受到,有时候不可以,大家帮忙呀
    MSComm1.InputMode = comInputModeBinary      '采用二进制接收 
    MSComm1.InBufferCount = 0  '清空接受缓冲区 
    MSComm1.OutBufferCount = 0  '清空传输缓冲区 
    MSComm1.InBufferSize = 100 '接收缓冲区大小 
    MSComm1.OutBufferSize = 100 '发送缓冲区大小 
    MSComm1.RThreshold = 1      '设置引发OnComm事件的字节长度 
    MSComm1.InputLen = 1 '设置Input从接收缓冲读取全部数据 
    MSComm1.PortOpen = True 
Private Sub MSComm1_OnComm() 
    Sleep (500) 
    Select Case MSComm1.CommEvent 
      Case comEvReceive 
        Do While MSComm1.InBufferCount <> 0 
            indata = MSComm1.Input 
            If jieshouok Then 
              arrt(p) = indata(0) 
              p = p + 1 
            Else 
                If jieshouok = False And indata(0) <> 35 Then 
                Else 
                  jieshouok = True 
                  arrt(p) = indata(0) 
                  p = p + 1 
                End If 
            End If 
            If indata(0) = 13 And jieshouok Then 
                '数据处理
               p=0
             end if 
        Loop 
    End Select 
end sub --------------------编程问答-------------------- #27楼 串口助手和本楼主的问题 没有太大关系。即使你会编写串口调试器,也不能解决楼主的问题。

比如单片机发送30个字符给 串口助手,于是你就看到串口助手上一下子显示出了30个字符。

事实上,助手是先收到8个 又受到8个 又受到8个 又收到6个!


#27楼解释一下 这个原因是什么??

--------------------编程问答-------------------- 应该是延迟等待的问题,就是那个Sleep的问题 --------------------编程问答-------------------- 楼上, 这个MSCOMM玄机太多了


Private Sub MsComm1_OnComm()
    Select Case MSComm1.CommEvent
        Case comEvReceive
            ReDim ReceiveFrame(MSComm1.InBufferCount)
            Print "MSComm1.InBufferCount" & MSComm1.InBufferCount
            If MSComm1.InBufferCount = 17 Then 
                MSComm1.InBufferCount = 0
            End If
    End Select
        
End Sub

单片机每发送17个字符,你猜上面这段代码打印出什么了???

太深奥了!!!
--------------------编程问答-------------------- 竟然打印出
MSComm1.InBufferCount8
MSComm1.InBufferCount8
MSComm1.InBufferCount16
MSComm1.InBufferCount16
MSComm1.InBufferCount17 --------------------编程问答-------------------- 还有 看看这段代码打印出什么来了???


Private Sub MsComm1_OnComm()
    Select Case MSComm1.CommEvent
        Case comEvReceive
            MSComm1.RThreshold = 0
            Sleep (30)
            ReDim ReceiveFrame(MSComm1.InBufferCount)
            Print "MSComm1.InBufferCount" & MSComm1.InBufferCount

    End Select
        
End Sub


--------------------编程问答-------------------- 关注 --------------------编程问答--------------------
无语~~
--------------------编程问答-------------------- 楼上两位动动脑筋,想想是什么原因? --------------------编程问答-------------------- 本来接收就存在不确定性 --------------------编程问答-------------------- --------------------编程问答--------------------
补充:VB ,  控件
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,