我写了一个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 SubPrivate 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进制字符与校检位比较吗?怎么个比较?还请高人能给个校检的模板出来

解决方案 »

  1.   

    有可能是串口数据传输太快,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 FunctionPrivate 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
      

  2.   

    另外没太看明白你描述的高低位效验,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 = bEnd Function'获取低位
    Public Function LowByte(a As Long)  Dim b As Long    b = a And &HFF
        LowByte = bEnd Function'2个8Bit高低位合并回16Bit
    Function MakeWord(ByVal bLow As Long, ByVal bHigh As Long) As Long    MakeWord = bLow + bHigh * 256End Function
      

  3.   

    激动啊,终于有大侠出现了,你的这段:
    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
    没看明白,请大侠可以在我的基础上帮看看哪里出的错误码?
      

  4.   

    高低校验是这样的,下面用的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位进行比较了吗?
      

  5.   

    不需要把缓存区清空,即InBufferCount=0不需要,这样在高速收发的时候反而会导致奇怪问题,你应该在触发comEvReceive事件后设置.RThreshold = 0,令串口暂停触发这个事件,然后再用.Input收回缓冲区里的数据即可,缓冲区会在你调用.Input后自动清空,如果你强制来一句InBufferCount=0反而可能破坏了缓冲区里还没来得及被.Input输出的数据。
      

  6.   

    按您的意思,也就是说我的程序这么改。
    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 SubPrivate 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就可以了吗?望赐教
      

  7.   

    补充,处理完后记得恢复.RThreshold = n,另外你的下位机数据收发是基于指令形式的还是自动间隔发送的?如果是自动间隔发送,那你还得考虑收发数据的间隙问题,即数据可能在缓冲区里并不是一段恰好的头尾,而是混杂着其他段落信息,这样你还得先进行数据剥离操作取出完整一段才能进行处理
      

  8.   

    串口助手和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 SubPrivate 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 '这两个不要
      

  9.   

    数据剥离操作,就是类似于这样的吧?
    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直接报错
      

  10.   

    非常感谢xiaojin1985的回答,我这会就去单位试试看,出现其他问题我再求助于您。
      

  11.   

    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
    如何才能实现这个功能,能给个框架出来最好