(求助高人)关于VB串口通讯下的一个奇怪的问题
我写了一个VB的用来发送和接受串口数据的小程序,下位机发送上来的16进制码比如:EB 90 C1(设备代号) A0/A1(标示位,A0要求采样发送,A1采样数据) 68 09 00 00 10 07 00 68 81 06 43 C3 83 3C 33 33 A2 A3(采样数据,9路,比如68代表高4位,09代表低4位) 16(校检和,从设备代号一直加到A2 A3采样)。我希望把接到16进制数据转成字符串比如EB90C1A0/A16809000010070068810643C3833C3333A2A316(校验和)
Private Sub Form_Load()
MSComm1.Settings = "1200,e,8,1"
MSComm1.InBufferSize = 1024
MSComm1.OutBufferSize = 512
MSComm1.InputMode = comInputModeBinary
MSComm1.RThreshold = 1 '1 or 18
MSComm1.PortOpen = True
End Sub
Private Sub MSComm1_OnComm()
Dim buffer() As variant
Dim i As Integer
Dim Incept_1 As string
Dim Incept As string
Select Case MSComm1.CommEvent
Case comEvReceive
buffer = MSComm1.Input '这地方串口调试助手显示发送进来的是正确的
For i = LBound(buffer) To UBound(buffer)
If Len(Hex(buffer(i))) = 1 Then
Incept_1 = Incept_1 & "0" + Hex(buffer(i)) & Chr32
Else
Incept_1 = Incept_1 & Hex(buffer(i)) & Chr32
End If
Next i
Incept=Incept_1
Incept_1=""
//////// Mscomm1.output = Incept ///为了引起大家注意
'这地方就奇怪了,如果这里我在END SELECT之后加'收发缓存情0,Incept就变成了字符串
EB90C1A0/A168090000833C3333A2A316(中间8位不见了,尝试加长输入16进制长度,每次都是隔8位才输出)
如果把收发缓存清0命令去掉,串口调试出来也正确,已经转成了我要的字符串,更奇怪的在下面。
'++++++++++++++++++++++++++
'下面主要是对已经转成了我要的字符串Incept进行操作了
If Mid(Incept, 7, 2) = "A0" Then
Text6.Text = “通讯建立”
Shape1.BackColor=RGB(0,255,0)
Else if Mid(Incept, 7, 2) = "A1" Then
Text6.Text = “开始接受采样”
//////////Mscomm1.output = Incept ’这个地方我又用串口调试助手看了下此时的Incept(这句话运行时,上面的那句///
Mscomm1.output = Incept 是被注释掉的),居然变成了EB90C1A0/A168090000!!!只留下了前8位,后面的都没了???这是怎么一回事???
End If
End Select
MSComm1.InBufferCount=0
MSComm1.OutBufferCount=0 '收发缓存清0 (执行///时,我全注释掉了)
//////////Mscomm1.output = Incept '最奇怪的地方来了,我在这个地方看下INCEPT值,前2个///注释掉,居然串口调试助手接收栏不停的往出刷新,停都停不下来,并且数据全是最后几位3333A2A316的重复
End Sub
求高人指点这是为什么???能不能给讲解一下这是什么原因?
令求高人指点,单字节校验和好算,我这个采样数据分的高位和低位的,这个校验和怎么计算啊???
还是采用转10进制,再转回16进制字符与校检位比较吗?怎么个比较?还请高人能给个校检的模板出来。。。 有可能是串口数据传输太快,RThreshold触发CommEvent事件的时候串口缓冲区已经被被后续数据刷掉了一部分,我做一个通讯分析的时候也遇到过类似问题,建议把RThreshold设置大一些
我的一部分代码供你参考下:
Function OpenComPort(ByVal mPort As String, Optional ByVal PORTSETTING As String = "9600,N,8,1") As Boolean
On Error GoTo ErrHandle
Call NoErrClosePort
OpenComPort = True
MSCommRead.CommPort = mPort
MSCommRead.Settings = PORTSETTING
MSCommRead.InputMode = comInputModeBinary
MSCommRead.InBufferSize = 1024
MSCommRead.RThreshold = 128 '足够大的触发阀值
If MSCommRead.PortOpen = False Then
MSCommRead.PortOpen = True
End If
Exit Function
ErrHandle:
OpenComPort = False
mErrStr = err.Description
err.Clear
End Function
Private Sub MSCommRead_OnComm()
On Error Resume Next
Dim Buffer As Variant, i As Integer, j As Integer
Dim vStr As String
With MSCommRead
Select Case .CommEvent
Case comEvReceive
j = .InBufferCount
If j > 0 Then
.RThreshold = 0 '停止接收
vStr = ""
lblRecv.Caption = "接收数据 (" & j & " 字节)"
Buffer = .Input
Call SendDataToBoard(gWritePData)
For i = 0 To j - 1
vStr = vStr & Hex(Buffer(i)) & " "
Next
vStr = Trim(vStr)
Call MainProcess(vStr)
End If
tmrShowErr.Enabled = False
.RThreshold = 128
Case comEventBreak
'Debug.Print "break"
Case comEventFrame
'Debug.Print "frame"
End Select
End With
End Sub
另外没太看明白你描述的高低位效验,COMM控件用binary形式接收的数据可以简单的用CStr(Hex(Buffer(i)))来变成字符串,再变回去用Val("&H" & Str)即可,例如Val("&HFF"),拆分一个16bit的高低位和合并回来也很简单
'获取高位
Public Function HiByte(a As Long)
Dim b As Long
b = a And &HFF00
b = b / 256
If b < 0 Then b = b + 256
HiByte = b
End Function
'获取低位
Public Function LowByte(a As Long)
Dim b As Long
b = a And &HFF
LowByte = b
End Function
'2个8Bit高低位合并回16Bit
Function MakeWord(ByVal bLow As Long, ByVal bHigh As Long) As Long
MakeWord = bLow + bHigh * 256
End Function 激动啊,终于有大侠出现了,你的这段:
Private Sub MSCommRead_OnComm()
On Error Resume Next
Dim Buffer As Variant, i As Integer, j As Integer
Dim vStr As String
With MSCommRead
Select Case .CommEvent
Case comEvReceive
j = .InBufferCount
If j > 0 Then
.RThreshold = 0 '停止接收
vStr = ""
lblRecv.Caption = "接收数据 (" & j & " 字节)"
Buffer = .Input
Call SendDataToBoard(gWritePData)
For i = 0 To j - 1
vStr = vStr & Hex(Buffer(i)) & " "
Next
vStr = Trim(vStr)
Call MainProcess(vStr)
End If
tmrShowErr.Enabled = False
.RThreshold = 128
Case comEventBreak
'Debug.Print "break"
Case comEventFrame
'Debug.Print "frame"
End Select
End With
End Sub
没看明白,请大侠可以在我的基础上帮看看哪里出的错误码? 高低校验是这样的,下面用的12位的TLC5234 A/D芯片,发上来的是&H 00 YY(高8位) CC XX(低8位)
我的想法是这样的,如果拿到的INCEPT是正确的,那么我处理INCEPT里的采样信息(已全是字符串),
利用MID函数,分别把每一组&H 00 YY CC XX都取出来,
是不是我得再用VAL转成10进制,然后VAL(YY)*4096+VAL(CC)*256+VAL(XX),然后再把这个和转成字符串,之后用RIGHT(,2)取最后2位呢?这样得到的字符串直接就可以喝INCEPT的最后2位进行比较了吗? 不需要把缓存区清空,即InBufferCount=0不需要,这样在高速收发的时候反而会导致奇怪问题,你应该在触发comEvReceive事件后设置.RThreshold = 0,令串口暂停触发这个事件,然后再用.Input收回缓冲区里的数据即可,缓冲区会在你调用.Input后自动清空,如果你强制来一句InBufferCount=0反而可能破坏了缓冲区里还没来得及被.Input输出的数据。 按您的意思,也就是说我的程序这么改。
Private Sub Form_Load()
MSComm1.Settings = "9600,e,8,1" (这地方一直是9600,我写错了)
MSComm1.InBufferSize = 1024
MSComm1.OutBufferSize = 512
MSComm1.InputMode = comInputModeBinary
MSComm1.RThreshold = 1 '1 or 18 (这地方的1得改吗?下位机发上来的不是定长,命令只有8个字节,而采样数据有40个字节)
MSComm1.PortOpen = True
End Sub
Private Sub MSComm1_OnComm()
Dim buffer() As variant
Dim i As Integer
Dim Incept_1 As string
Dim Incept As string
Select Case MSComm1.CommEvent
Case comEvReceive
/////////MSCOMM1.RThreshold = 0 (是在这里加这个吗?)
buffer = MSComm1.Input
For i = LBound(buffer) To UBound(buffer)
If Len(Hex(buffer(i))) = 1 Then
Incept_1 = Incept_1 & "0" + Hex(buffer(i)) & Chr32
Else
Incept_1 = Incept_1 & Hex(buffer(i)) & Chr32
End If
Next i
Incept=Incept_1
Incept_1=""
//////// Mscomm1.output = Incept ///引起大家注意
'++++++++++++++++++++++++++
'下面主要是对已经转成了我要的字符串Incept进行操作了
If Mid(Incept, 7, 2) = "A0" Then
Text6.Text = “通讯建立”
Shape1.BackColor=RGB(0,255,0)
Else if Mid(Incept, 7, 2) = "A1" Then
Text6.Text = “开始接受采样”
//////////Mscomm1.output = Incept
End If
End Select
MSComm1.InBufferCount=0
MSComm1.OutBufferCount=0 '收发缓存情0
//////////Mscomm1.output = Incept '那这个地方怎么解释?为什么串口助手再不断的刷新呢?加了.RThreshold = 0就可以了吗?
望赐教 补充,处理完后记得恢复.RThreshold = n,另外你的下位机数据收发是基于指令形式的还是自动间隔发送的?如果是自动间隔发送,那你还得考虑收发数据的间隙问题,即数据可能在缓冲区里并不是一段恰好的头尾,而是混杂着其他段落信息,这样你还得先进行数据剥离操作取出完整一段才能进行处理 串口助手和CommMonitor等程序的数据刷新的确很快而且不会漏收数据,这点我也发现了,大概因为它只需要触发就显示的缘故,并不需要其他后续处理,这样响应中断的速度自然快。
但是实际上COMM控件并无法实现足够高的响应触发速度,也就是设置一个字节触发的时候,可能缓冲区里已经有了一堆数据了,然后才会触发comEvReceive,既然这样,倒不如设置一个足够大的RT,等缓冲区里我们需要的数据足够后再抛出来供程序处理,你的RT可以控制在能完整收完一段数据再多一点点冗余的字节数上,这个具体要你去数了。
Private Sub Form_Load()
MSComm1.Settings = "9600,e,8,1" (这地方一直是9600,我写错了)
MSComm1.InBufferSize = 1024
MSComm1.OutBufferSize = 512
MSComm1.InputMode = comInputModeBinary
MSComm1.RThreshold = 56 '40+8=48,48+8=56,试试看
MSComm1.PortOpen = True
End Sub
Private Sub MSComm1_OnComm()
Dim buffer() As variant
Dim i As Integer
Dim Incept_1 As string
Dim Incept As string
Select Case MSComm1.CommEvent
Case comEvReceive
MSCOMM1.RThreshold = 0 '立即停止触发comEvReceive
buffer = MSComm1.Input
For i = LBound(buffer) To UBound(buffer)
If Len(Hex(buffer(i))) = 1 Then
Incept_1 = Incept_1 & "0" + Hex(buffer(i)) & Chr32
Else
Incept_1 = Incept_1 & Hex(buffer(i)) & Chr32
End If
Next i
Incept=Incept_1
Incept_1=""
MSCOMM1.RThreshold = 56 '恢复事件
//////// Mscomm1.output = Incept ///引起大家注意
'++++++++++++++++++++++++++
'下面主要是对已经转成了我要的字符串Incept进行操作了
If Mid(Incept, 7, 2) = "A0" Then
Text6.Text = “通讯建立”
Shape1.BackColor=RGB(0,255,0)
Else if Mid(Incept, 7, 2) = "A1" Then
Text6.Text = “开始接受采样”
//////////Mscomm1.output = Incept
End If
End Select
'MSComm1.InBufferCount=0
'MSComm1.OutBufferCount=0 '这两个不要 数据剥离操作,就是类似于这样的吧?
68 09 00 00 10 07 00 68 81 06 43 C3 83 3C 33 33 A2 16
If Mid(strData, 1, 2) = "68" And Len(strData) = 36 Then
Text1.Text = strData
Text3.Text = Mid(strData, 3, 2)
strData = ""
End If
End Select
End Sub
我这里做的收发形式是,我给下位一个指令(含标示位),下位收到了校验和,正确了了发
EB 90 C1 01 AA/55(正确AA,错误55) XX(校验和)
我收到信息后,判断有AA就继续运行,55直接报错
非常感谢xiaojin1985的回答,我这会就去单位试试看,出现其他问题我再求助于您。 xiaojin1985在吗?下午去单位试了一下,确实是MSCOMM1.RThreshold设置的问题,经过反复多次试验后,目前出现的新问题如下:
1. MSCOMM1.RThreshold的设置只能设置为单片机发上来的数据长度,比如在我这里,下位机回送命令为7个字节,含有采样信息的命令为38个字节。RThreshold设置大于38则不行,因为我对下位机传上来的每一数据包,都要处理。
2. 根据1所说的,下位机回送命令为7个字节,含有采样信息的命令为38个字节,这样就带来了问题,如果RT设置为38则不能对7字节命令实时处理,7则不能对38处理。
希望xiaojin1985能给个办法解决这个问题,目的就是自动判断下面送上来的数据长度,意思就是下面这个:
if length=7 RT=7
Else if length=38 RT=38
如何才能实现这个功能,能给个框架出来最好
补充:VB , 非技术类