首页  编辑  

COM串口编程

Tags: /超级猛料/Hardware.硬件相关/其它硬件/   Date Created:

COM编程

1. 调用CreateFile创建Comm口的句柄;

 fhComm : THandle;

 ..

 fhComm := CreateFile(PChar(strCommName),

                      GENERIC_READ or GENERIC_WRITE,

                      0,    //Exclusive Access

                      nil,  //No Security Attribute

                      OPEN_EXISTING,

                      FILE_FLAG_OVERLAPPED,

                      0);   //Template File

 if fhComm = INVALID_HANDLE_VALUE then

  //出错处理

 其中strCommName是一个string类型的,值如'COM1', 'COM2'等等

 

2. 调用SetupComm设置Comm通信的发送与接收缓冲区大小;  

3. 调用PurgeComm清空发送与接收缓冲区

 if not PurgeComm(fhComm, PURGE_RXABORT or PURGE_RXCLEAR or

                  PURGE_TXABORT or PURGE_TXCLEAR) then

 //出错处理

4. 充分使用Overlapped I/O提高效率就要设置读写的TimeOut, 调用例程如下:

 cto : TCommTimeOuts;

 ..

 cto.ReadIntervalTimeout := $ffffffff;

 cto.ReadTotalTimeoutMultiplier := 0;

 cto.ReadTotalTimeoutConstant := 1000; //1 second

 cto.WriteTotalTimeoutMultiplier := 2*CBR_9600 div CBR_19200;

 cto.WriteTotalTimeoutConstant := 0;

 if not SetCommTimeOuts(fhComm, cto) then

 //出错处理

5. 设置COMM口的Data Control Block的属性:

 dcb : TDCB;

 dcbFlag : integer;

 ..

 dcb.DCBlength := SizeOf(TDCB);

 if not GetCommState(fhComm, dcb) then

 //出错处理

 

   dcb.DCBlength := SizeOf(TDCB);

 dcb.BaudRate := nBaud;

 dcb.ByteSize := 8;

 dcb.Parity := ODDPARITY;

 dcb.StopBits := ONESTOPBIT;

 dcb.XonChar := Chr($11);  //Ctrl_Q

 dcb.XoffChar := Chr($13); //Ctrl_S

 dcb.ErrorChar := Chr(0);

 dcb.XonLim := 100;

 dcb.XoffLim := 100;

 dcbFlag := 1;            //Binary must be True

 dcbFlag := dcbFlag or 2; //Parity Check True

 dcbFlag := dcbFlag or 4; //FOutxCtsFlowCtrl

 dcbFlag := dcbFlag or 8; //FOutxDsrFlowCtrl

 dcbFlag := dcbFlag or $10;  //DsrEnable

//     dcbFlag := dcbFlag or $20;   //DtrHandShake

 dcbFlag := dcbFlag or $1000; //RtsEnable

//     dcbFlag := dcbFlag or $800;  //Ignore NULL Char

 dcb.Flags := dcbFlag;

 if not SetCommState(fhComm, dcb) then

 //出错处理

6. 使RTS和DTR设置为高电平,这是RS232通信一般需要的

 if not EscapeCommFunction(fhComm, SETDTR) then

 //出错处理

 if not EscapeCommFunction(fhComm, SETRTS) then

 //出错处理

7. 设置好读和写的Overlapped i/o对应的结构体:

  readOl, readOpOl: TOverLapped;

  writeOl : TOverLapped;

  ..

  FillChar(writeOl, SizeOf(writeOl), 0);

  writeOl.hEvent := CreateEvent(nil, True, False, nil);

  FillChar(readOl, SizeOf(readOl), 0);

  readOl.hEvent := CreateEvent(nil, True, False, nil);

  FillChar(readOpOl, SizeOf(readOpOl), 0);

  readOpOl.hEvent := CreateEvent(nil, True, False, nil);

  其中readOpOl是用来进行读操作事件响应的overlapped i/o操作的

overlapped 结构体

 

