问这个问题已经是第二次,上次是在单片机板块问,上个月,大约20人回答,都没法解。
这次我要讲得更清楚点。
程序的要求是和一个单片机管理的LCD通讯,通过串口发给它35字节长度的各种报文,接受到“OK”字符串表示成功,接收到“ER”字符串表示错误。
单片机的程序和硬件设计由另一人负责,其实我也不懂那一块。
我用VB编写PC机的软件。
最初我的设计很顺利,单片机串口接线是RS232标准,我用VB里的Form加一个MSCOMM控件轻松完成上面的通讯要求。通讯设置是4800,e,8,1
可是上个月,硬件更换了,接线是485标准,通过转232的接头接到PC机上,其他不变。
却发现我的软件程序无法正确收到"OK"的回应,而只是收到“K”或者“KK”的回应,非常不稳定。
而一个叫“串口调试助手”的著名的程序却是发送接受都很正常,与硬件结合很好。
于是开始了一个月繁杂的调试。
主要是因为我对串口通讯不熟悉,很多参数都是照网上画瓢。
下面是我的一个简单发送HELO呼叫报文的测试程序
HELO呼叫报文的要求是35字节长度,第一字节是128,第二字节是1,剩余的字节都是0。
单片机那边收到HELO呼叫,应该返回“OK”的回应。代码(精简的):
Private Packet(35) as Byte '当前要发送的包
Private instrBuffer As String '接收缓冲区,接收到单片机的回应码'一个发送按钮
Private Sub cmdSend_Click()
Packet(0) = 128
Packet(1) = 1
Packet(2) = 0
Packet(3) = 0
If ComTest.PortOpen = False Then 
ComTest.PortOpen = True
End If
ComTest.InputMode = comInputModeText
ComTest.Output = Packet
end Sub'MSCOMM控件ComTest的OnComm事件
Private Sub ComTest_OnComm()
instrBuffer = ""  '清空一下
txtInputCount.Text = CStr(ComTest.InBufferCount) '将InBufferCount显示到一个Text里面
instrBuffer = ComTest.Input     'COM的输入直接传给字符串
txtReceive.Text = txtReceive.Text + instrBuffer '将字符串显示在一个Text里面
End SubMSCOMM控件的设置是
.InputMode=comInputModeText
.RThreshold=1
.SThreshold=0
.Inputlen=40 '考虑到接收回慢些
.InBufferSize=1024
.OutBufferSize=512
.Settings="4800,e,8,1"
其余都是默认。可是收到回应的只有一个"K"。
如果把RThreshold=2,那么发送两次才收到一个"KK"。今天下午又写了一个VC的程序,也是和上面的要求一样,调用MSCOMM控件,
竟然可以正确的收到“OK”
实在是不明白。
还有,我们又试着将回应码改成了单字符,即 正确返回是“K”,错误返回是“R”
我的VB测试程序收到的是?
而VC程序仍然正常收到“K”
可以肯定是我的VB程序的思路错了
这些VB代码仅仅是工程里的小部分,现在老板催得急,不可能重新用VC编写工程,而且我的VC语言很弱。
现在我请朋友帮忙,让他用VC写一个lib文件,调用MSCOMM控件的发送和接收,然后我再到VB代码里调用他写的lib里的函数来控制MSCOMM控件。
很失望,很迷茫,很不甘心。
我希望各位能给我指点一下迷津,帮我在VB里面找出错误来。
为什么我只能收到“K”?
为什么同样是MSCOMM控件,VC的程序就能正常收到“OK”?
我真的必须调用VC代码才能控制MSCOMM控件吗?

