大家好,我用VB的MSComm控件做的上位机,下位机是一片单片机,PC串口上接一个232转485的转换器与单片机通讯,单片机端用的MAX487芯片。用二进制收发。
1、现在PC给单片机发送数据后,单片机内必须延迟100多ms,再向PC回送数据,PC才能将数据收全,否则接收数据错误。
2、在PC的串口上,将串口的收发线直接短接,就是说用串口自发自收,没问题,串口上加上485 转换器后,就必须在单片机内做延时了
3、如果单片机向PC回送几十个数据,前面的数据错误,从开始正确的数据 开始,往其后的数据都是正确的。比如单片机延时50ms,PC收到的数据前10个不正确,后面的几十个数据都正确
 如延时70ms,PC收到的数据前7个不正确,后面的都正确。
 如不延时,会有近20个数据不正确。
4、用串口调试助手,则不存在需延时的问题。
请教一下这问题怎么解决,先谢了

解决方案 »

  1.   

    没有看到你的接收代码和通信协议,很难提供意见.
    RS485口通常是单工的,收和发需分开进行.
    PC机侧的VB程序及COM口是双工的.收和发可同时进行.PC机侧的命令间需有适当的时间间隔,以空出等待单片机返回
    命令.PC机侧完全可利用MsComm1_OnComm事件触发来实现接收,设置MSComm1.RThreshold = 1或返回命令的字节
    长度来控制接收.PC机侧的命令间适当的时间间隔可根据波特率,接收字节长计算由TIMER计时器控制.
      

  2.   

    zdingyun 您好:
       我现在是另建了一个项目,只是串口收发的,共有一个窗口,一个发送用按键。
    接收用两种方法试过:1、发送完毕开启一个定时器,延时后,在定时器事件里查询接收
      用:          Do
                        DoEvents
                 Loop Until MSComm1.InBufferCount >= 1 
    2、在MSComm_OnComm事件里接收
        Select Case MSComm1.CommEvent    Case comEvReceive
            ReDim out(57) As Byte
            Do
                        DoEvents
                 Loop Until MSComm1.InBufferCount >= 1 '循环等待接收缓冲区>=1个字节
            Var = MSComm1.Input
            buffer = Var
            out = buffer
      
    End Select
      这是接收事件里的全部代码,做这个只为测试用 窗口中全部代码如下:
      MSComm1.CommPort = 1 '利用串口COM1进行通讯MSComm1.InputLen = 0 '每次读取接收缓冲区的1个字节
    MSComm1.OutBufferSize = 512 '设置发送缓冲区为512字节
    MSComm1.InBufferSize = 512 '设置接收缓冲区为2048字节
    MSComm1.OutBufferCount = 0 '清除发送缓冲区
    MSComm1.InBufferCount = 0 '清除接收缓冲区
    MSComm1.InputMode = comInputModeBinary '数据传输设置为二进制格式
    MSComm1.RThreshold = 1   '一次性接收起始码1个字节'可触发On_CommMSComm1.Settings = "2400,N,8,1" '设置波特率、校验位(1)、数据位、停止位
        MSComm1.PortOpen = True '打开通信口COM1 按键事件中所有代码如下:
    Private Sub Command1_Click()
    '手动发送键Dim DatTempBuf(30) As ByteDim OutDatV As VariantMSComm1.OutBufferCount = 0 '清除发送缓冲区MSComm1.OutBufferCount = 0  '清除发送缓冲区MSComm1.OutBufferCount = 0
     If MSComm1.PortOpen = False Then
      MsgBox "串口未打开", 52, "提示"
      
      Exit Sub
     End If
     'DatTempBuf(0) = &HFE
     'DatTempBuf(1) = &HFE
     'DatTempBuf(2) = &HFE
    ' DatTempBuf(3) = &HFE
    ' DatTempBuf(4) = &HFE
    ' DatTempBuf(5) = &HFE
    ' DatTempBuf(6) = &HFE
     ' DatTempBuf(7) = &HE
     ' DatTempBuf(8) = &HE
     ' DatTempBuf(9) = &HE
     ' DatTempBuf(10) = &HE
     'DatTempBuf(0) = &HFE
     ' DatTempBuf(0) = &HFE
      DatTempBuf(0) = &HFF
      DatTempBuf(1) = &H55
      DatTempBuf(2) = &HAA
      DatTempBuf(3) = &H4
      DatTempBuf(4) = &H0
      DatTempBuf(5) = &H3
      DatTempBuf(6) = &H7
      
      MSComm1.Output = DatTempBuf
    Do
    DoEvents '转交控制权
    Loop Until MSComm1.OutBufferCount = 0 '发送缓冲区清空
    MSComm1.InBufferCount = 0 '清除接收缓冲区
    MSComm1.RThreshold = 1 '所要接收的数据长度
    End Sub
      

  3.   

    有一点想不明白,将PC的串口的2、3脚也就是接收发送脚直接短接,相当于无间隔的自发自收,用上面的代码,接收都没问题。
    加上232-485转换器,并下位机用单片机后,就不行,
    用串口调试助手接单片机,单片机发送数据前也不用延时,也能很好接收单片机发来的数据。
    照现象看,很像MSComm控件和485转换器的配合问题,但转换器我用了两个试过都一样,其中一个是波士电子的,说是零延时且不用任何控制端的。
    但我测试是发现,发送前加上 MSComm1.RTSEnable = False ,发送后就是在Do-Loop语句后加上 MSComm1.RTSEnable = True  会使接收正确的数据多一个字节。望指教
      

  4.   

    试下下列代码:
    Option Explicit
        'Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
        Dim BytReceived() As Byte
        Dim strData As String
    Private Sub Form_Load()
        MSComm1.CommPort = 1 '利用串口COM1进行通讯
        MSComm1.InputLen = 0 '每次读取接收缓冲区的1个字节
        MSComm1.OutBufferSize = 512 '设置发送缓冲区为512字节
        MSComm1.InBufferSize = 512 '设置接收缓冲区为2048字节
        MSComm1.OutBufferCount = 0 '清除发送缓冲区
        MSComm1.InBufferCount = 0 '清除接收缓冲区
        MSComm1.InputMode = comInputModeBinary '数据传输设置为二进制格式
        MSComm1.RThreshold = 1   '一次性接收起始码1个字节'可触发On_Comm
        MSComm1.Settings = "2400,N,8,1" '设置波特率、校验位(1)、数据位、停止位
        MSComm1.PortOpen = True '打开通信口COM1
        Text1 = ""
    End SubPrivate Sub Command1_Click()
        '手动发送键
        Dim DatTempBuf(30) As Byte '发送字节长31
        Dim OutDatV As Variant
        MSComm1.OutBufferCount = 0 '清除发送缓冲区
        MSComm1.OutBufferCount = 0  '清除发送缓冲区MSComm1.OutBufferCount = 0
        If MSComm1.PortOpen = False Then
            MsgBox "串口未打开", 52, "提示"
            Exit Sub
        End If
        DatTempBuf(0) = &HFF
        DatTempBuf(1) = &H55
        DatTempBuf(2) = &HAA
        DatTempBuf(3) = &H4
        DatTempBuf(4) = &H0
        DatTempBuf(5) = &H3
        DatTempBuf(6) = &H7
        MSComm1.Output = DatTempBuf
        MSComm1.InBufferCount = 0 '清除接收缓冲区
        MSComm1.RThreshold = 1 '所要接收的数据长度
    End SubPrivate Sub MSComm1_OnComm() '接收数据
        Dim strBuff As String
        Dim i As Integer
        Select Case MSComm1.CommEvent
            Case 2
                MSComm1.InputLen = 0
                strBuff = MSComm1.Input
                BytReceived() = strBuff
                For i = 0 To UBound(BytReceived) '接收数据处理为16进制
                    If Len(Hex(BytReceived(i))) = 1 Then
                        strData = strData & "0" & Hex(BytReceived(i))
                    Else
                        strData = strData & Hex(BytReceived(i))
                    End If
                Next
                If Len(strData) = 60 Then
                    Text1 = strData '数据处理
                    strData = ""
                End If
        End Select
    End Sub
      

  5.   

    最近做一个报警盒软件时,也遇到过这个问题.报警盒采用被动回码的方式与计算机通讯,且发码查询间隔时间要在150ms以上,开始用VB6的MSCOMM控件做,效果不太理想,因为存在一个DOEVENTS和定时器的配合,如果时间控制不好,DOEVENTS会造成代码重入,从而数据收发发生错误.后改为用.NET+API实现,较好的解决了此问题.
    主要看你的单片机是如何与计算机通讯的,而且485为半双工,则计算机要在单片机回码后再发送,故计算机发码要有间隔或者判断,否则会导致数据错误.不清楚你的单片机如何工作的,所以不好给什么建议了.
      

  6.   

    TO it_sy_boy:
       现在的方式就是一次性读取,问题是前面的数据错误。调试时我是看的变量里的数据,所以应不是重复读取的问题
    TO zdingyun:
       刚将您的代码试过,效果与之前的相同,感觉不是读取方式的问题,很象是MSComm控件控制485转换器没有控制好,希望能继续提供解决方法,我在这边试试TO Arnold_fly:
       我这边单片机也是被动回码的方式,PC发数据,单片机接收到之后,再根据收到的命令,将一些数据回传给PC.
       现在测试的时候是PC单击按键发送一次,单片机回传数据一次,这样的情况都出错,必须要在单片机接收完PC数据后,延时100多ms再向PC上传数据才行。
      单片机程序现在只有接收中断,和发送函数,发送是用的查询发送标志位来做的。  你的150ms是PC端向下发命令的间隔吧。
      你用MSComm控件时,单片机收到数据立即回传可不可以呢?
      

  7.   

    TO lmjqul
    请问你单片机的通信口是RS485还是RS232?
    前贴已说明RS485口通常是单工的,收和发需分开进行.
    你经RS232转RS485口接单片机,单片机需接收到完整命令后,并确认无误后才能返回数据,单片机需接收到完整命令的过程本身需时间,约125MS。
    我贴的代码中Sleep函数或许能起作用:    
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    你可在MSComm1_OnComm事件代码中的
    Case 2句后加一句:
    Sleep (150)
    再试整个代码,看看有改观吗?
      

  8.   

    如果是被动回码的方式,可以参考我以前用MSCOMM做的程序:
        采用两个定时器Timer1和Timer2
    Private Sub Timer1_timer()
       函数A     '利用定时器循环调用函数A
        ....     '根据函数返回的数据进行处理或者显示   
    End SubPrivate Sub Timer2_timer()
       OverTime = true
    End SubPrivate Function 函数A(ByRef 返回值)
       '发送报警查询命令
        FlagIn = False
        Timer2.Interval = 150
        Do
          If OverTime Then
              Exit Do
          End If
          DoEvents
       Loop Until FlagIn
       Timer2.Interval = 0
       If FlagIn = True Then                     
           '对接受到的数据进行校验,若通过则返回正确的数据
       End If
    End FunctionPrivate Sub MSComm1_OnComm()    Select Case Me.MSComm1.CommEvent
            Case comEvReceive                '发生Oncomm事件
               DataInB = MSComm1.Input
                 '判断帧头,正确则
                 FlagIn = True
    End Sub
    用这种方式,比起你的代码少用了一次DOEVENTS,但是当有多个定时器时,这种方式也会出问题,因为必须响应一个ONCOMM事件,这样就必须跳出循环,从而造成控制权移交,难以控制.
    如果你的和我的一样,只被动回码,建议用API.
      

  9.   

    TO zdingyng:
     单片机上是485接口 
     我说的100多ms,是指单片机收到完整的命令后,要发送的时候,之前要另加100ms的延时。现在单片机程序只有接收和发送部分,接收用中断,发送用函数,查询发送标志的方式,发送时是连续的。单片机端由接收切换到发送的代码,放在100ms之前还是之后都对结果无影响。之前也试过,基本排除单片机切换收发所造成的影响。刚试过,加Sleep(150) 甚至200后,现象依旧。
      

  10.   

    还有两个变量声明:
       Public FlagIn As Boolean               '标识接收到数据了
       Public OverTime As Boolean             '标识到达间隔时间,可以发送下一条命令
      

  11.   

    TO Arnold_fly:
      刚试过,效果与之前的相同。你是否也用的232转485的转换器呢?
     现在分不清是哪部分的问题,PC串口接485转换器,下位机用单片机,单片机内不用延时,收到完整命令后,直接回传数据即可,用串口调试助手返回的数据是正确的。说明485转换器到单片机部分一般没问题
      串口短接后,用MSComm控件也没问题,完全可以立即自发自收,说明控件方面的程序应无问题。
     就用控件和485这块配合不好,必须要延时才行,真有些搞不明白。
      

  12.   

    LZ:
    PC串口上接一个232转485的转换器与单片机通讯你上述的做法,将PC侧的COM口变成单工的RS485标准,是否回导致不能及时转换收发状态.我的浅显理解单片机上是485接口 ,应使用RS485转RS232转换器与PC连接(硬件不太了解).
      

  13.   


     问题找到,呵呵
     向两位反馈一下结果:
       原因是上述程序发送的数据我用固定数组做的,所以预留了些位置,数组长31个字节,呵呵。
     我在所用的程序中也是类似的情况,我是做了个整理数据的子函数,存放数据的数组也是用的固定数组,长度20,所以造成这个现象。这次调试过程中对有些调试的表现太疏忽,也造成了我描述的不清,这个子函数是我以前做的,能想到这方面也是受了二位的启发,再次谢谢两位了,呵呵 现在还有个现象,单片机回送的数据少第一个字节,而波士电子的资料他们的232485 zhu an  sad  
      

  14.   


     刚打不了字,接上:
      现在还有个现象,单片机回送的数据少第一个字节,而波士电子的资料说他们的RS232转RS485的转换器是零延时的,
     PC机往下发的数据单片机全部能收到。这种现象你们是怎么处理的呢?TO zdingyun:
      我原来也是把注意力放在这方面,看现象确实象这方面问题,呵呵,我的PC串口出来是外接一个RS232转RS485的转换器的,成品的那种。单片机端是用的MAX487芯片做转换。
      

  15.   

    如果是定长31字节,设MSComm1.RThreshold = 31,接收时先判断帧头,通过后再进行校验和校验
      

  16.   


    TO Arnold_fly:
     刚刷新后才看到您的帖子,呵呵。
      PC接收的字节是不定长的,短的不到10个字节,长的300个字节左右。
     我现在用的方法是MSComm1.RThreshold = 1,发生事件后,在事件内将MSComm1.RThreshold = 0 ‘,再循环查询的方式。每次读取是全部读取PC缓冲区的内容,再把这些数据按你说的进行判断帧头和效验。  现在看效果还不错,等再仔细调试一下,看看是否有问题。:)
      
      

  17.   


    windows xp + vb6.0我要发送的 数据是  0a 03 00 64 00 01 c4 ae'Dim DatTempBuf(30) As Byte  '不能接收到正确的数据Dim DatTempBuf(7) As Byte  '改为  DatTempBuf(7)  就能接收到正确的数据 请教原因!
      

  18.   

    请教:不能实现手动发送2个数据包  接收到2个数据包;如何实现 对多个单片机的轮询?
      
      
    代码如下:'手动发送键'Dim DatTempBuf(30) As Byte  '不能接收到正确的数据Dim DatTempBuf(7) As Byte  '改为  DatTempBuf(7)  能接收到正确的数据  高兴Dim OutDatV As VariantMSComm1.OutBufferCount = 0 '清除发送缓冲区MSComm1.OutBufferCount = 0  '清除发送缓冲区MSComm1.OutBufferCount = 0
    If MSComm1.PortOpen = False Then
      MsgBox "串口未打开", 52, "提示"
      
      Exit Sub
    End If    '0A 03 F0 01 00 01 e7 b1  向下位机发送 数据一
      
      DatTempBuf(0) = &HA
      DatTempBuf(1) = &H3
      
      DatTempBuf(2) = &HF0
      DatTempBuf(3) = &H1
      
      DatTempBuf(4) = &H0
      DatTempBuf(5) = &H1
     
      DatTempBuf(6) = &HE7
      DatTempBuf(7) = &HB1
      
      
    Do
    DoEvents '转交控制权
    Loop Until MSComm1.OutBufferCount = 0 '发送缓冲区清空
    MSComm1.InBufferCount = 0 '清除接收缓冲区
    MSComm1.RThreshold = 1 '所要接收的数据长度
    Sleep (1000)
    '不能用上面的代码实现手动发送2个数据包 接收到2个数据包
    ''''''''''''''''''''''''''''''''''''''''
       '0A 03 00 01 00 05 d5 72  向下位机发送 数据二
     
     
      DatTempBuf(0) = &HA
      DatTempBuf(1) = &H3
      
      DatTempBuf(2) = &H0
      DatTempBuf(3) = &H1
      
      DatTempBuf(4) = &H0
      DatTempBuf(5) = &H5
      
      DatTempBuf(6) = &HD5
      DatTempBuf(7) = &H72
     
      
      MSComm1.Output = DatTempBuf
      
      
    Do
    DoEvents '转交控制权
    Loop Until MSComm1.OutBufferCount = 0 '发送缓冲区清空
    MSComm1.InBufferCount = 0 '清除接收缓冲区
    MSComm1.RThreshold = 1 '所要接收的数据长度''在 Private Sub MSComm1_OnComm() '接收数据
    ''不能接收到 下位机 对 0A 03 F0 01 00 01 e7 b1 数据一  返回值;只能接收到 '0A 03 00 01 00 05 d5 72  向下位机发送 数据二  用上面的代码实现手动发送2个数据包 接收到2个数据包
      

  19.   

    问题解决啦

    DatTempBuf(6) = &HE7 
    DatTempBuf(7) = &HB1 
    后加
    MSComm1.Output = DatTempBuf 能接收到 下位机 对 0A 03 F0 01 00 01 e7 b1 数据一  返回值;能接收到 0A 03 00 01 00 05 d5 72  向下位机发送 数据二  用上面的代码实现手动发送2个数据包 接收到2个数据包