8. 读操作:

 SetCommMask(fhComm, EV_RXCHAR);

 用来表示我们对EV_RXCHAR事件感兴趣,有Char来到的时候系统会通知我们

 

 dwEventMask := 0;

 WaitCommEvent(fhComm, dwEventMask, @readOpOl);

 上面这两句表示我们要等待设置好的事件的发生(上面一句我们已经设置了

EV_RXCHAR事件),由于有一个readOpOl,表示我们进行的是overlapped等待,不会

被这个等待堵塞住

 然后下面就要调用WaitForSingleObject或者是WaitForMultiObjects来等待

设置好的Event的发生了(代码省略,至少你要自己看看Event和WaitFor???Object

函数的帮助)

 

 下面你可以调用ClearCommError来得到Error,然后得到Comm口接收缓冲区里面

数据的大小:

 commStat : TComStat;

 ..

     ClearCommError(fhComm, dwErr, @commStat);

     if commStat.cbInQue >= 0 then

     ...

 再下面你要调用ReadFile来读取数据:

 pchToRecv : LPSTR;

 byteToRecv : array [0..nBuffLen-1] of Char;

 ..

 pchToRecv := LPSTR(LocalAlloc(LPTR, nBuffLen+1));

 

 if not ReadFile(fhComm, pchToRecv^, dwLength, dwLength, @readOl) then

 begin

   if GetLastError=ERROR_IO_PENDING then

   begin

     while not GetOverLappedResult(fhComm, readOl, dwLength, TRUE) do

     begin

       dwErr := GetLastError;

       if dwErr=ERROR_IO_INCOMPLETE then

         continue

       else begin

         ClearCommError(fhComm, dwError, @commStat);

         fStrErr := 'OS Synchronization Error!';

       end;

     end;  //end while

     ResetEvent(readOl.hEvent);

   end // end if GetLastError...

   else begin

     ClearCommError(fhComm, dwError, @commStat);

     fStrErr := 'OS Synchronization Error!';

   end;

 end; //end if dwLength>0 , end if not ReadFile...

 Result := dwLength;

 ...    

 CopyMemory(@byteToRecv, pchToRecv, nBuffLen);

 ...

 LocalFree(THandle(pchToRecv));

 

 在上面我用了一个LPSTR来接收数据,然后还使用了LocalAlloc, LocalFree

等这样的函数,这似乎是唯一的方法,直接使用array of char好像会出问题。

 另外上面的读取部分是我写在一个函数里面的,所以还有一个Result返回读

取的数据的多少,另外还有由于使用的是overlapped i/o,所以如果得到的错误

是i/o pending的错误是正常的。

9. 写操作:

 ClearCommError(fhComm, dwErr, @commStat);

 if commStat.cbOutQue>(2*nBuffLen + nBuffLen div 2) then

 //我自己判断发送缓冲区大小的阈值,如果大了就下次发送

  bWriteFile := WriteFile(fhComm, byteToSend, nToWrite, dwLength, @writeOl);

 if not bWriteFile then

 begin

   dwErr := GetLastError;

   if dwErr = ERROR_IO_PENDING then

   begin

     while not GetOverLappedResult(fhComm, writeOl, dwLength, TRUE) do

     begin

       dwErr := GetLastError;

       if dwErr = ERROR_IO_INCOMPLETE then

         continue

       else begin

         ClearCommError(fhComm, dwError, @commStat);

         fStrErr := 'OS synchronization error!';

       end;

     end; //end while

     //If you want to check error, call GetCommModemStatus

     ResetEvent(writeOl.hEvent);

   end // end if GetLastError=...

   else begin

     ClearCommError(fhComm, dwError, @commStat);

     fStrErr := 'OS synchronization error!';

   end;

 end;  //end if not bWriteFile

 Result := dwLength;

10. 关闭Comm口

 SetCommBreak(fhComm);

 //Break to notify the peer interrupt to end the transfer

 EscapeCommFunction(fhComm, CLRDTR);

 EscapeCommFunction(fhComm, CLRRTS);

 PurgeComm(fhComm, PURGE_RXCLEAR or PURGE_RXABORT or

                   PURGE_TXCLEAR or PURGE_TXABORT);

 SetCommMask(fhComm, 0);

 CloseHandle(fhComm);

 CloseHandle(writeOl.hEvent);

 CloseHandle(readOl.hEvent);

 CloseHandle(readOpOl.hEvent);

