PC机与20台单片机联接,单片机为并联式并且有各自的编号 
当PC机发送协议及单片机号时,相应的单片机则向PC机返回一条信息,PC机是按1到20顺序不断地发送协议及单片机号的,先发首个号,然后在PC机的OnReceiveData中收取信息,只有当OnReceiveData收到有效信息后,才发送下一号,但如果某一单片机出故障,PC机OnReceiveData就收不到有效信息,怎样才能做到继续下一号的发送呢?

解决方案 »

  1.   

    TimeOut := 30;你學過網絡通信吧。不會這也想不到吧。
      

  2.   

    感谢各位!!!!!TO微斯人:我还真的对网络通信不熟,唉!TimeOut有这个变量吗?放在那儿呢?TO小王、一叶风铃:具体怎么做呢?给点代码参考好吗?
      

  3.   

    delphi中常用串口控件简介:
    mscomm:微软的东西,是VB中带的一个ActiveX控件,使用简单,性能一般,由于是ActivX控件,打包时需要注册好多信息,在Delphi中使用,建议使用VCL控件,编译程序时直接编入程序中,再不需任何其它处理。
    spcomm:比较好的vcl控件,算是比较专业的,解剖了一下,功能比较完善。
    TurboPower:公认的专业通讯vcl控件。可以到其站点下载,开放源码了。
    我在制作串口通讯软件时三种都用过,最终全部使用TurboPower!所以也推荐大家使用它。
    写了个例子,基本的串口通讯都可以实现,可提供参考:
    下载基地-》文件名称:串口通讯控制器
    版权声明:以下本文只允许在本站观看,不得以任何媒体方式进行传播。
    发表意见请到留言版。TurboPower串口通讯实际应用:
    在串口通讯时有字符和十六进制两种数据传输方式,不论使用哪种方式,只要能正确收到数据就是目的,至于收到数据后如何处理,就要根据具体的情况来定了。1.接收数据的方法:
    轮询和中断(利用windows消息激发事件)。
    1)轮询:每间隔一定的时间查询一下串口接收缓存中有无数据,有就读出来。这种方法是很毫资源的,即没事找事。
    2)中断:在控件中有OnTrigger事件,当串口收到数据后,即触发此事件,无数据时什么都不做,在这个事件中接收数据就比较科学了。
    所以,提倡使用控件中的OnTrigger事件接收数据。2.通讯协议的制定:
    接收数据的一般处理方法,最基本的思路就是通过协议进行分析,所以协议的制定是至关重要的:
    1)首先要确定指令的起始点,从大量的数据流中将指令分离出来,没有起始标志的话,结果就可想而知了,一串无效的费数据!
    2)然后就是指令结束识别点,可以利用指令的长度(如果长度一定或有表示长度的数据)或结束标志来确定,当然还可以利用下一条指令的指令头。
    3)既然头尾都明确了,指令的截取想来不是什么问题了吧!但还有一种情况就是数据错误是的容错,如何容错呢,最简单的办法:发现不符合格式的指令,就将其抛掉或特殊处理(如要求重发)一下!
    4)有效数据中如果增加一些校验,通讯将会更加可靠!
    例:#(指令头)**(指令功能)0123456789(有效数据)**(有效数据校验和)%(指令尾)
    注:**代表变动值。3.接收数据的分析技巧:
    通讯协议制定好后,一切将以通讯协议为中心。一套协议中的所有指令可能长度都是统一的,也有可能是长短不同的,并且在OnTrigger事件中实际反应速度及快,可能一条指令数据还没有完全收齐就已经触发了此事件,即收到了半截指令,并且有可能继续收取的数据中除了下半截指令外,还有下一条指令的前半截,如何处理?
    我在做这种处理时是利用全局变量,将串口收到的所有数据都收到该串中,然后按指令格式进行截取,发现不合法指令做一下特殊处理(如要求重发)或抛弃。
    如收到的数据串为:
    #**0000012000**%#**0000000343#**000000540560**%#**0002200000**%
    分段截为:
    #**0000012000**%
    #**0000000343
    #**000000540560**%
    #**0002200000**%
    四条指令,其中:#**0000000343不完整,检测到后进行抛弃处理。调试技巧篇:
    对于已了解协议的支持串口产品,要想进行编程控制,可以使用“串口通讯控制器”进行调试,以摸清具体实现数据,可按如下步骤进行:
    1.确定硬件连接无误,这是首要条件,如果错误将没有成功的可能;
    连线必须正确,必要时可以使用计算机自带的多个端口相互进行测试,已保证硬件的连接无误。串口通讯线有9针和25针,多用9针,其中最重要的是2(RXD)、3(TXD)、5(GND)线,对应关系如下:
    9针 25针
    2 -- 3
    3 -- 2
    5 -- 72.确定通讯参数正确,如:波特率、奇偶校验位、数据位、停止位等,以及收发的是十六进制还是字符串:3.以上确保正确,则使用“串口通讯控制器”,按协议输入数据进行收发控制了。
    注意:有的仪器需要进行初始化,即先发一段激活指令,然后才能进入工作状态,这种设置主要是为了实现利用硬件为软件加密,即类似加密狗,需要有激活方法才行,不过该类方法使用较少。原创作者:JPYC,望业界专家多多指正!控件及例程源码请到:http://www.kaer.cn/default.aspx->下载基地
      

  4.   

    http://218.56.11.178:8020/web/technology.aspx->下载基地->例程-硬件控制->串口通讯控制器/spcom事例程序          ->控件-硬件控制->TurboPower Async Professional/spcom
      

  5.   

    TO jpyc大侠:2)中断:在控件中有OnTrigger事件,当串口收到数据后,即触发此事件,无数据时什么都不做,在这个事件中接收数据就比较科学了。
    所以,提倡使用控件中的OnTrigger事件接收数据。如果当前下位机发生故障,而不能返回数据,即串口收不到数据,OnTrigger事件就得不到触发,那么又如何做到跳过此机,而发送信息到下一个下位机呢?
    原来我用的是在OnReceiveData中处理完收到的数据后,下位机号+1,发送信息到下位机的
    请指教!!!
      

  6.   

    http://218.56.11.178:8009/-》delphi专栏-》高级技术  类似帖子自己搜
      

  7.   

    本文就是介绍基于Windows95/NT操作系统用Delphi来实现PC机与下层PLC控制器之间的串口通信方法。
    基于WIN95/NT的串行通信机制
      Windows操作系统的机制禁止应用程序直接访问计算机硬件,但它为程序员提供了一系列的标准API函数,使得应用程序的编制更加方便并且免除了对有关硬件的调试麻烦。在Windows95/NT中,原来Windows3.X的WM_COMMNOTIFY消息已被取消,操作系统为每个通信设备开辟了用户可定义大小的读/写缓冲区,数据进出通信口均由操作系统后台完成,应用程序只需对读/写缓冲区操作即可。WIN95/NT中几个常用的串行通信操作函数如下:
       CreatFile  : 打开串行口
       CloseHandle : 关闭串行口
       SetupComm  : 设置通信缓冲区的大小
       ReadFile   : 读串口操作
       WriteFile  : 写串口操作
       SetCommState : 设置通信参数
       GetCommState : 获取默认通信参数
       ClearCommErro: r清除串口错误并获取当前状态
      除上述几个函数外,还要经常用到一个重要的记录DCB(设备控制块)。DCB中记录有可定义的串行口参数,设置串行口参数时必须先用GetCommState函数将系统默认值填入DCB控制块,然后才可把用户想改变的自定义值设定。在WIN95/NT中进行串行通信除了解基本的通信操作函数外,还要掌握多线程编程。线程是进程内部执行的路径,是操作系统分配CPU时间的基本实体。每个进程都由单线程开始完成应用程序的执行。串行通信需要利用多线程技术实现,其主要的处理逻辑可以表述如下:进程一开始先由主线程做一些必要的初始化工作,然后主线程根据需要在适当时候建立通信监视线程监视通信口,当指定的串行口事件发生时,向主线程发送WM_COMMNOTIFY消息(由于WIN95取消了WM_COMMNOTIFY消息,因此必须自己创建),主线程对其进行处理。若不需要WM_COMMNOTIFY消息,则主线程终止通信监视线程。多线程同时执行,将会引起对共享资源的冲突。为避免冲突,就要用同步多线程对共享资源进行访问。WIN95提供了许多保持线程同步的方法,笔者采用创建事件对象来保持线程同步。通过CraeteEvent()创建事件对象,使用SetEvent() 或PulseEvent()函数将事件对象设置成信号同步。在应用程序中,利用WaitSingleObject() 函数等待同步的触发,等到指定的事件被其它线程设置为有信号时,才继续向下执行程序。
    Delphi下的具体实现方法
      Delphi的强大功能和支持多线程的面向对象编程技术,使得实现串行通信非常简单方便。它通过调用外部的API函数来实现,主要步骤如下:首先,利用CreateFile函数打开串行口,以确定本应用程序对此串行口的占有权,并封锁其它应用程序对此串口的操作;其次,通过GetCommState函数填充设备控制块DCB,再通过调用SetCommState函数配置串行口的波特率、数据位、校验位和停止位。然后,创建串行口监视线程监视串行口事件。在此基础上就可以在相应的串口上操作数据的传输;最后,用CloseHandle函数关闭串行口。具体的程序如下,本程序用Delphi3.0编制在Win95环t境下调试通过,已投入实际应用中,供广大读者参考。
    程序:
    unit comdemou;
    interface
    uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
    const
    Wm_commNotify=Wm_User+12;
    type
    TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    private
    Procedure comminitialize;
    Procedure MsgcommProcess(Var Message:Tmessage); Message Wm_commnotify;
    { Private declarations }
    public
    { Public declarations }
    end;
    // 线 程 声 明
    TComm=Class(TThread)
    protected
    procedure Execute;override;
    end;
    var
    Form1: TForm1;
    hcom,Post_Event:Thandle;
    lpol:Poverlapped;
    implementation
    {$R *.DFM}
    Procedure TComm.Execute; // 线 程 执 行 过 程
    var
    dwEvtMask:Dword;
    Wait:Boolean;
    Begin
    fillchar(lpol,sizeof(toverlapped),0);
    While True do Begin
    dwEvtMask:=0;
    Wait:=WaitCommEvent(hcom,dwevtmask,lpol); // 等 待 串 行 口事 件;
    if Wait Then Begin
    waitforsingleobject(post_event,infinite); // 等 待 同 步 事件 置 位;
    resetevent(post_event); // 同 步 事 件 复 位;
    PostMessage(Form1.Handle,WM_COMMNOTIFY,0,0);// 发 送 消 息;
    end;
    end;
    end;
    procedure Tform1.comminitialize; // 串 行 口 初 始 化
    var
    lpdcb:Tdcb;
    Begin
    hcom:=createfile('com2',generic_read or generic_write,0,nil,open_existing,
    file_attribute_normal or file_flag_overlapped,0);// 打 开 串行 口
    if hcom=invalid_handle_value then
    else
    setupcomm(hcom,4096,4096); // 设 置 输 入, 输 出 缓 冲区 皆 为4096 字 节
    getcommstate(hcom,lpdcb); // 获 取 串 行 口 当 前 默 认设 置
    lpdcb.baudrate:=2400;
    lpdcb.StopBits:=1;
    lpdcb.ByteSize:=8;
    lpdcb.Parity:=EvenParity; // 偶 校 验
    Setcommstate(hcom,lpdcb);
    setcommMask(hcom,ev_rxchar);
    // 指 定 串 行 口 事 件 为 接 收 到 字 符;
    end;
    Procedure TForm1.MsgcommProcess(Var Message:Tmessage);
    var
    Clear:Boolean;
    Coms:Tcomstat;
    cbNum,ReadNumber,lpErrors:Integer;
    Read_Buffer:array[1..100]of char;
    Begin
    Clear:=Clearcommerror(hcom,lpErrors,@Coms);
    if Clear Then Begin
    cbNum:=Coms.cbInQue;
    ReadFile(hCom,Read_Buffer,cbNum,ReadNumber,lpol);
    // 处 理 接 收 数 据
    SetEvent(Post_Event); // 同 步 事 件 置 位
    end;
    end;
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    comminitialize;
    post_event:=CreateEvent(nil,true,true,nil); // 创 建 同 步事 件;
    Tcomm.Create(False); // 创 建 串 行 口 监 视 线 程;
    end;
    end. 
      

  8.   

    To jypc大侠:就是说当超过一定时间没回应时,继续下边的工作在哪里判断呢?
      

  9.   

    var
      Form_TZ_JuanQuL: TForm_TZ_JuanQuL;
      dtTmp:TDateTime;
      sRece1:string; //接收到的字符串
      rbuf:array[1..20] of byte;//把接收到的二进制转化为整数,存储在数组里
      pDataToWrite:array[1..20] of char; // 要发送的,转化为字符
      LDate:Integer;//发送字符长度
    implementationuses COM_Frm, Globe_Frm, Main_Frm;{$R *.dfm}procedure TForm_TZ_JuanQuL.Bbtn_SureClick(Sender: TObject);
    var
      IniF:TIniFile;
    begin
      IniF:=TIniFile.Create('.\HJKZ.ini');
      IniF.WriteString('JuanQuLiang_TZ','JuanQuLiang',EdT_JQuL.Text);
      IniF.Free;  pDataToWrite[1]:=chr(byte($D1));
      LDate:=1;
      Comm1.Inx_XonXoffFlow:=false;
      Comm1.Outx_XonXoffFlow:=false;
      Comm1.WriteCommData(@(pDataToWrite),LDate);  dtTmp:=Now;
      while ((not (sRece1='CC')) and ((Now-dtTmp)<(10000 /(24*60*60*1000))))  do
      begin
        if Form_TZ_JuanQuL.ModalResult=mrCancel then
        begin
          Exit;
        end;
        Application.ProcessMessages;
      end;  if sRece1<>'CC' then
      begin
        Application.MessageBox('通信失败!','提示',MB_OK+MB_ICONINFORMATION);
        Exit;
      end;  pDataToWrite[1]:=chr(byte($AD));
      pDataToWrite[2]:=chr(byte($01));
      pDataToWrite[3]:=chr(byte( StrToInt(EdT_JQuL.Text) ) );
      //pDataToWrite[3]:=chr(byte( StrToInt('$'+EdT_JQuL.Text) ) );
      LDate:=3;
      Comm1.Inx_XonXoffFlow:=false;
      Comm1.Outx_XonXoffFlow:=false;
      Comm1.WriteCommData(@(pDataToWrite),LDate);
      
    end;procedure TForm_TZ_JuanQuL.FormCreate(Sender: TObject);
    var
      IniF:TIniFile;
    begin
      IniF:=TIniFile.Create('.\HJKZ.ini');
      EdT_JQuL.Text:=IniF.ReadString('JuanQuLiang_TZ','JuanQuLiang','20');
      IniF.Free;
    end;procedure TForm_TZ_JuanQuL.Bbtn_ExitClick(Sender: TObject);
    begin
       //Waitforsingleobject(Bbtn_Exit.Handle,1);
       //Form_TZ_JuanQuL.ModalResult:=mrCancel;
       pDataToWrite[1]:=chr(byte($F3));
       LDate:=1;
       Comm1.Inx_XonXoffFlow:=false;
       Comm1.Outx_XonXoffFlow:=false;
       Comm1.WriteCommData(@(pDataToWrite),LDate);
       //Close;
    end;procedure TForm_TZ_JuanQuL.EdT_JQuLKeyPress(Sender: TObject;
      var Key: Char);
    begin
      if((key<#48 ) or(key>#57)) and (key<>#8) then
      begin
         key:=#0;
      end;
    end;procedure TForm_TZ_JuanQuL.FormClose(Sender: TObject;
      var Action: TCloseAction);
    begin
       Comm1.StopComm;
       Form_Main.ComBz.StartComm;  
    end;procedure TForm_TZ_JuanQuL.FormShow(Sender: TObject);
    begin
       Comm1.CommName:=Glb_Com;
       Comm1.StartComm;
    end;procedure TForm_TZ_JuanQuL.Comm1ReceiveData(Sender: TObject;
      Buffer: Pointer; BufferLength: Word);
    var
       i:integer;
    begin
       sRece1:='';
       move(buffer^,pchar((@rbuf)^),bufferlength);
       for i:=1 to bufferlength do
       begin
         sRece1:=sRece1+ inttohex(rbuf[i],2);
       end;
    end;
    end.
      

  10.   

    超过一定时间没回应时,继续下边的工作如果5秒后,单片机还没有发CC给计算机继续下边的工作
      dtTmp:=Now;
      while ((not (sRece1='CC')) and ((Now-dtTmp)<(50000 /(24*60*60*1000))))  do
      begin
        Application.ProcessMessages;
      end;