| |||||||||||||||||
| |||||||||||||||||
| 正文: | ||
Delphi中串行通訊的實現 隨著現代資訊科技的發展以及電腦網路的廣泛使用,電腦通訊技術已日臻成熟,但串行通訊作為一種靈活方便可靠的通訊方式,仍不失為有效的通訊手段,被廣泛應用於工業控制中。在工業生產實務中,使用PC機對工程實現即時監控,通常要求PC機能在使用者介面上具有資料擷取、資料處理以及控制訊號的產生與傳輸等功能。在這種特定的環境下,PC機要與製程控制的即時訊號相聯繫,就要求能實現對PC機的串列埠直接操作。 Borland公司推出的Delphi是一種功能強大的高階程式語言,其具有的視覺化物件導向的特性,特別適合在Windows環境下圖形介面和使用者程式的編制。 基於WIN95/NT的串行通訊機制 Windows作業系統的機制禁止應用程式直接存取電腦硬件,但它為程式設計師提供了一系列的標準API函數,使得應用程式的編制更加方便並且免除了對有關硬體的調試麻煩。在Windows95/NT中,原來Windows3.X的WM_COMMNOTIFY訊息已被取消,作業系統為每個通訊設備開闢了用戶可定義大小的讀/寫緩衝區,資料進出通訊口均由作業系統後台完成,應用程式只需對讀取/寫入緩衝區操作即可。 WIN95/NT中幾個常用的串列通訊操作函數如下: CreatFile開啟序列埠 CloseHandle關閉串行口 SetupComm設定通訊緩衝區的大小 ReadFile讀取串列埠操作 WriteFile寫入串口操作 SetCommState設定通訊參數 GetCommState取得預設通訊參數 ClearCommError清除串列埠錯誤並取得目前狀態 除上述幾個函數外,還要常用到一個重要的記錄DCB(設備控制塊)。 DCB中記錄有可定義的串行口參數,設定串行口參數時必須先用GetCommState函數將系統預設值填入DCB控制塊,然後才可設定用戶想改變的自訂值。 在WIN95/NT中進行串列通訊除了解基本的通訊操作函數外,還要掌握多執行緒程式設計。執行緒是進程內部執行的路徑,是作業系統分配CPU時間的基本實體。每個進程都由單執行緒開始完成應用程式的執行。串行通訊需要利用多線程技術實現,其主要的處理邏輯可以表述如下:進程一開始先由主線程做一些必要的初始化工作,然後主線程根據需要在適當時候建立通信監視線程監視通信口,當指定的序列埠事件發生時,向主執行緒傳送WM_COMMNOTIFY訊息(由於WIN95取消了WM_COMMNOTIFY訊息,因此必須自行建立),主執行緒對其進行處理。若不需要WM_COMMNOTIFY訊息,則主執行緒終止通訊監視執行緒。 多執行緒同時執行,將會引起對共享資源的衝突。為避免衝突,就要用同步多執行緒對共享資源進行存取。 WIN95提供許多保持執行緒同步的方法,筆者採用建立事件物件來保持執行緒同步。透過CraeteEvent()建立事件對象,使用etEvent()或PulseEvent()函數將事件對象設定成訊號同步。在應用程式中,利用WaitSingleObject()函數等待同步的觸發,等到指定的事件被其它執行緒設定為有訊號時,才繼續向下執行程式。 Delphi下的具體實作方法 Delphi的強大功能和支援多執行緒的物件導向程式設計技術,使得實現串行通訊非常簡單方便。它透過呼叫外部的API函數來實現,主要步驟如下:首先,利用CreateFile函數開啟串列埠,以確定本應用程式對此串列口的佔有權,並封鎖其它應用程式對此串列口的操作;其次,透過GetCommState函數填滿裝置控制區塊DCB,再透過呼叫SetCommState函數來配置序列埠的波特率、資料位元、校驗位和停止位元。然後,建立串行口監視執行緒監視串行口事件。在此基礎上就可以在對應的串列埠上操作資料的傳輸;最後,用CloseHandle函數關閉串列口。具體的程序如下,本程式以Delphi3.0編製在Win95 環境下調試通過,已投入實際應用中,供廣大讀者參考。 程式: 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 或 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. 作者會員名稱:ruan_bangqiu | ||