11.另外你还需要用的函数可能是

 GetCommModemStatus,至于用法么,xixi, 自己查吧。

----------------------------------------

发信人: erain (红花会主), 信区: Delphi

标  题: 串口传输的API编程已经基本可用了

发信站: BBS 水木清华站 (Fri Feb 18 11:22:26 2000) WWW-POST

unit U_Main;

 interface

 uses

   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs

,

   StdCtrls, ExtCtrls, Math, fileCtrl;

 const

   nBuffLen = 1024;

   MEMOMAX = 100;

 type

   TCommDaemonThd = class(TThread)

     private

       strFile, strInfo : string;

       mOut : TMemo;

       hComm : THandle;

       pchToRecv : LPSTR;

       function ReadComm(dwLength : DWORD): integer;

       procedure ShowStatus;

     protected

       procedure Execute;override;

     public

       hFile : File;

       bClosed : Boolean;

       constructor Create(memo: TMemo; comm: THandle; sFile: string);

   end;

   TWriteCommThd = class(TThread)

     private

       byteToSend : array[0..nBuffLen-1] of Char;

       pchToSend : LPSTR;

       bEndSend : Boolean;

       strFile : string;

       mOut : TMemo;

       hComm : THandle;

       dwError : DWORD;

       nCount : integer;

       strInfo : string;

       function WriteComm(nToWrite: integer): integer;

       procedure ShowStatus;

       procedure CleanUp;

     protected

       procedure Execute;override;

     public

       hFile : File;

       constructor Create(memo: TMemo; comm: THandle);

   end;

   TfrmMain = class(TForm)

     btnConnect: TButton;

     btndisConnect: TButton;

     radioComm: TRadioGroup;

     Label1: TLabel;

     eSendFile: TEdit;

     eRecvFile: TEdit;

     Label2: TLabel;

     btnSend: TButton;

     mStatus: TMemo;

     Label3: TLabel;

     dlgOpen: TOpenDialog;

     btnStop: TButton;

     procedure eSendFileDblClick(Sender: TObject);

     procedure frmMainCreate(Sender: TObject);

     procedure frmMainDestroy(Sender: TObject);

     procedure btnConnectClick(Sender: TObject);

     procedure btndisConnectClick(Sender: TObject);

     procedure btnSendClick(Sender: TObject);

     procedure btnStopClick(Sender: TObject);

   private

     thdDaemon : TCommDaemonThd;

     thdWrite : TWriteCommThd;

     hComm : THandle;

     procedure EnableCtrl(bEnable : Boolean);

   public

     strCommName : string;

     bConnected : Boolean;

     writeOl, readOl : TOverLapped;

   end;

 var

   frmMain: TfrmMain;

 implementation

 {$R *.DFM}

 procedure TfrmMain.eSendFileDblClick(Sender: TObject);

 begin

   dlgOpen.Title := 'Select Send File';

   if dlgOpen.Execute then

     eSendFile.Text := dlgOpen.FileName;

 end;

 procedure TfrmMain.frmMainCreate(Sender: TObject);

 begin

   bConnected := False;

   writeOl.Offset := 0;

   writeOl.OffsetHigh := 0;

   writeOl.hEvent := CreateEvent(nil, True, False, nil);

   readOl.Offset := 0;

   readOl.OffsetHigh := 0;

   readOl.hEvent := CreateEvent(nil, True, False, nil);

 end;

 procedure TfrmMain.frmMainDestroy(Sender: TObject);

 begin

   if bConnected then

    btndisConnectClick(Sender);

   CloseHandle(writeOl.hEvent);

   CloseHandle(readOl.hEvent);

 end;

 procedure TfrmMain.btnConnectClick(Sender: TObject);

 var

   cto : TCommTimeOuts;

   dcb : TDCB;

   dcbFlag : integer;

 begin

   if not FileExists(eSendFile.Text) then

   begin

     if mStatus.Lines.Count=MEMOMAX then

       mStatus.Lines.Delete(0);

     mStatus.Lines.Append('File not Exists or Directory not Exists!');

     Beep;

     Exit;

   end;

   if radioComm.ItemIndex=0 then

     strCommName := 'COM1'

   else

     strCommName := 'COM2';

   try

     hComm := CreateFile(PChar(strCommName),

                         GENERIC_READ or GENERIC_WRITE,

                         0, //Exclusive Access

                         nil,  //No Security Attribute

                         OPEN_EXISTING,

                         FILE_FLAG_OVERLAPPED,

                         0); //Template File

      if hComm=INVALID_HANDLE_VALUE then

        raise Exception.Create(strCommName+' Open Failed!');

    except

      if mStatus.Lines.Count=MEMOMAX then

        mStatus.Lines.Delete(0);

      mStatus.Lines.Append(strCommName+' Open Failed!');

      Beep;

      Exit;

    end;

    try

      if not SetCommMask(hComm, EV_RXCHAR) then

        raise Exception.Create(strCommName+' Setup Step 1 Failed!');

      if not SetupComm(hComm, 4*nBuffLen, 4*nBuffLen) then

        raise Exception.Create(strCommName+' Setup Step 1 Failed!');

      if not PurgeComm(hComm, PURGE_RXABORT or PURGE_RXCLEAR or

                       PURGE_TXABORT or PURGE_TXCLEAR) then

        raise Exception.Create(strCommName+' Setup Step 1 Failed!');

      cto.ReadIntervalTimeout := $ffffffff;

      cto.ReadTotalTimeoutMultiplier := 0;

      cto.ReadTotalTimeoutConstant := 1000; //1 second

      cto.WriteTotalTimeoutMultiplier := 2*CBR_9600 div CBR_19200;

      cto.WriteTotalTimeoutConstant := 0;

      if not SetCommTimeOuts(hComm, cto) then

        raise Exception.Create(strCommName+' Setup Step 1 Failed!');

    except

      CloseHandle(hComm);

      if mStatus.Lines.Count=MEMOMAX then

        mStatus.Lines.Delete(0);

      mStatus.Lines.Append(strCommName+' Setup Step 1 Failed!');

      Beep;

      Exit;

    end;

    try

      dcb.DCBlength := SizeOf(TDCB);

      if not GetCommState(hComm, dcb) then

        raise Exception.Create(strCommName+' Setup Step 2 Failed!');

      dcb.DCBlength := SizeOf(TDCB);

      dcb.BaudRate := CBR_38400;

      dcb.ByteSize := 8;

      dcb.Parity := NOPARITY;

      dcb.StopBits := ONESTOPBIT;

      dcb.XonChar := Chr($11);  //Ctrl_Q

      dcb.XoffChar := Chr($13); //Ctrl_S

      dcb.ErrorChar := Chr(0);

      dcb.XonLim := 100;

      dcb.XoffLim := 100;

      dcbFlag := 1;            //Binary must be True

      dcbFlag := dcbFlag or 2; //Parity Check True

      dcbFlag := dcbFlag or 4; //FOutxCtsFlowCtrl

      dcbFlag := dcbFlag or 8; //FOutxDsrFlowCtrl

      dcbFlag := dcbFlag or $20;   //DtrHandShake

      dcbFlag := dcbFlag or $1000; //RtsEnable

 //     dcbFlag := dcbFlag or $800;  //Ignore NULL Char

      dcb.Flags := dcbFlag;

      if not SetCommState(hComm, dcb) then

        raise Exception.Create(strCommName+' Setup Step 2 Failed!');

      bConnected := True;

    except

      CloseHandle(hComm);

      if mStatus.Lines.Count=MEMOMAX then

        mStatus.Lines.Delete(0);

      mStatus.Lines.Append(strCommName+' Setup Step 2 Failed!');

      Beep;

      Exit;

    end;

    EscapeCommFunction(hComm, SETDTR);

    EscapeCommFunction(hComm, SETRTS);

    thdDaemon := TCommDaemonThd.Create(mStatus, hComm, eRecvFile.Text);

    EnableCtrl(False);

    if mStatus.Lines.Count=MEMOMAx then

      mStatus.Lines.Delete(0);

    mStatus.Lines.Append('Connection setup successfully!');

 end;

 procedure TCommDaemonThd.Execute;

 var

   dwEventMask, dwErr, dwLength: DWORD;

   commStat : TComStat;

   nTotal, nLength, nLen : integer;

   bContinue : Boolean;

 begin

   bContinue := True;

   nTotal := 0;

   SetCommMask(hComm, EV_RXCHAR);

   SetLength(strInfo, 0);

   AssignFile(hFile, strFile);

   ReWrite(hFile, 1);

   bClosed := False;

   while bContinue do

   begin

     dwEventMask := 0;

     WaitCommEvent(hComm, dwEventMask, nil);

     if ( (dwEventMask and EV_RXCHAR)=EV_RXCHAR ) then //Char Received

     repeat

       ClearCommError(hComm, dwErr, @commStat);

       dwLength := min( nBuffLen, commStat.cbInque);

       pchToRecv := LPSTR(LocalAlloc(LPTR, dwLength+1));

       nLength := ReadComm(dwLength);

       nTotal := nTotal + nLength;

       if nLength<>0 then

       begin

         BlockWrite(hFile, pchToRecv^, nLength, nLen);

         strInfo := IntToStr(nTotal)+' bytes successfully received!';

         Synchronize(ShowStatus);

       end;

       LocalFree(THandle(pchToRecv));

     until nLength=0;

     PurgeComm(hComm, PURGE_RXCLEAR or PURGE_RXABORT);

   end;

   CloseFile(hFile);

   bClosed := True;

 end;

 constructor TCommDaemonThd.Create(memo: TMemo; comm: THandle; sFile: strin

g);

 begin

   strFile := sFile;

   mOut := memo;

   hComm := comm;

   bClosed := True;

   FreeOnTerminate := True;

   inherited Create(False);

 end;

 procedure TCommDaemonThd.ShowStatus;

 begin

   if mOut.Lines.Count=MEMOMAX then

     mOut.Lines.Delete(0);

   mOut.Lines.Append(strInfo);

 end;

 function TCommDaemonThd.ReadComm(dwLength : DWORD):integer;

 var

   dwErr, dwError : DWORD;

   commStat : TComStat;

 begin

   if dwLength>0 then

   if not ReadFile(hComm, pchToRecv^, dwLength, dwLength,

@frmMain.readOl)

