⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 全局变量.txt

📁 使用Delphi 6.0开发用于控制空调的程序
💻 TXT
📖 第 1 页 / 共 2 页
字号:
~200260e0B00560000

先定义几个全局变量:


FDataStatus:Word;  //0 就绪  1.帧头1  2.帧头2
FNextLength:Word;  //接下来要读的长度
FCurrentLength:Word;  //当前的长度
FtmpStr:string;//一帧数据 
代码如下:
procedure ComPortRxChar(Sender: TObject; Count: Integer);
var
  S1:string;
  J:Integer;
begin
  case FDataStatus of
    0:begin
      J:=0;
      FtmpStr:='';
      repeat
        ComPort.ReadStr(S1,1);
        J:=J+1;
      until (Ord(S1[1])=$68) or (J=Count);
      if Ord(S1[1])=$68 then
      begin
        FtmpStr:=S1;
        FDataStatus:=1;
        FNextLength:=10;
        FCurrentLength:=0;
      end;
    end;
    1:begin
      J:=FCurrentLength;
      repeat
        ComPort.ReadStr(S1,1);
        FtmpStr:=FtmpStr+S1;
        FCurrentLength:=FCurrentLength+1;
      until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);
      if FCurrentLength=FNextLength then
      begin
        FDataStatus:=2;
        FNextLength:=(Ord(FtmpStr[11]) shl 8) + Ord(FTmpStr[10])+2;
        FCurrentLength:=0;
      end;
    end;

    2:begin
      J:=FCurrentLength;
      repeat
        ComPort.ReadStr(S1,1);
        FtmpStr:=FtmpStr+S1;
        FCurrentLength:=FCurrentLength+1;
      until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);
      if FCurrentLength=FNextLength then
      begin
        FDataStatus:=0;
        FNextLength:=0;
        FCurrentLength:=0;
        FReceiveFrame.Str:=FtmpStr;
        SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));
      end;
    end;
  end;
end; 


 case FDataStatus of
判断目前接收的数据段。 
    0:begin
      J:=0;
      FtmpStr:='';
      repeat
        ComPort.ReadStr(S1,1);
        J:=J+1;
      until (Ord(S1[1])=$68) or (J=Count);
      if Ord(S1[1])=$68 then
      begin
        FtmpStr:=S1;
        FDataStatus:=1;
        FNextLength:=10;
        FCurrentLength:=0;
      end;
    end;

如果是一帧开始,那么就对相关参数进行初始化,数据帧初始化为空。


J:=0;
FtmpStr:=''; 


然后开始读,直到读到帧起始符$68或者读完为止,没有的话,就抛弃读到的数据(当然你也可以另外记下来,不过意义不大)。


   repeat

        ComPort.ReadStr(S1,1);
        J:=J+1;
      until (Ord(S1[1])=$68) or (J=Count); 
 J就是读到的数据,J=count表示读完了缓冲区。 
 Ord(S1[1])=$68,S1是个字符串,ORD(S1[1])表示S1的第一个字节。  
 ComPort.ReadStr(S1,1);每次只读一个字节,所以S1里只有1个字节。
如果$68在你读的第二个字节里,那不是蒙混过关了? 
如果读到了$68,就接下来设置读状态为第二阶段,帧数据加上$68,下阶段长度为10,读取的字节数为0.然后结束函数。


 if Ord(S1[1])=$68 then
      begin
        FtmpStr:=S1;
        FDataStatus:=1;
        FNextLength:=10;
        FCurrentLength:=0;
      end; 


如果缓冲区还有数据,它将再次触发OnRxChar事件。 
只要缓冲区中有数据,没有处理或者没有正在处理,就会一直触发OnRxChar事件。 
刚才将读状态设置为第二阶段了,那么现在就会执行第二阶段的代码:
 Case语句来判断的。 
    1:begin
      J:=FCurrentLength;
      repeat
        ComPort.ReadStr(S1,1);
        FtmpStr:=FtmpStr+S1;
        FCurrentLength:=FCurrentLength+1;
      until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);
      if FCurrentLength=FNextLength then
      begin
        FDataStatus:=2;
        FNextLength:=(Ord(FtmpStr[11]) shl 8) + Ord(FTmpStr[10])+2;
        FCurrentLength:=0;
      end;
    end; 
 J:=FCurrentLength;//计数器,记下共读了几个。 

 RTUA    4       终端逻辑地址       
MSTA     2       主站地址与命令序号    
68H 1     帧起始符       
C     1     控制码    
L     2     数据长度       