解决方案 »

  1.   

    你的ComTest_OnComm事件中的代码有问题,ComTest.Input这个是读出并清空,不要用两次.Private Sub ComTest_OnComm()
    instrBuffer = ""  '清空一下instrBuffer = ComTest.Input 
    txtInputCount.Text = instrBuffer 
    txtReceive.Text = txtReceive.Text & instrBuffer '将字符串显示在一个Text里面
    End Sub
      

  2.   

    首先请确认VB 和 VC 中控件的设置是否真的完全一样.
    再次比较两者的逻辑是否一样.
    一点见解.
      

  3.   

    to tztz520(午夜逛街) 
    我的原来的大工程里的程序代码没加
     txtInputCount.Text = CStr(ComTest.InBufferCount) 
    一样是收到K。to wz19761022
    VB 和 VC通用MSCOMM控件,这是在编程平台里能找到的。
      

  4.   

    Private Sub ComTest_OnComm()
    instrBuffer = ""  '清空一下
    Select Case MSComm1.CommEvent
        Case comEvReceive
            instrBuffer = ComTest.Input 
            txtInputCount.Text = instrBuffer 
            txtReceive.Text = txtReceive.Text & instrBuffer '将字符串显示在一个Text里面
        Case comEventRxOver
            '接 收 缓 冲 区 溢 出
            ComTest.InBufferCount = 0
            instrBuffer = ComTest.Input
            Debug.Print "接 收 缓 冲 区 溢 出"
         Case comEventTxFull
            '发 送 缓 冲 区 溢 出。
            ComTest.OutBufferCount = 0
            instrBuffer = ComTest.Input
            Debug.Print "发 送 缓 冲 区 溢 出"
         Case comEventBreak
            Debug.Print "接收到一个中断信号"
            instrBuffer = ComTest.Input
    end select
    End Sub
    '你试一下看会打印出什么东西出来
      

  5.   

    VB可中的文本方式是两个字节的,所以OK会变成K,而前面这个字节会被VB进行转换时吞掉。可以用字节数组来接收
        dim a() as byte
        a=mscomm1.input    'a(0)=asc('o')
        'a(1)=asc('k')
      

  6.   

    MSCOMM 测试中是最稳定的, 我们公司的系统,是7 * 24 小时运行的, rs232, 485都有,测试过好几个控件,包括自己直接用api,感觉还是 mscomm 最稳定!
      

  7.   

    应该不属于VB默认编码(Unicode)方式而引起的
    关注
      

  8.   

    to  wz19761022(柱子) 
    单片机部分都是以16进制收发数据,
    有什么不对吗?to  shangfc(指南针) 
    见过byte数组作接收的吗?我没用过,好像这样会出现类型错误的提示!!!
      

  9.   

    不会呀!我用了都没有有问题
    楼主看一下是不是MSCOM的设置有问题,我们都是用的二进制方式接收的
    email:[email protected]
    可以和楼主交流一下
      

  10.   

    试试:
    .InputMode = comInputModeBinary
    用文本方式接收的话,出现高位为1的字符,下一个字符就被吃掉了,接收的时候用字节
    Dim Rec() as Byte
    Recv = .Input
    如果接收的内容包含多余的东西,找单片机的编制者就好了
      

  11.   

    如果下位机是按16进只发送的那接受和发送一定要使用comInputModeBinary 方式.否则会丢失数据的我以前遇到过.楼上的说的对:设置空间的InputMode = comInputModeBinary,按Byte的方式处理接受肯定没错.
      

  12.   

    to AllCHN(大中国):
    你说的:
    .InputMode = comInputModeBinary
    用文本方式接收的话,出现高位为1的字符,下一个字符就被吃掉了,接收的时候用字节
    Dim Rec() as Byte
    Recv = .Input昨天我又进一步比较了我的VB程序和“串口调试助手”的区别
    我发现SThreshold=0和SThreshold=1也有关系,究竟该设置哪一个呢?
    还有就是InputMode的选择,VC的程序都用Binary接收,而我的VB程序都是Text接收
    变成Binary形式真的能行吗?
    那么我的接受代码该怎么写呢?
    如下:
    Dim Rec(2) as Byte
    Rec=ComTest.Input
    是吗?
    我在顶层的帖子上说了:单片要收到35字节的报文才能返回返回“OK”或者“ER”
    我做了一个假设
    单片机的汇编程序接受了头两个字节的数据,比对第一字节=128,第二字节=1,就返回OK
    而这时剩余的33字节还完全传送完毕,这样返回字OK就和剩余的发送字节冲突了,造成了丢失?
    那这种丢失为什么偏偏少了头字节呢?
    还有我们修改了返回字成为K,竟然收到“?”问号--表示奇偶校验错误,为什么?也用上面的假设解释不清楚。还请帮我分析一下。
      

  13.   

    看你说的,一定要使用二进直方式接发,出现'?'正常,你接到的是位数据,要转换成字副才性.InputMode 的值不同接收到的数据并不在任何时候都一样,只有用二进直方式是最保险的.找本这方面的资料好好研读一下.
      

  14.   

    的确, 各位大侠说得都有道理, 用二进制的确是最安全的, 但是我想楼主在没弄清楚2进制的具体操作前,盲目修改程序麻烦会更大, 你不用考虑232和485的转换问题, 那些是成熟的产品, 不会有问题的, 我看了一下你的程序, 发现一个可能性:'MSCOMM控件ComTest的OnComm事件
    Private Sub ComTest_OnComm()
    instrBuffer = ""  '清空一下
    .....
    问题就在这个清空一下
    因为你的 MSCOMM控件的设置是
    .InputMode=comInputModeText
    .RThreshold=1也就是说收到一个字符就会产生一个OnComm事件, 
    那么, 在你收到OK的"O"的时候, 你取了并显示在TEXT里,并清了, 接着收到"K",你又取了显示在TEXT里,
    那么后面的"K"是不是就会把前边的"O"给盖掉了成了"K"了呢?
    建议如下:
    Private Sub ComTest_OnComm()
    if ComTest.inBufferCount<2 then 
        Exit sub  '没有两个以上的字符就等待下一次
    else
        instrBuffer = ""  '清空一下
        instrBuffer = ComTest.Input 
        txtInputCount.Text = instrBuffer 
        txtReceive.Text = txtReceive.Text & instrBuffer '将字符串显示在一个Text里面
    endif
    End Sub或则:
    Private Sub ComTest_OnComm()
    dim sngTimer as single
        sngTimer=Timer+1
        Do while sngTimer>Timer     ''等上一秒种, 确保接收完毕
            doevents
        loop    instrBuffer = ""  '清空一下
        instrBuffer = ComTest.Input 
        txtInputCount.Text = instrBuffer 
        txtReceive.Text = txtReceive.Text & instrBuffer '将字符串显示在一个Text里面
    end sub
      

  15.   

    昨天又测试了一下新写的程序
    Option Explicit
    Dim Out() As Byte   '接收var中的值
    Dim var As Variant   '接收MSC.input中的数值
    Dim nRece As Integer   '计算MSC.inputbuffer的个数
    Dim i As Integer, j As Integer  '随即变量,计算循环
    Dim DELAY As SinglePrivate Sub cmdSend_Click() '发送按钮
    Dim packet(35) As Byte
    For i = 0 To 34
    packet(i) = 0
    Next i
    packet(0) = 128
    packet(1) = 1
    DELAY = CSng(txtDelay.Text)
    If Not MSC.PortOpen Then
    MSC.PortOpen = True
    End If
    MSC.Output = packet
    End SubPrivate Sub Form_Load() ‘form加载
        ClearText
        With MSC
            .CommPort = 1  '设置Com1为通信端口
            .Settings = "4800,E,8,1"
            .InBufferSize = 40  '设置缓冲区接收数据为40字节
            .InputLen = 2  '设置Input一次从接收缓冲读取字节数为2
            .RThreshold = 1  '设置接收一个字节就产生OnComm事件
            .SThreshold = 1 '发送完毕就产生OnComm事件
        End With
        DELAY = 0#
        txtDelay.Text = Format(DELAY, "00.00")
        Label1.Caption = Label1.Caption + " " + CStr(MSC.Settings)
    End Sub
    Private Sub ClearText() '清空Text1    
        Text1.Text = ""
    End Sub
    Private Sub Command1_Click() 'reset按钮 
        ClearText
        nRece = 0  '计数器清零
        With MSC
            .InputMode = comInputModeBinary '设置数据接收模式为二进制形式
            .InBufferCount = 0  '清除接收缓冲区
            If Not .PortOpen Then
                .PortOpen = True   '打开通信端口
            End If
        End With
        
    End SubPrivate Sub MSC_OnComm()
        DelayTime   '‘用来延续时间
        ClearText
        With MSC
            Select Case .CommEvent  '判断通信事件
                Case comEvReceive:  '收到Rthreshold个字节产生的接收事件
                    var = Null
                   var = .Input
                    Out = var
                    Text1.Text = Text1.Text + Str(Out(0)) + " " + Str(Out(1))
                    
                    
                   
                Case Else
    '                .PortOpen = False
            End Select    End WithEnd Sub
    Private Sub DelayTime() ’延时函数
        Dim bDT As Boolean
        Dim sPrevious As Single, sLast As Single
        bDT = True
        sPrevious = Timer  '(Timer可以计算从子夜到现在所经过的秒数,在Microsoft Windows中,Timer函数可以返回一秒的小数部分)    Do While bDT
            If Timer - sPrevious >= DELAY Then bDT = False
        Loop
        bDT = True
    End Sub测试结果:
    测试没有延时和多种延时情况下都是收到“75 0”,按 reset按钮后发送,出现对话框“运行时错误‘9’下标越界”,继而关闭。怎么办啊?我觉得写的代码已经使用了二进制接收了阿
    大家帮忙啊
      

  16.   

    .RThreshold = 2接收时去掉延时。
      

  17.   

    请在一开始就设成二进制形式Private Sub Command1_Click() 'reset按钮 
        ClearText
        nRece = 0  '计数器清零
        With MSC
            If Not .PortOpen Then .PortOpen=False
            .PortOpen = True   '打开通信端口
            .InputMode = comInputModeBinary '设置数据接收模式为二进制形式
            .InBufferCount = 0  '清除接收缓冲区
        End With
        
    End SubPrivate Sub MSC_OnComm()
        DelayTime   '‘用来延续时间
        ClearText
        With MSC
            Select Case .CommEvent  '判断通信事件
                Case comEvReceive:  '收到Rthreshold个字节产生的接收事件
                    var = Null
                   Out = .Input
                    for i=lbound(Out) to Ubound(Out)
                     Text1.Text = Text1.Text + chr(Out(i)) + " " 
                    next 
                 
                Case Else
    '                .PortOpen = False
            End Select    End WithEnd Sub
      

  18.   

    多谢vodlinux,借助你昨天给我写的代码
    问题终于解决了,我的程序可以合理的收到返回字“OK”了。
    我犯了一个严重的数组长度的错误,我把VB语言里的数组边界和VC里面的边界混淆了。
    在顶部的问题帖子里面,我有声明语句
    Private Packet(35) as Byte '当前要发送的包
    其实这样是不对的,在vB里面这表示36个元素的Byte数组,数组的游标是0到35
    而MSCOMM1.Output=Packet
    就相当于发送了36字节的helo报文,
    在下位机,汇编代码里计数35字节以后就返回OK,
    于是发送的第36字节和返回字的第一字节“抵触”了
    无论如何也收不到今天在测试的时候,我看vodlinux写给我的代码里面把packet数组定义为
    dim packet(34) as byte,我忽然意识到自己的错误。
    修改了数组的边界,Packet(34),
    inputmode=Text
    inputlen=2
    Rthreshold=1
    Sthreshold=1
    一切问题都搞定了!成功了!
    万岁!实际上,我并没有完全明白,
    因为项目的头两个月用RS232的时候,我也是声明Packet(35),多一个字节,仍可以正常通信,
    如今改了RS485,我的Packet(34)才能收到OK返回字,而Packet(35)就只能收到K
    谁能给再详细解释一下?
      

  19.   

    一段VB代码实现mscomm读取数,转换用DP+spcomm怎么写呀
    http://community.csdn.net/Expert/topic/3486/3486032.xml?temp=.5334131