串口一开始实时监控数据的,所以会实时的接收数据。但是我有时候需要对下位机进行写操作,读操作和写操作分别有不同接收事件发生。有时候我对下位机进行写的时候,下位机的接收事件没发生,而是跳转到了读操作的接收事件,我该怎么样屏蔽这种情况的发生啊?请教大家了啊,比较着急,

解决方案 »

  1.   

    当对下位机进行写操作的时候,我没有STOPCOMM
      

  2.   

    STOPCOMM,然后在START的话,串口出现延时的问题,而且通信也不顺畅了
      

  3.   

    "有时候我对下位机进行写的时候,下位机的接收事件没发生,而是跳转到了读操作的接收事件"
    你说的“下位机”是指的非PC机(嵌入式设备)吗?如果是这样的话,和上位机(PC电脑)有什么关系?
      

  4.   

    if Identify='WR' then
      begin
        if IWRCount=1 then
        begin
           TempString:='';
           OutPutStr:='@00WR00050000';
           OutPutStr:=OutPutStr+CalBcc(OutPutStr)+'*'+Chr(13);
           Plength:=length(OutPutStr)+1;
           while not  plcComm.WriteCommData(PChar(OutPutStr),PLength) do
           begin
              showmessage('数据还没发出');
              plcComm.WriteCommData(PChar(OutPutStr),PLength);
           end;
           IWRCount:=IWRCount+1;
           showmessage('数据第二次已经发出');    end
        else
        begin
          TempString:='';
          OutputStr := '@00RD01460006'+CalBcc('@00RD01460006')+'*'+chr(13);
          Plength:=length(OutPutStr)+1;
         // sleep(10);
          Application.ProcessMessages ;
          plcComm.WriteCommData(PChar(OutPutStr),PLength);
          IWRCount:=0;
        end;
      end;  If Identify='RD' then
      begin
         if Open=true then
         begin
            showmessage('跳转到这里RD');
            edit2.Text :=TempString;
            Open:=False;
         end;
         省略中间过程
             TempString:='';
         OutputStr := '@00RD01460006'+CalBcc('@00RD01460006')+'*'+chr(13);
         Plength:=length(OutPutStr)+1;
         Application.ProcessMessages ;
         plcComm.WriteCommData(PChar(OutPutStr),PLength);
      end;  上位机没事情做的时候就是执行“RD”操作,会循环的进行。我如果对下位机操作要进行“WR”写命令,WR命令要写两次,因为只写一次的话,对操作会一直置位,我要进行复位,就要写两次,第一次是往里面写“1”,第二次是往里面写“0”,如果进行写操作,下位机理论上是应该得到“WR”标识符的,但是老是会被"RD"标识符冲掉,因为“RD”命令是一直循环进行的,我应该怎么样捕捉到“WR”标识符呢?
      

  5.   

    我说的下位机是PLC,硬件设备啊,如果PC机往里面写的时候,硬件PLC会发出应答命令传送给PC机的,我用SPCOMM的RECIEVEDATA事件来接收过来的应答命令,进而进行判断的
      

  6.   

    To 1楼:对SPComm进行写(输出)时,不需要StopComm。To 2楼:这种情况我觉得不是你的操作顺序的关系。我用SpComm的时候没有发现过类似问题。是不是在你进行StopComm和StartComm时,串口正在发送数据?
      

  7.   

    1.6楼给出的代码都是在一个函数或过程中吗?还是楼主省略了多个函数或过程名定义语句直接拷贝过来的?
      这个(或这些)函数是什么时候被调用的?触发这个(或这些)函数的条件是什么?(定时器?线程?用户点击按钮?接收到某些数据之后调用以作通讯上的交互?)2.Identify变量是在什么时候或什么情况下赋值的?3.If Identify='RD' then 
      begin 
        if Open=true then //这里的Open是个普通变量还是个Function名称?
      

  8.   

    Open,只是一个测试的标志
    procedure TForm1.BitBtn2Click(Sender: TObject);
    //5.03手动停止
    begin
      OutPutStr:='@00WR00050008';
      OutPutStr:=OutPutStr+CalBcc(OutPutStr)+'*'+Chr(13);
      Plength:=length(OutPutStr)+1;
      if  plcComm.WriteCommData(PChar(OutPutStr),PLength) then
      begin
          showmessage('发送成功');
          IWRCount:=1;
          Open:=True;
      end;
      SystemTime.Enabled :=False;
    end;下面的是RECIEVEDATA事件内容
    procedure TForm1.plcCommReceiveData(Sender: TObject; Buffer: Pointer;
      BufferLength: Word);
    var
      TempString:pChar;
      Identify:String;
      X,y,AdJustX,AdJustY:Single;
      BinStr:String;
    //D146,147X电机脉冲数,D148,149Y电机脉冲数
    //D150 拉力传感器模拟量数字 D151 IO 状态
    begin
      TempString:=Buffer;
      edit1.Text :=TempString;
      Identify:=Copy(TempString,4,2);
      sleep(10);
    //  Application.ProcessMessages ;
      If Identify='SC' then
      begin
        TempString:='';
        OutPutStr := '@00RD01460006'+CalBcc('@00RD01460006')+'*'+chr(13);
        Plength:=length(OutPutStr)+1;
        //sleep(10);
        Application.ProcessMessages ;
        plcComm.WriteCommData(PChar(OutPutStr),PLength);
      end;  if Identify='WD' then
      begin
       // showmessage('执行到了');
        Edit2.Text :=TempString;
        TempString:='';
        OutputStr := '@00RD01460006'+CalBcc('@00RD01460006')+'*'+chr(13);
        Plength:=length(OutPutStr)+1;
       // sleep(10);
       Application.ProcessMessages ;
        plcComm.WriteCommData(PChar(OutPutStr),PLength);
      end;刚才上面的是其中的一部分
      

  9.   

    上面的SLEEP和application.processmessage,是我刚才调试的时候加上去的,其实没什么用
      

  10.   

    修改通信协议什么意思?我现在和硬件的通信协议,是硬件规定的,没法改,硬件只能被动的响应方式,来发送数据,上位机才能接收到数据。也就是我上位机只有给硬件发数据了,硬件才会响应。 
    Veron_04的意思是,我先判断下位机里面有没有接收到数据,然后再写?但是我想做的是随时可以写啊!
      

  11.   

    针对你在6楼的描述:
    上位机没事情做的时候就是执行“RD”操作,会循环的进行。
      --这句话我猜测你是在定时器中执行的
    下位机理论上是应该得到“WR”标识符的,但是老是会被"RD"标识符冲掉
      --这句话我应该是你没有根据WR标识去控制RD的执行。如果我的猜测正确的话,那么你的问题在于:
    由于你是用定时器去定时进行发送RD命令,而这个过程没有受到双方应有的交互关系制约和控制,所以在你进行正确的交互通讯时,定时器仍然在按设置的时间进行发送RD命令,所发送的命令与正常的交互命令在时序上混在了一起,导致下位机的执行顺序混乱。建议:
    1.治标不治本的办法
      发送RD命令仍然采用定时器,但需要将定时器的时间延长至发送WD命令至命令响应数据返回之后的最大时间,并且当通讯软件需要向下位机发送WR命令前禁止定时器,并且只有当收到下位机对WR的响应数据之后才允许定时器的执行。
      这种办法之所以说治标不治本,是因为定时器的时序比较难控制,在你的正常交互比较复杂的情况下很有可能被其干扰。
    2.另外一种办法:
      重新设计上位机与下位机的交互关系,将下位机的内容主动上报,而不是由上位机来实时查询
      

  12.   

    真是非常感谢wooden954的热心回答啊,我的上位机进行“RD”操作是根据收到命令标识符的,比如我现在现在收到的是“RD”标识符,那么我立刻再才发送 OutputStr := '@00RD01460006'+CalBcc('@00RD01460006')+'*'+chr(13); ,如果我收到的是“WD”标识符,我也是立刻发送 OutputStr := '@00RD01460006'+CalBcc('@00RD01460006')+'*'+chr(13); ,
    只有这样才能进行实时的循环数据的,你仔细看我的每个标识符的判断,如果没什么事情干的话,立刻进行"RD"操作,只是我“WR”操作为什么写两次呢,因为在我的硬件里面,如果只写一次的话,一直是置位状态,但是我想让它只是瞬间的接通,所以我需要复位,我需要根据得到的“WR”标识进行复位,也就是写      OutPutStr:='@00WR00050000'; 
          OutPutStr:=OutPutStr+CalBcc(OutPutStr)+'*'+Chr(13); 
          Plength:=length(OutPutStr)+1; 但是我老是捕捉不到'WR'标识符,所以我的“WR”写操作老是完不成
      

  13.   

    有点儿明白你的交互方式了。
    说说我对你的代码等的疑问,希望能够有些提示作用:
    1.代码中判断字符串是否等于另外一个字符串你用的是这样的表达方式:If Identify='SC' then 
      那么,在下位机中发送的是否全部字符,比如RD、WR、WD等是否是大写?如果不是,那么用这种判断肯定会出现捕捉不到的问题。
      另外,字符串比较用等号判断的话,当长度不一至,可能判断结果是错误的。
      建议使用判断字符串是否相等用语句if StrComp(Pchar(Identify),Pchar('SC'))=0 then
      当然再加上大小写不敏感的话,会需要形如
      if StrComp(Pchar(UpperCase(Identify)),Pchar('SC'))=0 then的语句2.事件触发及串口数据流中断:
      对于Spcomm来说,它的产生接收事件的机制我不是太了解,但我觉得有如下可能:
      如果一长串数据需要从串口接收,如“018438938924509824908245098245"
      这一长串数据本来是一个完整帧,但是由于接收控件的缓冲区或其它接收机制造成一帧数据产生了两次接收事件(甚至更多次),每次接收到了数据的一部分,那么你在控件的接收事件中写的代码就需要考虑这种情况了。这就需要一个大的缓冲区,将收到的数据按顺序放在缓冲区中,然后再有一个程序从缓冲区中判断帧数据的开始和结束,再对分解出的完整帧的数据内容进行分析。3.楼主在17楼描述说:
    只是我“WR”操作为什么写两次呢,因为在我的硬件里面,如果只写一次的话,一直是置位状态,但是我想让它只是瞬间的接通,所以我需要复位,我需要根据得到的“WR”标识进行复位,也就是写 
      OutPutStr:='@00WR00050000'; 
      OutPutStr:=OutPutStr+CalBcc(OutPutStr)+'*'+Chr(13); 
      Plength:=length(OutPutStr)+1; 
    但是我老是捕捉不到'WR'标识符,所以我的“WR”写操作老是完不成 
      --在这个描述中,三行代码前的描述是用SpComm写WR命令到下位机的,但是三行代码之后描述“捕捉不到WR标识符”,我的理解是楼主需要在上位机给下位机发送的数据中捕捉WR标识符,而不是在下位机发给上位机的数据中捕捉这个WR标识符,不知道是我理解的不对还是楼主的描述不正确?如果我理解正确的话,那么你在接收事件中肯定是捕捉不到发送的数据的。
      

  14.   

    这是上下位机同步问题吧,和PLC的通信一般是半双工的,必须同步方式而不是异步方式。上位机发一个命令,一定要等到应答返回或超时,才能结束这一会话过程。你的通信方式有点类似异步通信方式了,我想PLC不支持这种通信方式(如果是全双工的,异步通信也行,不过上位机的处理逻辑比较复杂)。还有要考虑延时问题,上位机往串口写(命令),要延时100-500ms才能读(串口),才不会丢失数据,这个时间试几次就能找到较小且稳定的(另外也可以检测串口缓冲区中数据长度,以判断数据收全否)。
    象你这种情况,我一般是这样处理:
    采集数据过程用一个线程,在线程里就重复读数据处理过程然后用个事件TEvent,上位机如果要偶尔和下位机交互其它命令,主线程就设置这个事件有信号,在采集数据线程里,waitforsingleobject()这个事件信号,就临时停止了采集过程,主线程交互过程结束,设置事件无信号,恢复采集过程执行。(不要用suspend和resume来停止和恢复线程,不大可靠)
      

  15.   

    谢谢wooden954  和 jankercsdn 的热心帮助,我现在通过开辟一个二级缓存区,来解决问题