这就是我们第二阶段要读的10个字节。 
第二阶段的处理是读10个字节,不管缓冲区里有几个字节,不管读几次,读满10个为止。  
repeat
        ComPort.ReadStr(S1,1);
        FtmpStr:=FtmpStr+S1;
        FCurrentLength:=FCurrentLength+1;
      until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);
 FNextLength=10是第一阶段设置的。 
读到10个字节之后将进入第三阶段。 
 if FCurrentLength=FNextLength then
      begin
        FDataStatus:=2;
        FNextLength:=(Ord(FtmpStr[11]) shl 8) + Ord(FTmpStr[10])+2;
        FCurrentLength:=0;
      end; 
下一阶段要读几个呢?
FNextLength:=(Ord(FtmpStr[11]) shl 8) + Ord(FTmpStr[10])+2;
DATA      变长       数据域    
CS   1     校验码    
16H 1     结束码 


这就是最后一阶段要读的字节数。 
但是DATA是变长,所以我们才会多分一阶段,如果都是定长的话,只用判断到帧起始符,然后再读需要的字节数就可以了。 

好在有个
L     2     数据长度
表示DATA的长度。


所以就有了这一句。
FNextLength:=(Ord(FtmpStr[11]) shl 8) + Ord(FTmpStr[10])+2;
+2是最后两个字节。
接下来就是第三个阶段了: 

 2:begin
      J:=FCurrentLength;
      repeat
        ComPort.ReadStr(S1,1);
        FtmpStr:=FtmpStr+S1;
        FCurrentLength:=FCurrentLength+1;
      until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);


      if FCurrentLength=FNextLength then
      begin
        FDataStatus:=0;
        FNextLength:=0;
        FCurrentLength:=0;
        FReceiveFrame.Str:=FtmpStr;
        SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));
      end;
    end;
  end; 


佛山-sky  A (11116580) 20:55:21


第三个阶段的读法跟前面一样,都是读一个计一个,直到读完需要的字节为止。 


 J:=FCurrentLength;
      repeat
        ComPort.ReadStr(S1,1);
        FtmpStr:=FtmpStr+S1;
        FCurrentLength:=FCurrentLength+1;
     until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count); 


读好了就初始化:


if FCurrentLength=FNextLength then
      begin
        FDataStatus:=0;
        FNextLength:=0;
        FCurrentLength:=0;
        FReceiveFrame.Str:=FtmpStr; 
 SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));  


最后就得到一帧完整的数据FtmpStr,然后将这个数据发送给处理数据的函数。


SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));


相关的函数就会来处理它了。 


这样,不关什么东西来,什么时候来,一帧完整的数据都在FtmpStr这个字符串里 


当然具体的读法还要根据数据帧的格式来定,但是万变不离其宗:就是将读到的字节跟给定的格式比较。 


有了这些知识,就基本上可以应付串口通讯程序的编写了。 


考虑到Delphi面向对象的特性,如果不用类来规范数据帧的格式似乎有些可惜了。 


还是拿刚才那帧数据格式来举例: 


