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--------------------编程问答-------------------- Veron_04 ,8楼!!
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
你的这个程序是有问题的!!问题很大!!!
你把我的 原先程序中的MSComm1.InBufferCount = 0 '删除了 ,这是不对的。
接收到不完整的帧,那么接收到下一帧 也不会符合 “If (ReceiveFrame(0) = Asc("@")) And (ReceiveFrame(3) = Asc("R")) And (ReceiveFrame ”这个协议的。
你在程序中加入MSComm1.RThreshold = 0和MSComm1.RThreshold = 1是为了避免中断的嵌套,
这种做法是看起来有理论价值,但实际上似乎很有必要。我会好好考虑的。
--------------------编程问答-------------------- 看看你的单片机发送端是否有问题,发送数据的时候是不是两个数据之间间隔时间太长,如果太长就会触发两次ONCOMM事件。 --------------------编程问答-------------------- 10楼 ,尽管你说得可能是来自的经验。你说得倒也不错。不过,看样子你没有仔细看我在4楼发的帖子。 --------------------编程问答--------------------
你的通信是被动的,你要掌握好单片机发给你的数据的频率,使用了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是下位机 。不要以为电脑总是下位机。 --------------------编程问答--------------------
那就解决干扰问题.
单片机会断电,那找个好的UPS电源来解决断电问题.
同样电脑也会断电,LZ的情况是要在非正常工况下如何来保证通信的正常.
--------------------编程问答-------------------- [url=http://www.vbgood.com/viewthread.php?tid=86156&extra=&page=1url]
各位回复时看下LZ的上贴. --------------------编程问答-------------------- --------------------编程问答--------------------
RS232 串行通讯是真正双工的,没有所谓“上位”“下位”的严格区分。任何一方都可以发起通讯。
不知楼上强调下位机的目的何在?这和一次接收多少个字节有关系吗?
--------------------编程问答-------------------- 21楼可以分得清 楼上楼下 却分不清上位下位???
怎么回事呢??
of123,你知识都学杂了啊!! --------------------编程问答-------------------- MSComm1.InputLen = 1
电脑一个字节一个字节的收,你的状况没必要使用中断。
用语句来计数。
搞硬件,尽量采用简单的方法去处理才会可靠。 --------------------编程问答-------------------- 仔细看了LZ在3楼的代码,靠你的代码是无法完成正确接收来自下(上)位机的17字节数据的.
除非你初始化MSComm1控件时设置
MSComm1.RThreshold = 17
否则你只能在你的机器条件下如你所说的情况.
串口通信的基础是依据通信协议和数据帧约定做好算法,并依据此算法设计接收代码.
在次建议LZ,在有缺陷的代码上出问题,不找代码问题而去钻牛角尖,并听不进他人的意见,在此斗嘴.
下面给出一段经调试通过的符合LZ要求的代码:
Option Explicit--------------------编程问答-------------------- 更正LS回复的一句话,应该为:
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
建议LZ,在有缺陷的代码上出问题时,应找代码问题而不要去钻牛角尖,也不要听不进他人的意见,在此斗嘴. --------------------编程问答--------------------
从我的感觉来说,感觉就是你的没有延迟,单片机发送数据的速率太快了,你的数据还没有处理完,就又有数据来了,所以,我个人的构思是:
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 , 控件