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

(求助高人)关于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 ,  非技术类
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,