我们可以这样定义基类。


  TCustomGY = class(TObject)
  //0 规约基类
  private
    FFrameBegin:Byte;               //帧起始符
    FTerminalLogicAddr:T4Byte;      //终端逻辑地址
    FMasterStation:T2Byte;          //主站地址与命令序号
    FFrameBegin2:Byte;              //帧起始符
    FFrameControl:Byte;             //控制码
    FDataLength:Word;               //数据长度
    FFrameVerify:Byte;              //校验码
    FFrameEnd:Byte;                 //结束码
    FPosSend:TFramePosTerminal;     //帧发起端
    FPosReceive:TFramePosTerminal;  //帧接收端
    procedure SetDataLength(const Value: Word);
    procedure SetFrameBegin(const Value: Byte);
    procedure SetFrameBegin2(const Value: Byte);
    procedure SetFrameControl(const Value: Byte);
    procedure SetFrameEnd(const Value: Byte);
    procedure SetFrameVerify(const Value: Byte);
    function ReadFrameDataLengthHi: Byte;
    function ReadFrameDataLengthLo: Byte;
    function ReadFrameFSEQ: Byte;
    function ReadFrameISEQ: Byte;
    function ReadFrameMSTA: Byte;
    function ReadFrameMSTA2: Byte;
    procedure SetPosReceive(const Value: TFramePosTerminal);
    procedure SetPosSend(const Value: TFramePosTerminal);
    procedure SetMS1(const Value: Byte);
    procedure SetMS2(const Value: Byte);
    procedure SetFrameFSEQ(const Value: Byte);
    procedure SetFrameISEQ(const Value: Byte);
    procedure SetFrameMSTA(const Value: Byte);
    procedure SetFrameMSTA2(const Value: Byte);
    function GetTerminalAddr: Word;
    function GetTerminalLogicAddr(const AIndex: T4Integer): Byte;
    procedure SetTerminalAddr(const Value: Word);
    procedure SetTerminalLogicAddr(const AIndex: T4Integer;
      const Value: Byte);
    function GetMasterStation(const AIndex: T2Integer): Byte;
    procedure SetMasterStation(const AIndex: T2Integer; const Value: Byte);
    function GetTerminalLogicAddrStr: string;
    procedure SetTerminalLogicAddrStr(const Value: string);
    function GetMasterstationStr: string;
    procedure SetMasterstationStr(const Value: string);
  protected


    procedure SetData(const Value: string); virtual; abstract;//设置数据区 虚函数,实现方法在子类中
    function ReadData:string; virtual; abstract;//ReadData虚函数,由子类重载.
    function ToVerifyFrame: Boolean; virtual;//帧是否正确
    function ReadWholeFrame: string; virtual;
    procedure setWholeFrame(const Value: string); virtual;
  public
    property FrameBegin:Byte read FFrameBegin write SetFrameBegin; //帧起始符
    property RTUA[const AIndex:T4Integer]:Byte
      read GetTerminalLogicAddr write SetTerminalLogicAddr; //终端逻辑地址
    property RTUAStr:string read GetTerminalLogicAddrStr write SetTerminalLogicAddrStr;
    property TerminalAddr:Word read GetTerminalAddr write SetTerminalAddr; //终端地址
    property MSTAs[const AIndex:T2Integer]:Byte read GetMasterStation write SetMasterStation; //主站地址与命令序号
    property MSTAStr:string read GetMasterstationStr write SetMasterstationStr;
    property FrameBegin2:Byte read FFrameBegin2 write SetFrameBegin2;      //第二个帧起始符
    property FrameControl:Byte read FFrameControl write SetFrameControl;   //控制码
    property DataLength:Word read FDataLength write SetDataLength;         //数据长度,2位,地位在前,高位在后
    property Data:string read ReadData write SetData;                      //数据,虚属性,在子类实现
    property CS:Byte read FFrameVerify write SetFrameVerify;      //帧校验位
    property FrameEnd:Byte read FFrameEnd write SetFrameEnd;               //帧结束码
    property FrameIsRight:Boolean read ToVerifyFrame;                      //检测帧是否正确
    property WholeFrame:string read ReadWholeFrame write setWholeFrame;    //整帧信息,虚函数,子类实现
    property MS1:Byte read FMasterStation[0] write SetMS1;                    //MS1
    property MS2:Byte read FMasterStation[1] write SetMS2;                    //MS2
    property MSTA:Byte read ReadFrameMSTA write SetFrameMSTA;                 //MSTA
    property MSTA2:Byte read ReadFrameMSTA2 write SetFrameMSTA2;              //MSTA2,发送端和接收端都为主站时有效
    property ISEQ:Byte read ReadFrameISEQ write SetFrameISEQ;                 //ISEQ
    property FSEQ:Byte read ReadFrameFSEQ write SetFrameFSEQ;                 //FSEQ
    property DataLengthLo:Byte read ReadFrameDataLengthLo;                    //数据长度低位
    property DataLengthHi:Byte read ReadFrameDataLengthHi;                    //数据长度高位
    property PosSend:TFramePosTerminal read FPosSend write SetPosSend;             //帧发送端
    property PosReceive:TFramePosTerminal read FPosReceive write SetPosReceive;    //帧接收端
    class function VerifyFrame(S:string): Boolean;  //帧是否正确
    class function GetVerifyByte(S:string):Byte;    //得到校验码
    constructor Create; virtual;
    destructor Destory; virtual;
    function DecodeFrame(const S:string;NoError:Boolean=True):Boolean; //dynamic; //分解操作后返回True
    function SumVerify:Byte; dynamic;               //计算数据帧CS码
    function SumDataLength:Word; dynamic;           //计算数据长度
  end; 


所有的判断处理的东西都交给这个类。


然后不同的命令再派生出不同的子类。


留两个接口,一个进一个出。


其他的那些中间数据就随意处置。 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -