http://blog.163.com/zhaojun_xf/blog/static/30050580200872465739835/
2008
劉樹(shù)坤 劉金
摘 要:介紹利用Windows API 在VC++ 5.0下實(shí)現(xiàn)微機(jī)串行通信程序的設(shè)計(jì)方法,并給出主要的通信程序代碼。
關(guān)鍵詞:Windows API;串行通信;VC++
1 引言
Windows 下的串行通信可有多種實(shí)現(xiàn)方法,如利用VB的MSCOMM.OCX控件,或利用Windows API等。在某些情況下,VB的MSCOMM.OCX控件不適合我們的要求,這時(shí)我們可以用Windows API 提供的通信函數(shù)來(lái)實(shí)現(xiàn),以編寫(xiě)出可移植性強(qiáng)的串行通信程序。自Windows95以來(lái),Windows 提供了大量的API,這些API幾乎可以幫助我們快速、方便地實(shí)現(xiàn)Windows應(yīng)用程序的各種功能。VC++是Windows應(yīng)用程序開(kāi)發(fā)的主流語(yǔ)言之一,它具有良好的圖形設(shè)計(jì)界面并支持面向?qū)ο蟮某绦蛟O(shè)計(jì)方法。本文結(jié)合一個(gè)實(shí)例介紹VC++5.0下利用Windows API 的串行通信程序設(shè)計(jì)。
2 通信程序設(shè)計(jì)
本文的實(shí)例是一臺(tái)微機(jī)和多臺(tái)單片機(jī)設(shè)備進(jìn)行串行通信,通信協(xié)議為RS—485,微機(jī)和單片機(jī)組成一個(gè)總線式網(wǎng)絡(luò)結(jié)構(gòu)。由于采用這種總線式結(jié)構(gòu),為防止總線競(jìng)爭(zhēng)(數(shù)據(jù)碰撞),只能由微機(jī)依次輪循總線上的各單片機(jī)設(shè)備來(lái)進(jìn)行通信。
對(duì)串行通信設(shè)備,Win32 API 可支持同步和異步兩種I/O操作。同步操作方式程序設(shè)計(jì)相對(duì)比較簡(jiǎn)單,但I(xiàn)/O操作函數(shù)在I/O操作結(jié)束前不能返回,這將掛起調(diào)用線程,直到I/O操作結(jié)束。異步操作方式相對(duì)要復(fù)雜一些,但它可讓耗時(shí)的I/O操作在后臺(tái)進(jìn)行,這樣不會(huì)掛起調(diào)用線程,這在大數(shù)據(jù)量通信的情況下對(duì)改善調(diào)用線程的響應(yīng)速度是相當(dāng)有效的。異步操作方式特別適合同時(shí)對(duì)多個(gè)串口設(shè)備進(jìn)行I/O操作和同時(shí)對(duì)一個(gè)串口設(shè)備進(jìn)行讀/寫(xiě)操作。兩種操作方式的程序設(shè)計(jì)基本思想是相似的,考慮到本文實(shí)例的這種總線網(wǎng)絡(luò)結(jié)構(gòu)且通信數(shù)據(jù)量不大,將針對(duì)同步操作方式給出具體的通信程序設(shè)計(jì),同時(shí)簡(jiǎn)單說(shuō)明如何實(shí)現(xiàn)異步I/O操作。
2.1 串口設(shè)備的初始化
通過(guò)CreateFile獲得串口設(shè)備句柄并對(duì)其進(jìn)行通信參數(shù)設(shè)置,包括設(shè)置輸出/接收緩沖區(qū)大小、超時(shí)控制和事件監(jiān)視等。
HANDLE hComDev=0;
//串行設(shè)備句柄;
BOOL bOpen=FALSE;
//串口打開(kāi)標(biāo)志;
HANDLE hEvent=0;
//線程同步事件句柄;
BOOL SetupSynCom()
{
DCB dcb;
COMMTIMEOUTS timeouts;
if(bOpen) return FALSE;
//設(shè)備已打開(kāi);
if((hComDev=CreateFile("COM1",GENERIC—READ|GENERIC—WRITE,0,NULL,OPEN—EXISTING,FILE—ATTRIBUTE—NORMAL,NULL))==INVALID—HANDLE—VALUE)
//打開(kāi)COM1;
return FALSE;
//此處我們不使用超時(shí)控制,
//將timeouts結(jié)構(gòu)中各項(xiàng)的值賦成0;
SetCommTimeouts(hComDev,&timeouts);
//設(shè)置超時(shí)控制;
SetupComm(hComDev,1024,512);
//設(shè)置接收緩沖區(qū)和輸出緩沖區(qū)的大小;
GetCommState(hComDev,&dcb);
//獲取缺省的dcb結(jié)構(gòu)的值
dcb.BaudRate=CBR—9600;
//波特率為9600 bps;
dcb.fParity=NOPARITY;
//無(wú)奇偶校驗(yàn);
dcb.ByteSize=8;
//數(shù)據(jù)位為8;
dcb.StopBits=ONESTOPBIT;
//一個(gè)停止位;
SetCommMask(hComDev,EV—ERR|EV—RXCHAR);
//監(jiān)視串口的錯(cuò)誤和接收到字符兩種事件;
SetCommState(hComDev,&dcb);
//設(shè)置串口設(shè)備控制參數(shù);
bOpen=TRUE;
//設(shè)備已打開(kāi);
hEvent=CreateEvent(NULL,FALSE,FALSE,"WatchEvent");
//創(chuàng)建人工重設(shè)、未發(fā)信號(hào)的事件;
AfxBeginThread(CommWatchProc,pParam);
//創(chuàng)建一個(gè)事件監(jiān)視線程來(lái)監(jiān)視串口事件;
}
在設(shè)置串口的DCB結(jié)構(gòu)的參數(shù)時(shí),我們沒(méi)有必要設(shè)置每一個(gè)的值,首先讀出DCB缺省的參數(shù)設(shè)置,然后只修改我們需要的參數(shù),其它都取缺省值。由于我們對(duì)串口進(jìn)行的同步I/O操作,除非指定進(jìn)行監(jiān)測(cè)的事件發(fā)生,否則WaitCommEvent函數(shù)不會(huì)返回,所以在串口設(shè)備初始化的最后,建立一個(gè)單獨(dú)的監(jiān)視線程來(lái)監(jiān)視串口的事件,以免掛起當(dāng)前調(diào)用線程,其中pParam可以是一個(gè)對(duì)事件進(jìn)行處理的窗口類(lèi)指針。
如果要進(jìn)行異步I/O操作,打開(kāi)設(shè)備句柄時(shí),CreateFile的第六個(gè)參數(shù)應(yīng)增加FILE—FLAG—OVERLAPPED 標(biāo)志。
2.2 數(shù)據(jù)發(fā)送
我們用WriteFile來(lái)進(jìn)行數(shù)據(jù)發(fā)送,對(duì)于同步I/O操作,它的最后一個(gè)參數(shù)可為NULL,而對(duì)異步I/O操作,它的最后一個(gè)參數(shù)必須是一個(gè)指向OVERLAPPED結(jié)構(gòu)的指針,通過(guò)這個(gè)OVERLAPPED結(jié)構(gòu)來(lái)獲得當(dāng)前操作狀態(tài)。
BOOL WriteComm(LPCVOID lpSndBuffer,DWORD dwBytesToWrite)
{ //lpSndBuffer為發(fā)送數(shù)據(jù)緩沖區(qū)指針,dwBytesToWrite為
將發(fā)送的字節(jié)長(zhǎng)度;
BOOL bWriteState;
//發(fā)送結(jié)果;
DWORD dwBytesWritten;
//實(shí)際發(fā)送的字節(jié)數(shù);
if(!bOpen) return FALSE;
//設(shè)備未打開(kāi);
bWriteState=WriteFile(hComDev,lpSndBuffer,dwBytesToWrite,&dwBytesWritten,NULL);
if(!bWriteState || dwBytesToWrite!=dwBytesWritten)
return FALSE;
//發(fā)送失?。?/font>
else
return TRUE;
//發(fā)送成功;
}
2.3 數(shù)據(jù)接收
用ReadFile來(lái)從串口接收緩沖區(qū)讀取數(shù)據(jù),數(shù)據(jù)讀取前,用ClearCommError函數(shù)獲得接收緩沖區(qū)中的字節(jié)數(shù)。至于同步和異步讀取的差別,同發(fā)送數(shù)據(jù)是一樣的。
DWORD ReadComm(LPVOID lpInBuffer,DWORD dwBytesToRead)
{ //lpInBuffer為接收數(shù)據(jù)的緩沖區(qū)指針, dwBytesToRead為準(zhǔn)備讀取的數(shù)據(jù)長(zhǎng)度(字節(jié)數(shù));
COMSTAT ComStat;
//串口設(shè)備狀態(tài)結(jié)構(gòu);
DWORD dwBytesRead,dwErrorFlags;
if(!bOpen) return 0;
//設(shè)備未打開(kāi);
ClearCommError(hComDev,&dwErrorFlags,&ComStat);
//讀取串口設(shè)備的當(dāng)前狀態(tài);
dwBytesRead=min(dwBytesToRead,ComStat.cbInQue);
//應(yīng)該讀取的數(shù)據(jù)長(zhǎng)度;
if(dwBytesRead>0)
if(!ReadFile(hComDev,lpInBuffer,dwBytesRead,&dwBytesRead,NULL))
//讀取數(shù)據(jù);
dwBytesRead=0;
return dwBytesRead;
}
2.4 事件監(jiān)視線程
事件監(jiān)視線程對(duì)串口事件進(jìn)行監(jiān)視,當(dāng)監(jiān)視的事件發(fā)生時(shí),監(jiān)視線程可將這個(gè)事件發(fā)送(SendMessage)或張貼(PostMessage)到對(duì)事件進(jìn)行處理的窗口類(lèi)(由pParam指定)中去。
UINT CommWatchProc(LPVOID pParam)
{ DWORD dwEventMask=0;
//發(fā)生的事件;
while(bOpen)
{ WaitCommEvent(hComDev, &dwEventMask, NULL);
//等待監(jiān)視的事件發(fā)生;
if ((dwEventMask & EV—RXCHAR) == EV—RXCHAR)
//接收到字符事件,可以將此消息張貼到由pParam有指定的窗口類(lèi)中進(jìn)行處理;
if(dwEventMask & EV—ERR)==EV—ERROR)
//發(fā)生錯(cuò)誤事件,處理;
}
SetEvent(hEvent);
//發(fā)信號(hào),指示監(jiān)視線程結(jié)束;
return 0;
}
2.5 關(guān)閉串口設(shè)備
在整個(gè)應(yīng)用程序結(jié)束時(shí)或不再使用串口設(shè)備時(shí),應(yīng)將串口設(shè)備關(guān)閉,包括取消事件監(jiān)視,將設(shè)備打開(kāi)標(biāo)志bOpen置為FALSE以使事件監(jiān)視線程結(jié)束,清除發(fā)送/接收緩沖區(qū)和關(guān)閉設(shè)備句柄。
void CloseSynComm()
{ if(!bOpen) return;
bOpen=FALSE;
//結(jié)束事件監(jiān)視線程;
SetCommMask(hComDev,0);
//取消事件監(jiān)視,此時(shí)監(jiān)視線程中的WaitCommEvent將返回;
WaitForSingleObject(hEvent,INFINITE);
//等待監(jiān)視線程結(jié)束;
CloseHandle(hEvent);
//關(guān)閉事件句柄;
//停止發(fā)送和接收數(shù)據(jù),并清除發(fā)送和接收緩沖區(qū);
PurgeComm(hComDev,PURGE—TXABORT|PURGE—RXABORT|PURGE—TXCLEAR|PURGE—RXCLEAR);
CloseHandle(hComDev);
//關(guān)閉設(shè)備句柄;
}
3 結(jié)束語(yǔ)
以上給出了用Windows API 設(shè)計(jì)串行通信的基本思想,對(duì)這個(gè)同步I/O操作的串行通信程序稍加改造就可進(jìn)行異步I/O操作。在實(shí)際應(yīng)用中,我們可以將這些串行通信函數(shù)和成員變量加到一個(gè)現(xiàn)成的CWnd類(lèi)或其派生類(lèi)中來(lái)實(shí)現(xiàn)串行通信,也可設(shè)計(jì)一個(gè)新的串行通信類(lèi)來(lái)包含這些成員函數(shù)和成員變量??傊?,利用WIN 3.2 API可以設(shè)計(jì)出靈活、能滿足各種要求的串行通信程序。
作者簡(jiǎn)介:劉樹(shù)坤 從事計(jì)算機(jī)網(wǎng)絡(luò)通信和工業(yè)控制、檢測(cè)的研究開(kāi)發(fā)工作。
作者單位:劉樹(shù)坤(西南計(jì)算中心 四川.綿陽(yáng)621900)
劉金(西南計(jì)算中心 四川.綿陽(yáng)621900)
參考文獻(xiàn)
[1]?。溃㏒tefano Maruzzi.The Microsoft Windows 95開(kāi)發(fā)人員指南[M].周靖,等譯.北京:機(jī)械工業(yè)出版社,1997.
[2] 史惠康.VISUAL C++5.0實(shí)用編程技術(shù)[M]. 中國(guó)水利水電出版社,1998.
聯(lián)系客服