then

   begin

     if GetLastError=ERROR_IO_PENDING then

     begin

       while not GetOverLappedResult(hComm, frmMain.readOl, dwLength, TRUE)

do

       begin

         dwErr := GetLastError;

         if dwErr=ERROR_IO_INCOMPLETE then

           continue

         else begin

           ClearCommError(hComm, dwError, @commStat);

           strInfo := 'Error: '+IntToStr(dwError);

           Synchronize(ShowStatus);

         end;

       end;  //end while

     end // end if GetLastError...

     else begin

       ClearCommError(hComm, dwError, @commStat);

       strInfo := 'Error: '+IntToStr(dwError);

       Synchronize(ShowStatus);

     end;

   end; //end if dwLength>0 , end if not ReadFile...

   Result := dwLength;

 end;

 procedure TfrmMain.btndisConnectClick(Sender: TObject);

 begin

   thdDaemon.Suspend;

   if not thdDaemon.bClosed then

     CloseFile(thdDaemon.hFile);

   thdDaemon.Free;

   if btnStop.Enabled then

   begin

     thdWrite.Suspend;

     CloseFile(thdWrite.hFile);

     thdWrite.Free;

     btnSend.Enabled := True;

     btnStop.Enabled := False;

   end;

   EscapeCommFunction(hComm, CLRDTR);

   EscapeCommFunction(hComm, CLRRTS);

   PurgeComm(hComm, PURGE_RXCLEAR or PURGE_RXABORT or

                    PURGE_TXCLEAR or PURGE_TXABORT);

   bConnected := False;

   SetCommMask(hComm, 0);

   CloseHandle(hComm);

   EnableCtrl(True);

   if mStatus.Lines.Count=MEMOMAx then

     mStatus.Lines.Delete(0);

   mStatus.Lines.Append('Disconnected successfully!');

 end;

 procedure TfrmMain.EnableCtrl(bEnable : Boolean);

 begin

   btnConnect.Enabled := bEnable;

   btnSend.Enabled := not bEnable;

   btndisConnect.Enabled := not bEnable;

   radioComm.Enabled := bEnable;

   eRecvFile.Enabled := bEnable;

 end;

 procedure TWriteCommThd.Execute;

 var

   nStart, nEnd, nLen, nByteToWrite : integer;

 begin

   nStart := GetTickCount;

   nCount := 0;

   strFile := frmMain.eSendFile.Text;

   AssignFile(hFile, strFile);

   ReSet(hFile, 1);

   nLen := FileSize(hFile);

   while not bEndSend do

   begin

     BlockRead(hFile, byteToSend, nBuffLen, nByteToWrite);

     pchToSend := byteToSend;

     PurgeComm(hComm, PURGE_TXCLEAR or PURGE_TXABORT);

     nCount := nCount + WriteComm(nByteToWrite);

     if nCount>= nLen then

       bEndSend := True;

   end;

   nEnd := GetTickCount-nStart;

   Synchronize(CleanUp);

   CloseFile(hFile);

   strInfo := 'Transfer completed: '+IntToStr(nLen)+' bytes transfered!';

   Synchronize(ShowStatus);

   strInfo := 'The transfer has taken '+IntToStr(nEnd)+' milliseconds';

   Synchronize(ShowStatus);

 end;

 constructor TWriteCommThd.Create(memo: TMemo; comm: THandle);

 begin

   bEndSend := False;

   hComm := comm;

   mOut := memo;

   FreeOnTerminate := True;

   inherited Create(False);

 end;

 function TWriteCommThd.WriteComm(nToWrite:integer): integer;

 var

   bWriteFile : Boolean;

   dwLength, dwErr: DWORD;

   commStat : TComStat;

 begin

   bWriteFile := WriteFile(hComm, pchToSend^, nToWrite, dwLength,

@frmMain.

writeOl);

   if not bWriteFile then

   begin

     dwErr := GetLastError;

     if dwErr=ERROR_IO_PENDING then

     begin

       while not GetOverLappedResult(hComm, frmMain.writeOl, dwLength, TRUE

) do

       begin

         dwErr := GetLastError;

         if dwErr=ERROR_IO_INCOMPLETE then

           continue

         else begin

           ClearCommError(hComm, dwError, @commStat);

           strInfo := 'Error: '+IntToStr(dwError);

           Synchronize(ShowStatus);

         end;

       end; //end while

       //If want to check error, call GetCommModemStatus

     end // end if GetLastError=...

     else begin

       ClearCommError(hComm, dwError, @commStat);

       strInfo := 'Error: '+IntToStr(dwError);

       Synchronize(ShowStatus);

     end;

   end;  //end if not bWriteFile

   Result := dwLength;

 end;

 procedure TfrmMain.btnSendClick(Sender: TObject);

 begin

   thdWrite := TWriteCommThd.Create(mStatus, hComm);

   btnSend.Enabled := False;

   btnStop.Enabled := True;

   if mStatus.Lines.Count=MEMOMAx then

     mStatus.Lines.Delete(0);

   mStatus.Lines.Append('Sending file...');

 end;

 procedure TfrmMain.btnStopClick(Sender: TObject);

 begin

   thdWrite.Suspend;

   CloseFile(thdWrite.hFile);

   thdWrite.Free;

   btnSend.Enabled := True;

   btnStop.Enabled := False;

   if mStatus.Lines.Count=MEMOMAx then

     mStatus.Lines.Delete(0);

   mStatus.Lines.Append('File transfer interrupted!');

 end;

 procedure TWriteCommThd.ShowStatus;

 begin

   if mOut.Lines.Count=MEMOMAX then

     mOut.Lines.Delete(0);

   mOut.Lines.Append(strInfo);

 end;

 procedure TWriteCommThd.CleanUp;

 begin

   frmMain.btnSend.Enabled := True;

   frmMain.btnStop.Enabled := False;

 end;

 end.