用Win32 API完成串行通信
發表時間:2023-08-21 來源:明輝站整理相關軟件相關文章人氣:
[摘要]串口是常用的計算機與外部串行設備之間的數據傳輸通道,由于串行通信方便易行,所以應用廣泛。我們可以利用Windows API 提供的通信函數編寫出高可移植性的串行通信程序。 在Win1...
串口是常用的計算機與外部串行設備之間的數據傳輸通道,由于串行通信方便易行,所以應用廣泛。我們可以利用Windows API 提供的通信函數編寫出高可移植性的串行通信程序。
在Win16中,可以利用OpenComm、CloseComm和WriteComm等函數打開、關閉和讀寫串口。但在Win32中,串口和其他通信設備均被作為文件處理,串口的打開、關閉和讀寫等操作所用的API函數與操作文件的函數相同。可通過CreateFile函數打開串口,通過CloseFile函數關閉串口,通過CommProp、DCB結構、GetCommProperties、SetCommProperties、GetCommState及SetCommState等函數設置串口狀態,通過函數ReadFile和WritFile讀寫串口。
VC++ 6.0是Windows應用程序開發的主流語言之一,它具有良好的圖形設計界面并支持面向對象的程序設計方法。本文結合一個實例介紹在VC++ 6.0下如何利用Win32 API 實現串行通信程序。
實現原理
本文的實例來自一個水泥發貨系統,在系統中,需要將通過總量傳感器采集到的倉重值傳入到計算機中,以便系統做出相應的處理。這需要使用串行通信來完成采集數據的傳遞工作。
對于串行通信設備,Win32 API支持同步和異步兩種I/O操作。同步操作方式的程序設計相對比較簡單,但I/O操作函數在I/O操作結束前不能返回,這將掛起調用線程,直到I/O操作結束。異步操作方式相對要復雜一些,但它可讓耗時的I/O操作在后臺進行,不會掛起調用線程,這在大數據量通信的情況下對改善調用線程的響應速度是相當有效的。異步操作方式特別適合同時對多個串行設備進行I/O操作和同時對一個串行設備進行讀/寫操作。這兩種操作方式的程序設計基本思想是相似的,本文將針對同步操作方式給出具體的通信程序設計,同時簡單說明如何實現異步的I/O操作。
串行設備的初始化
串行設備的初始化是利用CreateFile函數實現的。該函數獲得串行設備句柄并對其進行通信參數設置,包括設置輸出/接收緩沖區大小、超時控制和事件監視等。
//串行設備句柄;
HANDLE hComDev=0;
//串口打開標志;
BOOL bOpen=FALSE;
//線程同步事件句柄;
HANDLE hEvent=0;
BOOL SetupSynCom()
{
DCB dcb;
COMMTIMEOUTS timeouts;
//設備已打開
if(bOpen) return FALSE;
//打開COM1
if((hComDev=CreateFile(“COM1”,GENERICREAD GENERICWRITE,0,NULL,OPENEXISTING,FILEATTRIBUTENORMAL,NULL))==
INVALIDHANDLEVALUE)
return FALSE;
//設置超時控制
SetCommTimeouts(hComDev,&timeouts);
//設置接收緩沖區和輸出緩沖區的大小
SetupComm(hComDev,1024,512);
//獲取缺省的DCB結構的值
GetCommState(hComDev,&dcb);
//設定波特率為9600 bps
dcb.BaudRate=CBR9600;
//設定無奇偶校驗
dcb.fParity=NOPARITY;
//設定數據位為8
dcb.ByteSize=8;
//設定一個停止位
dcb.StopBits=ONESTOPBIT;
//監視串口的錯誤和接收到字符兩種事件
SetCommMask(hComDev,EVERR EVRXCHAR);
//設置串行設備控制參數
SetCommState(hComDev,&dcb);
//設備已打開
bOpen=TRUE;
//創建人工重設、未發信號的事件
hEvent=CreateEvent(NULL,FALSE,FALSE,
“WatchEvent”);
//創建一個事件監視線程來監視串口事件
AfxBeginThread(CommWatchProc,pParam);
}
在設置串口DCB結構的參數時,不必設置每一個值。首先讀出DCB缺省的參數設置,然后只修改必要的參數,其他參數都取缺省值。由于對串口進行的是同步I/O操作,所以除非指定進行監測的事件發生,否則WaitCommEvent函數不會返回。在串行設備初始化的最后要建立一個單獨的監視線程來監視串口事件,以免掛起當前調用線程,其中pParam可以是一個對事件進行處理的窗口類指針。
如果要進行異步I/O操作,打開設備句柄時,CreateFile的第6個參數應增加FILEFLAGOVERLAPPED 標志。
數據發送
數據發送利用WriteFile函數實現。對于同步I/O操作,它的最后一個參數可為NULL;而對異步I/O操作,它的最后一個參數必需是一個指向OVERLAPPED結構的指針,通過OVERLAPPED結構來獲得當前的操作狀態。
BOOL WriteComm(LPCVOID lpSndBuffer,DWORD
dwBytesToWrite)
{ //lpSndBuffer為發送數據緩沖區指針,
dwBytesToWrite為將要發送的字節長度
//設備已打開
BOOL bWriteState;
//實際發送的字節數
DWORD dwBytesWritten;
//設備未打開
if(!bOpen) return FALSE;
bWriteState=WriteFile(hComDev,lpSndBuffer,
dwBytesToWrite,&dwBytesWritten,NULL);
if(!bWriteState dwBytesToWrite!=dwBytesWritten)
//發送失敗
return FALSE;
else
//發送成功
return TRUE;
}
數據接收
接收數據的任務由ReadFile函數完成。該函數從串口接收緩沖區中讀取數據,讀取數據前,先用ClearCommError函數獲得接收緩沖區中的字節數。接收數據時,同步和異步讀取的差別同發送數據是一樣的。
DWORD ReadComm(LPVOID lpInBuffer,DWORD
dwBytesToRead)
{ //lpInBuffer為接收數據的緩沖區指針, dwBytesToRead為準備讀取的數據長度(字節數)
//串行設備狀態結構
COMSTAT ComStat;
DWORD dwBytesRead,dwErrorFlags;
//設備未打開
if(!bOpen) return 0;
//讀取串行設備的當前狀態
ClearCommError(hComDev,&dwErrorFlags,&ComStat);
//應該讀取的數據長度
dwBytesRead=min(dwBytesToRead,ComStat.cbInQue);
if(dwBytesRead>0)
//讀取數據
if(!ReadFile(hComDev,lpInBuffer,dwBytesRead,&dwBytesRead,NULL))
dwBytesRead=0;
return dwBytesRead;
}
事件監視線程
事件監視線程對串口事件進行監視,當監視的事件發生時,監視線程可將這個事件發送(SendMessage)或登記(PostMessage)到對事件進行處理的窗口類(由pParam指定)中。
UINT CommWatchProc(LPVOID pParam)
{ DWORD dwEventMask=0; //發生的事件;
while(bOpen)
{ //等待監視的事件發生
WaitCommEvent(hComDev, &dwEventMask,
NULL);
if ((dwEventMask & EVRXCHAR) ==
EVRXCHAR)
……//接收到字符事件后,可以將此消息登記到由pParam有指定的窗口類中進行處理
if(dwEventMask & EVERR)==EVERROR)
……//發生錯誤時的處理
}
SetEvent(hEvent);
//發信號,指示監視線程結束
return 0;
}
關閉串行設備
在整個應用程序結束或不再使用串行設備時,應將串行設備關閉,包括取消事件監視,將設備打開標志bOpen置為FALSE以使事件監視線程結束,清除發送/接收緩沖區和關閉設備句柄。
void CloseSynComm()
{
if(!bOpen) return;
//結束事件監視線程
bOpen=FALSE;
SetCommMask(hComDev,0);
//取消事件監視,此時監視線程中的WaitCommEvent將返回
WaitForSingleObject(hEvent,INFINITE);
//等待監視線程結束
CloseHandle(hEvent); //關閉事件句柄
//停止發送和接收數據,并清除發送和接收緩沖區
PurgeComm(hComDev,PURGETXABORT
PURGERXABORT PURGETXCLEAR
PURGERXCLEAR);
//關閉設備句柄
CloseHandle(hComDev);
}
小 結
以上給出了用Win32 API 設計串行通信的基本思路,對這個同步I/O操作的串行通信程序稍加改造就可進行異步I/O操作。在實際應用中,我們可以將這些串行通信函數和成員變量加到一個已有的CWnd類或其派生類中來實現串行通信,也可設計一個新的串行通信類來包含這些成員函數和成員變量。總之,利用Win32 API可以設計出滿足各種需要的串行通信程序。