Windows
95下串行通信編程技術(shù)及其實(shí)現(xiàn)
郭峰林 朱才連
摘 要 本文首先簡(jiǎn)單討論MSDOS、16位Windows和Windows95下的通信編程差別,然后著重講述32位Windows95
環(huán)境下的通信編程技術(shù),最后給出利用該技術(shù)實(shí)現(xiàn)串行通信的實(shí)例。 關(guān)鍵詞 API函數(shù),串行通信,中斷,查詢,線程,同步,異步,阻塞
TECHNIQUE OF SERIAL COMMUNICATION PROGRAMMING AND ITS
REALIZATION IN WINDOWS95
Guo Fenglin Zhu
Cailian Institute of Geodesy & Geophysics, Chinese Academy
of Sciences, Hubei.Wuhan 430077
Abstract This paper discusses the
difference of communication programming in the operating system of
MSDOS,16 bit Windows and Windows95.Then tells of some problems about
serial communication programming technique in 32 bit OS of windows95.
Finally, an example of the application of this technique in windows95is
provided. Keywords API function, Serial
communication, Interrupt, Poll, Thread, Synchronization,
Asynchronization
1 前言 Windows95以其形象的圖形界面設(shè)計(jì)、操作簡(jiǎn)單、功能強(qiáng)大、可靠性高等優(yōu)點(diǎn),贏得了越來(lái)越多的用戶,開發(fā)Windows95應(yīng)用程序已經(jīng)成為當(dāng)今的主流。在諸多的應(yīng)用開發(fā)中,與外部硬件設(shè)備通信是常見的應(yīng)用,而串行通信以其簡(jiǎn)單的硬件連接方式常常成為應(yīng)用開發(fā)者的首選。然而串行通信編程從MSDOS、Windows3.1到Windows95各不相同,雖然在功能上越來(lái)越強(qiáng),但是編程的復(fù)雜度也相應(yīng)增大。 筆者最近在Windows95環(huán)境下開發(fā)一套“公安110智能報(bào)警系統(tǒng)”,該系統(tǒng)需要對(duì)報(bào)警電話進(jìn)行實(shí)時(shí)監(jiān)控,以便能實(shí)時(shí)地進(jìn)行接警和處警。報(bào)警電話的監(jiān)控是通過(guò)檢測(cè)從電話交換機(jī)中饋送的RS232標(biāo)準(zhǔn)的串行通信信號(hào),其中串行口通信采用3線方式。該系統(tǒng)采用Windows95下的Visual
C++
5.0編寫,由于有關(guān)Windows95的串行口通信編程方面的資料少,串行通信編程的實(shí)例也不多見,筆者在成功開發(fā)“公安110智能報(bào)警系統(tǒng)”的基礎(chǔ)上,取得了一些經(jīng)驗(yàn),現(xiàn)在將有關(guān)串行口通信方面的一些關(guān)鍵技術(shù)寫出來(lái),供廣大的編程者借鑒、參考。
2 下串行通信編程特征 MSDOS下的串行通信編程較簡(jiǎn)單,通信編程可以直接對(duì)串口的物理地址進(jìn)行編程操作同時(shí)配合BIOS調(diào)用,即可實(shí)現(xiàn)串行口數(shù)據(jù)讀寫。 在Windows下,串行口作為系統(tǒng)資源,由設(shè)備驅(qū)動(dòng)程序統(tǒng)一管理,用戶不能象在MSDOS下一樣直接對(duì)串行口硬件端口進(jìn)行編程。16位的Windows3.1操作系統(tǒng)提供了專門的串行通信的API函數(shù):OpenComm()、CloseComm()、ReadComm()、
WriteComm()等,通過(guò)這些專用API(Application Programming
Interfaces)函數(shù)來(lái)設(shè)置和讀、寫串行口。而Windows95將串行口和其它通信設(shè)備如Modern、傳真機(jī)等統(tǒng)一視作文件,對(duì)串行口的打開、關(guān)閉、讀寫等操作與操作普通文件的API函數(shù)相同,如CreateFile()、CloseHandel()、ReadFile()、WriteFile(),正是由于這些函數(shù)的“多態(tài)性”,
同時(shí)還由于需要結(jié)合Windows95的線程編程、事件驅(qū)動(dòng)等新技術(shù),因而使得Windows95下的串行口通信編程比較復(fù)雜。
3 Windows95下串行通信API函數(shù) 在Windows95中將串行口與文件的統(tǒng)一了起來(lái),對(duì)它們的打開、讀、寫、關(guān)閉等操作都使用相同的API函數(shù),但是它們之間又有差別,譬如串行口不能象文件一樣可以被刪除,這些差別體現(xiàn)在API函數(shù)中部分參數(shù)的設(shè)置上。 弄清串行通信API函數(shù)的用法是掌握串行通信編程技術(shù)的關(guān)鍵。下面介紹幾個(gè)與串行通信編程密切相關(guān)的API函數(shù),著重說(shuō)明這些API函數(shù)在進(jìn)行串行通信時(shí)參數(shù)設(shè)置需要注意的地方。其它沒(méi)有提及的函數(shù)及參數(shù)可以參考Windows95
API函數(shù)手冊(cè)。 3.1 打開串行口API函數(shù) Windows95通信會(huì)話以調(diào)用CreateFile()函數(shù)打開串行口開始。調(diào)用CreateFile()打開串口成功,返回一個(gè)操作句柄。該句柄供隨后對(duì)串行口的設(shè)置、讀寫等操作用。 CreateFile()函數(shù)原型: HANDLE
CreateFile(LPCTSTR szDevice, DWORD dwAccess, DWORD dwShareMode,
LPSECURITY—ATTRIBUTES lpSA, DWORD dwCreate, DWORD
dwFlagsAndAttributes, HANDLE hTemplateFile
); 調(diào)用此函數(shù)要注意這幾個(gè)參數(shù)的設(shè)置:dwShareMode指定該端口的共享屬性。該參數(shù)是為文件共享提供的,串行口不能作為共享設(shè)備。故參數(shù)值必須為0,這是文件與通信設(shè)備之間的主要差異之一;dwCreate必須為OPEN—EXISTING。因?yàn)镃reateFile()只能打開存在的端口,而不能象創(chuàng)建新文件一樣創(chuàng)建物理上不存在的新串口;dwFlagsAndAttributes描述了該端口的各種屬性。對(duì)于文件來(lái)說(shuō),具有多種屬性(只讀、隱藏、系統(tǒng))是可能的,但是對(duì)于串行口,唯一有意義的設(shè)置是FILE—FLAG—OVERLAPPED;參數(shù)hTemplateFile必須為NULL。 返回值:若成功,返回創(chuàng)建的句柄;否則返回,INVALID—HANDLE—VALUE. 舉例:打開串行口1 HANDLE
hComm; //定義句柄變量 hComm = CreateFile( "COM1",
GENERIC—READ|GENERIC—WRITE,NULL,NULL, OPEN—EXISTING,FILE—FLAG—OVERLAPPED,NULL); if
(hComm == INVALID—HANDLE—VALUE) {……. //
打開串口錯(cuò)誤的處理}
3.2 配置串行口API函數(shù) 串行口打開成功,接下來(lái)可以配置串行口通信參數(shù)如波特率、數(shù)據(jù)位數(shù)、停止位、校驗(yàn)位等。修改這些參數(shù)時(shí)要和設(shè)備控制塊DCB(Device
Control
Block)打交道,DCB有近30個(gè)數(shù)據(jù)成員,是一個(gè)很復(fù)雜的數(shù)據(jù)結(jié)構(gòu),全部弄清楚它們的含義相當(dāng)費(fèi)時(shí)。而對(duì)于采用3線方式的串行通信來(lái)說(shuō),DCB結(jié)構(gòu)中絕大多數(shù)參數(shù)可以不予考慮,因?yàn)橹灰O(shè)置好波特率、數(shù)據(jù)位、停止位、校驗(yàn)位等幾個(gè)關(guān)鍵參數(shù)就行。這里介紹一種簡(jiǎn)捷的方法可以做到不了解DCB的詳細(xì)內(nèi)容也可以設(shè)置好串行通信參數(shù)。 通過(guò)下面的程序來(lái)說(shuō)明串行通信參數(shù)的設(shè)置方法。例程中利用BuildCommDCB函數(shù)來(lái)設(shè)置DCB,然后用函數(shù)SetCommState()配置串行通信口。 DCB
dcb
; //定義設(shè)備控制塊 GetCommState(hComm,&dcb); //取出系統(tǒng)缺省設(shè)備控制塊 BuildCommDCB("COM2:9600,N,8,1",&dcb);
//設(shè)置DCB主要參數(shù) SetCommState(hComm,&dcb); 3.3 超時(shí)設(shè)置API函數(shù) 編寫通信應(yīng)用程序的一個(gè)很關(guān)鍵的問(wèn)題就是如何處理通信中的不可預(yù)測(cè)的事件。譬如接收數(shù)據(jù)過(guò)程中突然被中斷,或者發(fā)送數(shù)據(jù)突然停止等等。如果不認(rèn)真對(duì)待,這些情況可能會(huì)引起I/O線程掛起或者線程被無(wú)限阻塞。Windows95對(duì)于這類問(wèn)題提供了安全措施,它讓你通過(guò)超時(shí)設(shè)置來(lái)決定通信是否異常并作相應(yīng)處理。因此超時(shí)設(shè)置在串行通信中顯得尤為重要。 超時(shí)設(shè)置過(guò)程分為兩步,首先設(shè)置COMMTIMEOUTS結(jié)構(gòu)中的五個(gè)變量,然后調(diào)用SetCommTimeouts()函數(shù)設(shè)置超時(shí)值。COMMTIMEOUTS結(jié)構(gòu)的定義如下: typedef
struct — COMMTIMEOUTS { DWORD ReadIntervalTimeout;
//讀端口間隔超時(shí) DWORD
ReadTotalTimeoutMultiplier; //讀端口總超時(shí)乘數(shù) DWORD
ReadTotalTimeoutConstant; //讀端口總超時(shí)常數(shù)(ms) DWORD
WriteTotalTimeoutMultiplier; //寫端口總超時(shí)乘數(shù) DWORD
WriteTotalTimeoutConstant; //寫端口總超時(shí)常數(shù)(ms) }
COMMTIMEOUTS,*LPCOMMTIMEOUTS; 3.4 讀串口API函數(shù) 串行口打開后,可以對(duì)它進(jìn)行讀寫操作。讀串行口的函數(shù)原型: BOOL
ReadFile (HANDLE hFile, LPVOID lpBuffer, DWORD
nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED
lpOverlapped
data); 其中,第一個(gè)參數(shù)hFile是由CreateFile()返回的句柄。參數(shù)lpBuffer是讀取的數(shù)據(jù)緩沖區(qū)指針,要注意給該數(shù)據(jù)緩沖區(qū)分配足夠的空間;參數(shù)nNumberOfBytesToRead是要讀取的字節(jié)數(shù);參數(shù)lpNumberOfBytesRead是實(shí)際讀取的字節(jié)數(shù);最后一個(gè)參數(shù)lpOverlapped
是指向一個(gè)可重疊I/O(異步)的數(shù)據(jù)結(jié)構(gòu)指針。如果lpOverlapped設(shè)置為NULL,則ReadFile()工作在同步方式;如果lpOverlapped指向一個(gè)重疊結(jié)構(gòu),則工作在異步方式。 3.5 寫串口API函數(shù) BOOL WriteFile (HANDLE
hFile, // 由CreateFile()返回的句柄 LPCVOID
lpBuffer, // 寫緩沖區(qū)指針 DWORD
nNumberOfBytesToWrite, // 要寫的字節(jié)數(shù) LPDWORD
lpNumberOfBytesWritten, // 實(shí)際寫的字節(jié)數(shù) LPOVERLAPPED
lpOverlapped //
指向一個(gè)可重疊I/O的數(shù)據(jù)結(jié)構(gòu)); WriteFile()函數(shù)的工作方式選擇與ReadFile()的相同,在此不重復(fù)。 3.6 關(guān)閉串口API函數(shù) 串行口是非共享資源,某應(yīng)用程序打開串行口后,即獨(dú)占該資源,使其它應(yīng)用程序無(wú)法再訪問(wèn),直到該應(yīng)用程序釋放串口。所以打開串口后,一定要關(guān)閉串口。關(guān)閉串口函數(shù)較簡(jiǎn)單。函數(shù)原型:BOOL
CloseHandle( HANDLE hObject
);其中hObject參數(shù)為CreateFile()返回的端口句柄。返回值非0,則調(diào)用成功。
4 Windows95的串行通信工作方式 串行通信會(huì)話以調(diào)用CreateFile()函數(shù)打開串行口開始,接著設(shè)置串行口波特率、數(shù)據(jù)位、校驗(yàn)位、停止位等參數(shù)以及超時(shí)參數(shù),最后選擇一種工作方式讀、寫串行口。在Windows95中,串行通信有兩種工作方式可供選擇:查詢方式和事件驅(qū)動(dòng)方式。這兩種工作方式各有優(yōu)缺點(diǎn),用戶可以根據(jù)應(yīng)用程序的實(shí)際需要選擇其中的一種工作方式,下面對(duì)這兩種工作方式分別介紹。 4.1 查詢方式 對(duì)于從串口讀取數(shù)據(jù)來(lái)說(shuō),查詢是最為直接、易于理解的技術(shù)。但是查詢會(huì)占用大量的CPU時(shí)間,效率較低。利用查詢方式讀取串口數(shù)據(jù)時(shí)通常要建立一個(gè)線程,建立線程使用CreateThread()函數(shù)。循環(huán)查詢?cè)诰程里進(jìn)行。舉例:(假設(shè)端口已經(jīng)打開) DWORD
ReadThread(LPDWORD lpdwParam) { BYTE Buff[100];
//讀數(shù)據(jù)緩沖區(qū) DWORD nBytesRead;
//實(shí)際讀取的字節(jié)數(shù) COMMTIMEOUTS
Timeouts; //超時(shí)設(shè)置 Memset(&Timeouts,0,sizeof(COMMTIMEOUTS)); Timeouts.ReadIntervalTimeout
=
MAXDWORD; SetCommTimeouts(hComm,&Timeouts); While(bReading){ if(!ReadFile(hComm,Buff,100,&nBytesRead,NULL)) {……… //讀取數(shù)據(jù)出錯(cuò)處理} else{……… //正確讀取數(shù)據(jù)的處理} } PurgeComm(hComm,PURGE—RXCLEAR); return
0; } 例程中,線程的退出由bReading標(biāo)志控制,當(dāng)bReading為TRUE時(shí),循環(huán)串口;當(dāng)breading為FALSE時(shí),線程退出。 4.2 事件驅(qū)動(dòng)方式 事件驅(qū)動(dòng)I/O方式是指線程通過(guò)監(jiān)視通信資源中的一組事件來(lái)進(jìn)行I/O操作,這種方式類似于MSDOS下的中斷工作方式,效率高?杀槐O(jiān)視的事件列表如下: |