项目要求:1.有六个串口设备,并有六个测试通道
         2.六个通道可对六个设备选择,可能一个通道一台,也可能一个通道六台
         3.这六个通关对仪器有不同的控制要求,并要读回数据作处理,要可以同时运行六个通道(六通道动作不同)
开发语言:VB6
OS:WINXP
我的作法:用六个TIMER控件写六个通道下的各种行为,点击指定按键,运行某个TIMER事件,当运行多个TIMER后,发现当后运行的TIMER动作时,先运行的TIMER暂停了
我查了相关资料说是在同一个窗体中同时这样运行六个TIMER是会出现我这样的情况,请问有何解决方法?我目前了解和确认用MDI窗体作六个子窗体是可以实现要求,但有无方法在一个窗体中实现这样的行为?

解决方案 »

  1.   

    1.设备事实上有七台,只有一台可以用ONCOMM事件来作,另六台是不同的指令对应不同的回传值,并且发送指令的时机也是不同的这个项目简单点说就是六个通道的操作要互不干扰,互不影响
      

  2.   

    我试过单窗体下,用6个TAB页面,在六个页面中作为六个通道,当两个通道同时运行开始测试时,两个TIMER控件中,先运行的TIMER控件就会停止;
    串口是一对一的,所以六台设备各有一个串口,各串口参数一致.我再补充一下我的项目要求:
    这是一个生产线产品测试程序,有六个测试通道,可能会同时或不同时测试不同的产品,而且各产品的测试步骤也是不同的,个别通道下可能会要求并联两台以上设备,(当然这样一来其它通道就不会进行测试)我现在遇到的问题是:当两个或者以上通道同时进行测试时,先运行的通道会停止在读数据的子函数中(有时是读电流,有时读电压,有时是读功率,这是查询Stack发现的),当把后运行的通道停止掉后,先运行的通道才会再继续下去
    之前因为怀疑通道会共用某一个SUB或者函数,所以把所有通道用的SUB,函数全部独立了出来有四五年没写VB了,现在一上手就遇到这么个问题,难道真的太老了...哎求助!
      

  3.   

    Private Sub comInitLd1(idLoad As Integer)      '串口初始化
      On Error GoTo eHcomInitLd1
      With mscomLoad1
        If .PortOpen Then .PortOpen = False
        .CommPort = portCom(idLoad)
        .Settings = "115200,n,8,1"
      End With
      Exit Sub
    eHcomInitLd1:
      MsgBox Err.Description, vbOKOnly, "9841"
    End SubPrivate Function loadRdLd1(strRd As String) As String
    On Error GoTo EhClassRdLd1
      Dim tTime As Long, DataReceive As String, timP As Long
      With mscomLoad1
        If Not .PortOpen Then .PortOpen = True
        tTime = GetTickCount
        .InBufferCount = 0
        DataReceive = ""
        .Output = strRd & Chr(10)
        Do
          DataReceive = DataReceive & .Input
          DoEvents
          If Right(DataReceive, 1) = Chr(10) Then
            timP = GetTickCount - tTime
            Debug.Print "LOAD Time Read : " & timP
            loadRdLd1 = Left(DataReceive, Len(DataReceive) - 1)
            Exit Do
          End If
        Loop Until (Right(DataReceive, 1) = Chr(10)) Or (GetTickCount > tTime + timeoutRd)
    '    Do While GetTickCount < tTime + timeoutRd
    '      DataReceive = DataReceive & .Input
    '
    '      If Right(DataReceive, 1) = Chr(10) Then '.InBufferCount <> 0 Then 'And Right(.Input, 1) = Chr(10) Then
    ''      Debug.Print "Length = " & Len(DataReceive)
    '        loadRdLd1 = Left(DataReceive, Len(DataReceive) - 1)
    '          Exit Do
    '      Else
    '        DoEvents
    '      End If
    '    Loop
      End With  Exit Function
    EhClassRdLd1:
      MsgBox "9841 Communication Error : " & Err.Number & " : " & Err.Description, vbOKOnly, "9841"
    End Function
    这是其中一个通道的读回函数,程序经常性的暂停于此或者其它的设备的读回函数中
      

  4.   

    DoEvents 最好别加,容易发生重入的问题。
      

  5.   

    谢谢你的回复,但问题应该不是在这个DOEVENTS这
    昨天试的方法不可行;
    印象中这个问题N年前遇到过,似乎是个小地方,VB6有BUG?我在程序中用到了结构数组,所以主程序中有大量WITH存在,不知道有没有影响
      

  6.   

    不必用六个Timer控件,一个足够。
      

  7.   

    我这边目前查出来在用MDI窗体的作法中发现读回延时不同+DOEVENTS的使用可能是造成问题产生的原因,目前还在试.
    TO:贝隆
    1.你几年前上传的那个串口API的程序我试用了,发现不能连接我的设备,指令发送过去无效,因为没有时间就没有深究了,有空再研究.里面的API用法对我突破16个口限制有帮忙,这里先谢过;
    2.你说在单窗体中用一个TIMER怎么实现,求教,因为在我的设计中,六通道以应六个TIMER,这六个TIMER通常只会运行一次,也即点击对应TAB页面上的测试键开始运行;六通道的测试项目也是不同的,运行的时间也相应的会是不同的.
    3.呵呵,自从看了你的一些回复,一直想联系你,但不知道如何能找到你.
      

  8.   

    我有个新的怀疑点,我两个通道先后点击不同的按键测试后,先运行的通道会暂停,查STACK发现总是停在DOEVENTS上,直后运行的通道完成测试后才会再动.问题应该就是出在DOEVENTS的运用上,但我的读回串口的函数中还是会用到DOEVENTS,有点纠结
      

  9.   

    我这边目前查出来在用MDI窗体的作法中发现读回延时不同+DOEVENTS的使用可能是造成问题产生的原因,目前还在试.
    TO:贝隆
    1.你几年前上传的那个串口API的程序我试用了,发现不能连接我的设备,指令发送过去无效,因为没有时间就没有深究了,有空再研究.里面的API用法对我突破16个口限制有帮忙,这里先谢过;
    2.你说在单窗体中用一个TIMER怎么实现,求教,因为在我的设计中,六通道以应六个TIMER,这六个TIMER通常只会运行一次,也即点击对应TAB页面上的测试键开始运行;六通道的测试项目也是不同的,运行的时间也相应的会是不同的.
    3.呵呵,自从看了你的一些回复,一直想联系你,但不知道如何能找到你.对于多串口通信,我通常的做法就是采用轮询,也就是在一个Timer中轮流读取各个下位机的数据,具体怎么区分?也很简单,就用Select语句来分别就是,实际上其效果和多线程并无区别。
      

  10.   

    我这边目前查出来在用MDI窗体的作法中发现读回延时不同+DOEVENTS的使用可能是造成问题产生的原因,目前还在试.
    TO:贝隆
    1.你几年前上传的那个串口API的程序我试用了,发现不能连接我的设备,指令发送过去无效,因为没有时间就没有深究了,有空再研究.里面的API用法对我突破16个口限制有帮忙,这里先谢过;
    2.你说在单窗体中用一个TIMER怎么实现,求教,因为在我的设计中,六通道以应六个TIMER,这六个TIMER通常只会运行一次,也即点击对应TAB页面上的测试键开始运行;六通道的测试项目也是不同的,运行的时间也相应的会是不同的.
    3.呵呵,自从看了你的一些回复,一直想联系你,但不知道如何能找到你.对于多串口通信,我通常的做法就是采用轮询,也就是在一个Timer中轮流读取各个下位机的数据,具体怎么区分?也很简单,就用Select语句来分别就是,实际上其效果和多线程并无区别。
    我的程序单个通道运行起来是没有问题的,但当开始了另一个通道的测试后,先运行的通道就没有问题,所以我想我的顺序读数据这一块出问题可能性不大;
    之前一直怀疑是两个通道使用了公用变量造成了问题,但现在我已经把人用变量私化了,我反复检查过,也不太可能在这出问题;
    所以想请你帮我想想什么情况下会出现这样的情况?也即暂停的通道总是停在DOEVENTS上,DOEVENTS的位置是在读BUFFER时才会有
      

  11.   

    重入很烦人的,程序连退都退不出来,只能用end强行结束
    耗时的代码最好分过程
      

  12.   

    执行流程如下:Private Function loadRdLd1(strRd As String) As String
    ...
      Select Case Index
        Case 0
    '      timCh1.Enabled = True
          Call testCh1
        Case 1
          Call testCh2
    ....
      end select
    end subPrivate Sub testCh1()
      Dim i As Integer, j As Integer, k As Integer, h As Integer, m As Integer
      Dim flagFor1 As Integer, flagFor2 As Integer
      totalStep(currentChanT(0)) = 0
      flagFor1 = 0
      flagFor2 = 0
    '  With itemStep(currentChan, i)
      '查找For1,Loop1,For2,Loop2位置
      For i = 1 To nStep
        If flagStop(0) Then
          Call doneStop(currentChanT(0) = 0)
          Exit Sub
        End If
        With itemStep(currentChanT(0), i)
         If .kindStep <> 100 Then
          '状态显示表格赋值
          valShow(currentChanT(0), 0) = getItemName(.kindStep)
          
          '
          If .kindStep = 5 Then     '当为FOR1时在余下步骤中找出第一个LOOP1
            stepFor1(currentChanT(0)) = i
            For j = i + 1 To nStep  '查找loop1位置
              If itemStep(currentChanT(0), j).kindStep = 6 Then
                stepLoop1(currentChanT(0)) = j
                flagFor1 = 1
                i = j               '将当前测试步骤指向循环尾
                Exit For
              End If
            Next j
            '判断在FOR1中有无For2
            For k = stepFor1(currentChanT(0)) + 1 To stepLoop1(currentChanT(0)) - 1
              If itemStep(currentChanT(0), k).kindStep = 7 Then
                stepFor2(currentChanT(0)) = k
                flagFor2 = 1
                Exit For
              End If
            Next k
                '查找LOOP2
            For k = stepFor2(currentChanT(0)) + 1 To stepLoop1(currentChanT(0)) - 1
              If itemStep(currentChanT(0), k).kindStep = 8 Then
                stepLoop2(currentChanT(0)) = k
              End If
            Next k
            '状态显示表格赋值,记录FOR1循环次数
            valShow(currentChanT(0), 1) = itemStep(currentChanT(0), stepFor1(currentChanT(0))).itemDes(1)
               '按循环次执行FOR2测试,DO WHILE循环次数,FOR作循环步骤,步骤为stepFor2+1->stepLoop2-1
            For k = 1 To itemStep(currentChanT(0), stepFor1(currentChanT(0))).itemDes(1)
             '状态显示表格赋值
              valShow(currentChanT(0), 1) = valShow(currentChanT(0), 1) - 1
              valShow(currentChanT(0), 0) = getItemName(.kindStep)
              For h = stepFor1(currentChanT(0)) + 1 To stepLoop1(currentChanT(0)) - 1
                If itemStep(currentChanT(0), h).kindStep = 7 Then
                  '状态显示表格赋值,记录FOR2循环次数
                  valShow(currentChanT(0), 2) = itemStep(currentChanT(0), stepFor2(currentChanT(0))).itemDes(1)
                  
                  For m = 1 To CInt(itemStep(currentChanT(0), stepFor2(currentChanT(0))).itemDes(1))
                    valShow(currentChanT(0), 2) = valShow(currentChanT(0), 2) - 1
                    For j = stepFor2(currentChanT(0)) + 1 To stepLoop2(currentChanT(0)) - 1
    '                  DoEvents
                       '执行五测试
                      Call itemTestCh1(currentChanT(0), j)
                     '按下停止键,停止测试
                      If flagStop(currentChanT(0)) = True Then
                        Call doneStop(currentChanT(0))
                        Exit Sub
                      End If
                    '判断是否按下暂停键
                      Do While flagPause(currentChanT(0)) = True
                        DoEvents
                      Loop
                      '关闭数据读取
    '                  Call swTimer(currentchant(0), False, 1000)
                    Next j
                  Next m
                Else
                 '执行五测试
                  Call itemTestCh1(currentChanT(0), h)
                  '按下停止键,停止测试
                  If flagStop(currentChanT(0)) = True Then
                    Call doneStop(currentChanT(0))
                    Exit Sub
                  End If
                  '判断是否按下暂停键
                  Do While flagPause(currentChanT(0)) = True
                    DoEvents
                  Loop
                  '关闭数据读取
    '              Call swTimer(currentchant(0), False, 1000)
                End If
              Next h
            Next k
          Else
            '判断项目类别执行五种测试项之一
            Call itemTestCh1(currentChanT(0), i)
            '按下停止键,停止测试
            If flagStop(currentChanT(0)) = True Then
              Call doneStop(currentChanT(0))
              Exit Sub
            End If
            '判断是否按下暂停键
            Do While flagPause(currentChanT(0)) = True
              DoEvents
            Loop
            '关闭数据读取
    '        Call swTimer(currentchant(0), False, 1000)
           
          End If
         End If
        End With
      Next i
      
      '测试完成,停止测试
      Call doneStop(currentChanT(0))
      flagStop(0) = True
    End Sub
      

  13.   


    Private Sub itemTestCh1(tmpChan As Integer, tmpStep As Integer)
    ...
        Select Case .kindStep
          Case 0        'DisCharge
          ...
          Call statusReadCh1(tmpChan)
          case 1
          ...
        end select
    end sub Private Sub statusReadCh1(tmpChan As Integer)
    ...
      valShow(tmpChan, 3) = Format(cmdRdPloadVCh1(tmpChan), "#0.000")      '电压
      valShow(tmpChan, 4) = Format(cmdRdPloadICh1(tmpChan), "#0.000")      '电流'
      valShow(tmpChan, 5) = Format(cmdRdPloadPCh1(tmpChan), "#0.000")      '功率'
    ...
    end subPrivate Function cmdRdPloadVCh1(tmpChan As Integer) As Double
    ...
    loadRdLd1("meas:volt?")
    ...
    end sub
    Private Function loadRdLd1(strRd As String) As String
    On Error GoTo EhClassRdLd1
      Dim tTime As Long, DataReceive As String, timP As Long
      With mscomLoad1
        If Not .PortOpen Then .PortOpen = True
        tTime = GetTickCount
        DataReceive = ""
        .Output = strRd & Chr(10)
        Do
          DataReceive = DataReceive & .Input
          DoEvents
          If Right(DataReceive, 1) = Chr(10) Then
            timP = GetTickCount - tTime
            Debug.Print "LOAD Time Read : " & timP
            loadRdLd1 = Left(DataReceive, Len(DataReceive) - 1)
            Exit Do
          End If
        Loop Until (Right(DataReceive, 1) = Chr(10)) Or (GetTickCount > tTime + timeoutRd)
    '    Do While GetTickCount < tTime + timeoutRd
    '      DataReceive = DataReceive & .Input
    '
    '      If Right(DataReceive, 1) = Chr(10) Then '.InBufferCount <> 0 Then 'And Right(.Input, 1) = Chr(10) Then
    ''      Debug.Print "Length = " & Len(DataReceive)
    '        loadRdLd1 = Left(DataReceive, Len(DataReceive) - 1)
    '          Exit Do
    '      Else
    '        DoEvents
    '      End If
    '    Loop
      End With  Exit Function
    EhClassRdLd1:
    ...
    End Function以上是其中一个通道的代码,原来两个通道是在一个SUB中,但担心出问题,所以分离了开来,本来代码没这么复杂,后面基于同样的原因采取了分离,所以两个通道的代码基本是一致的而双通道运行时,程序总是会停在读串口数据的DOEVENTS那,这是查看call stack发现的,而且我用断点证明过确实是停在那里
    目前程序中就只有在读回上有DOEVENTS存在请大家帮忙看看,我这边也在同步在调试
      

  14.   

    按键检测那里比较恐怖,单片机上才这么干,一般时间占用小的话可以这么处理,对付长按的情况就不好了,具体情况我不了解。
    看起来跟发电机复示器比较像,
    如果是那种程序的话,先把采集和显示分离开来,
    比如你可以用一组mscomm和timer数组,功能就是定时采集数据,然后存到一组变量里边,这样采集程序就完成他的功能了。
    然后按钮部分,按钮不管是外部的还是内部的,只对他的触发做处理,就是按下或者放开,按下时标记一个按下的值,放开时标记一个放开的值,不要去紧盯着按钮状态,很消耗资源有按下标记了就认为他按下。
    然后是数据加工处理部分,那就要看你需要了,是根据按钮动作来的,还是需要实时处理的,反正处理完的数据就交给显示程序去显示。
      

  15.   

    在最初的设计中的确是作个标记,然后用TIMER来执行,但会遇到同样的问题,所以就作成这样;
    产生的问题也有可能和时间有关
      

  16.   

    谢谢你的回复,我想到一种最大的可能:数据采集和发送分离,这点很重要
    在我原来的程序中,数据采集和发送处于同一个SUB中,那么当扔出DOEVENTS时,权限就会跑到另一个通道,直到另一个通道的进程终止;
    我想我的改法是:1.设立六个TIMER对应六台设备,TIMER设为10ms运行一次,读回VIP(三次读回最大耗时180ms)
    2.通道程序中只提供设备的设定指令,设定完成后即等待一个设定的延时后再去取回TIMER中采集的VIP
    而恰巧客户对于显示的要求是1S一次,而我的机器读回一个参数最大耗时是60ms,写入约10ms的延时
    3.其中有一项测试步骤比较麻烦一点,要实时采回一组连在一起的字符串,不过原因找到了我想就能想办法规避掉再次感谢你的回复,我先要改程序了,时间隔太久了,这问题以前肯定遇到过并解决过.我有五年没写VB程序,8年没写VB的串口程序了,老了老了,真怀疑我的工作寿命还有多久
      

  17.   

    timer 10ms 可能太短了,程序处理不过来,一般一次timer响应都在20ms左右,我觉得最好在50ms左右
    我一般在timer头上把timer.enable=false,在结束的地方timer.enable=true
      

  18.   

    DOEVENTS使用要小心呀,这种程序中不要使用DOEVENTS
      

  19.   

    我算了一下,我的程序在结构上要重新布局:
    1.将主函数也即测试步骤进行的函数分割,通过判断标志压缩运行时间;
    2.将分割后的函数再细分,长时间运行的函数再细分,将其运行时间压缩,这样用了DOEVENTS后控制权也能快速放出
    就现在的情况来说,不这样分就用不了DOEVENTS,因为不用DOEVNETS的话程序就象死机一样,虽然仪器还在动,呵呵谢谢你的回复,但这种程序还是需要用DOEVENTS的谢谢你的回复,100ms以内的误差都可以接受
      

  20.   

    TO:笨狗飞先
    请教:多个TIMER同时运行时即其ENABLE都为TURE时执行顺序有无规则?
      

  21.   

    VB是单线程的,一个timer在处理的时候只要不doevents不用担心别的timer来砸场子
      

  22.   

    欲哭 无泪!!!!!!!!!!!!!!我被需求里的同时六通道测试给害死了!!!!!好简单的程序,给我花了这么久时间和精力改了这么多遍!其实只需要一个TIMER来采集数据就可以了,至于那六个通道实际上是放在一个SUB中来作,根本不可能在一个窗体中用六个TIMER来作,因为六个通道TIMER加两种设备的数据采集TIMER的时间和先后顺序根本无法掌控,杯具,极度杯具了,发现了路,但打击太大,都不太想改了,
    最近几年没有写过VB,一直在作LABVIEW,真的忘记太多
    思索作为程序员的未来的路.希望有朋友们顺便加入来讨论这个问题
      

  23.   

    每个通道都需要产生报表记录,并且每条记录的时间间隔可能不同,如果在一个SUB中顺序一个个的通道执行下去,产生的报表的时间也就累加了,还是需要隔离;每个通道中的设备执行步骤的时间点也是不同的,有点纠结,继续想:如何实现同步!
      

  24.   

    嗯,那是要好作得多,,只要外部采集数据就成,时间的话,只要采集时间在要求范围内,还是能作到秒精度的
    我这个真的有点头痛,
    我在想这样一个作法:把六个通道分到六个MDI子窗体中,程序运行放在各窗体的按键事件中循环,当然循环会很短的耗时,再加上DOEVENTS画面就不显得死机,同时用一个TIMER来捕捉7台设备的数据提供给六个通道调用,感觉不一定行,但要试,现在在家没法试了
      

  25.   

    VB6
    不知道如何实现六进程呀,之所以用六个MDI子窗体是因为我见过一个几乎相同的程序以MDI子窗体为对象,创建了八个通道来作
    顺便请教如何实现六进程
      

  26.   

    不会每一个设备都有一秒的要求吧?
    timer时间太短的话doevents很容易触发别的timer,或者你在doevents之前把别的timer关掉,结束之后再开,不知道会不会好些,不过担心有些timer可能会因此不能工作,试过才知道。看来只用一个timer处理起来比较好些
    一进timer之后就把这个timer 关掉
    然后就可以放心用doevents了
    结束timer之前再把timer打开,像这样子。
    Sub Timer1_Timer()
     Timer1.Enable=False
     ...
     ...
     Doevents
     ...
     ...
     Timer1.Enable=True
    End Sub
      

  27.   

    这样的话Timer的周期要设长一点,最好能在50ms以上,不然可能还是不好处理,反正你一个周期里六个通道都处理一次了,长些应该没关系。
      

  28.   

    这种想法我想到过,但问题这六个通道权限是相同的,要求也是相同的
    六个通道下还有不同的步骤,当然也有可能是相同的步骤,对于时间的要求自由性很大,有可能是1S采集记录一次,也可以是多秒,这个时间也是由客户定义,并且在通道的步骤中有一个步骤还需要不断的侦测设备的返回值,而这个步骤也是由用户自由设定的可以出现在任何一个通道中
    如你所说,我想我的作法将要改为如此:
    1、用两个TIMER,一个采集数据,一个运行六通道的程序,同一时间保证只运行一个TIMER;
    2、对时间的要求比较高,而且也相对较难处理;对六通道的当前步骤的采集时间进行排序,建立一个序列,顺序采集并保存显示;这一块有一定难度,到时要想想
    3、运行六通道的程序作为主程序,用FOR循环顺序判断六个通道是否执行,如果步骤为初期设定,则设定完后跳出到下一个通道,并且设置采集TIMER为TRUE;如果是采集,则收回采集TIMER中采集到的数据,
    4、采集TIMER每运行一次在尾部FALSE;
    5、假设六个通道都在运行,采集间隔要求为1S,3S,2S,5,6S,那么就需要定义两个变量数组,分别对应当前步骤的起始测试时间,和采集间隔时间,在每一次主程序循环时都判断这个起始时间+间隔时间到了没有如果到了就采集,并对最短的时间量进行延时补偿
    今天要在家陪女儿,明天去公司试,笨狗兄再帮我补充补充
      

  29.   

    刚才测试的结果,在不考虑存贮时间,采集间隔和先后顺序的情况下,前天的设想是可行的,现在需要作的就是对各个通道的采集顺序作个程序来处理.
    另外请问一下:我打算每个通道满5K条记录就存贮为一个文件,我现在的这个存贮部分是放在单个通道的显示SUB中,不知道对于量测时间上会否产生干扰?或者说这部分可能需要多久时间完成?