前言:本站為你精心整理了小議Windows通信編程思考范文,希望能為你的創作提供參考價值,我們的客服老師可以幫助你提供個性化的參考范文,歡迎咨詢。
Windows與DOS編程的重要差別在于Windows程序是消息驅動和設備統一管理。體現在通信方面,DOS中的寄存器直接讀寫、BIOS調用和通信中斷程序等編程方法都不能或不宜采用。Windows通過通信驅動程序COMM.DRV與硬件接口,向程序員提供了多達17個標準函數,功能強大,但也增加了理解和編程的難度。
Windows3.1通信函數主要有:
OpenComm
打開一通信設備
BuildCimmDCB將一設備定義字符串轉變為DCB數據結構
EnableCommNotification使能或禁止傳送WM_COMMNOTIFY消
息
SetCommState設置通信設備狀態
SetCommEventMask設置通信事件掩碼
ReadComm從通信設備讀字符
WriteComm向通信設備寫字符
FlushComm清除一發送或接收隊列
GetCommEventMask返回通信事件掩碼
GetCommState返回設備控制塊(DCB)
GetCommError恢復通信設備狀態
CloseComm關閉一通信設備
DCB數據結構、其它通信函數及各函數的具體用法請參見有關資料。
一般Windows通信編程應包括兩部分:設備初始化及WM_COMMNOTIF
Y消息處理。
設備初始化典型流程如圖1。
圖1
WM_COMMNOTIFY消息處理典型流程如圖2。
圖2
對于大多數實際通信來說,可能只需要處理流程圖中的一部分。
設備初始化及WM_COMMNOTIFY消息處理兩部分密切相關。所有類型WM_COMMNOTIFY消息的傳送都是因為在初始化函數中進行了相應的設置。
換言之,可以根據通信的實際情況有選擇地設置,控制Windows向應用程序發送的WM_COMMNOTIFY消息的數量和類型,以期達到高效、可靠的通信。例如,對于固定長度消息型的通信可以在EnableCommNotification函數中設置cbWriteNotify和cbOutQueue參數為消息長度;對于以固定字符結尾的消息型通信可以在事件掩碼中包括EV_RXFLAG,將DCB數據結構中的EvtChar變量置為結尾字符,然后調用SetCommState和SetCommEventMask函數;對于遵循V.25bis之類協議的通信,由于用到了大量信號線來作握手信號,則事件掩碼中要包含EV_CTS、EV_DSR、EV_RSLD及EV_RING等;而對于文件傳送型的通信,則宜將OpenComm函數中的cbInQue和cbOutQue變量、EnablecCommNotification中的cbWriteNotify和cbOutQueue變量設置為較大值,以加快文件傳送速度。
二、Windows通信疑難探討
現將筆者在實際編程中遇到的疑難和解決辦法描述如下,希望對遇到類似問題的朋友有所啟發。
1.怎樣用Windows未提供的波特率通信?
Windows提供了由110bps至256000bps共十三種波特率,一般情況下已足夠使用。但在某種特定情況下,例如通信對方使用150bps、又無法要求對方改變波特率時,Windows通信就比較困難了。
首先想到的解決方法是直接調用BIOS中斷14H來設置波特率(DOS提供了150bps的波特率)。結果是Windows屏蔽了該中斷,嘗試失敗。
最后的是采用"蒙混過關"的辦法解決問題的:首先,以任一Windows支持的波特率(例如300bps)構造通信參數字符串,調用BuildCommDCB產生DCB數據結構;然后調用SetCommState設置通信參數;最后再調用自編函數直接修改串口通信寄存器的值。經實驗,設置成功,且對Windows程序運行無任何不良影響。
2.接收數據為何"丟失"?
通過設置EnableCommNotification函數中的cbWriteNotify參數(在發送WM_COMMNOTIFY消息之前,通信設備驅動程序必須向應用程序出入隊列中寫入的字節數),可以使系統每收到固定個字符發出一WM_COMMNOTIFY消息,這對于固定長度消息型的通信是很方便的。但實際應用時有時會發生接收數據"丟失"現象,即收到WM_COMMNOTIFY消息后從接收隊列讀出cbNotify個數據時,發現只有前面部分數據正確。
經檢查,"丟失"現象是由于接收數據超時引起的,當通信對方時鐘頻率較低時,規定時間內收不到cbWriteNotify指定的數據量,即所謂"超時",Windows照樣向應用程序發送帶CN_RECEIVE標志的WM_COMMNOTI
FY消息。然后,在應用程序輸入隊列數據讀出之前,Windows不再發送該類消息。
解決的方法是減小cbWriteNotify的設定值直到不再發生"超時"現象。
發送數據時同樣應正確設定cbOutQue值,以免產生"超時"現象。
如果將cbWriteNotify或cbOutQue設為-1,則Windows不傳送帶CN_RECEIVE或CN_TRANSMIT標志的WM_COMMNOTIFY消息。
3.怎樣合理使用FlushComm與GetCommError函數?
FlushComm函數的功能是清除指定設備接收或發送隊列。GetCommError函數的功能是返回指定設備最近錯誤碼和當前狀態,更重要的是"解鎖"功能:當出現通信錯誤時,Windows會鎖死通信端口直到調用GetCommError。
調用FlushComm的時機很重要,如果通信端口發生錯誤,不調用該函數就有可能會使接收隊列包含不期望的數據;若隨便調用該函數,也有可能造成尚未讀入或發出的數據丟失。總之,調用該函數要做到"心中有數"。
為了合理調用FlushComm和GetCommError函數,建議在事件掩碼中包含EV_ERR與EV_BREAK。
4.Windows多串口通信
Windows最多可支持四個串口的通信,但對于ISA總線的PC,由于其COM1與COM3、COM2與COM4分別共用IRQ3和IRQ4,所以只能同時使用兩個串口。MCA、EISA總線系統沒有此限制。
如果需要使用的端口不止四個,可以在PC護展槽中加插多用戶卡,如美國的Comtrol、臺灣的Moxa(摩莎)等,就可以支持幾個到幾十個串口,加上隨卡提供的Windows驅動程序,就可以進行多串口通信。具體用法請參閱擴展卡說明書。
三、Windows通信實例
實例的通信環境為:本方COMPAQ4/50微機,安裝中文Windows3.2;對方為8031單片機。通信參數設置:波特率150bps,數據位8,停止位1,無校驗。通信協議是:對方發FF,本方收到后回0F,對方收到0F后發一條十字節的消息,本方回0F,結束一次通信。
編程環境為中文Windows3
2、BorlandC++3.1OWL。
#include<windows.h>
#include<owl.h>
#include<window.h>
#include<string.h>
intCOM=1;//串口號
unsignedcharReceiveBuff〔11〕;//接收數據緩存
_CLASSDEF(TCommApp)
classTCommApp:publicTapplication
{
public:
TCommApp(LPSTRAName,HINSTANCEhInstance,HINSTANCE
HPrevInstance,LPSTR1p
CmdLine,intnCmdshow)
:TApplication(AName,hInstance,hPrevInstance,1pCmd
Line,nCmdShow){};
virtualvoidInitMainWindow();
};
_CLASSDEF(TCommWin)//主窗口類
classTCommWin:publicTWindow
{
public:
TCommWin(PTWindowsObjectAParent,LPSTRATitle):
TWindow(AParent,Atitle){}
intInitCom();
voidSetBaud();//設置Windows不支持的波特率
virtualBOOLWMCommNotify(TMessage&Mg)=〔WM_FIRST+
WM_COMMNOTIFY〕;
virtualvoidSetupWindow();
};
//該函數設置串口2的波特率為150bps,若用Windows提//供的波特率通信,則無須該函數
VoidTCommWin::SetBaud()
{
asmcli;
asmmovdx,2fbh;
asmmoval,80h;
asmoutdx,al;
asmmovdx,2f8h;
asmmoval,00h;
asmoutdx,al;
asmmovdx,2f9h;
asmmoval,3;
asmoutdx,al;
asmmovdx,2fbh;
asmmoval,03;
asmoutdx,al;
asmmovdx,2fch;
asmmoval,0bh;
asmoutdx,al;
asmmovdx,2f9h;
asmmoval,0fh;
asmoutdx,al;
asmmoval,20h;
asmout21h,al;
asmsti;
}
intTCommWin::InitCom()
{
charstr〔20〕,s〔2〕;
intCOMid,err;
DCBdcb;//設備控制塊
UINTMask=EV_BREAK|EV_ERR|EV_RXFLAG;//事件掩碼
strcpy(str,"COM");
strcat(str,itoa(COM+1,s,10));
COMid=OpenComm(str,128,1);
if(COMid<0)returnCOMid;
strcat(str,":300,n,8,1");
err=BuildCommDCB(str,&dcb);
dcb.EvtChar=-1;//事件字符0xff
err=SetCommState(&dcb);
SetBaud();
if(err>0)returnerr;
FlushComm(COMid,1);
if(!EnableComunNotification(COMid,HWindow,10,-1))
return-1;
SetCommEventMask(COMid,Mask);
returnCOMid;
}
voidTCommWin::SetupWindow()
{
TWindow::SetupWindow();
InitCom();
}
BOOLTCommWin::WMCommNotify(TMessage&Mg)
{
UINTflag=0;
intid;
COMSTATstat;
unsignedcharSendChar;
staticunsignedchar
*p=ReceiveBuff;
staticnum=0;
intret;
id=Mg.WParam;
switch(Mg.LP.Lo)
{
caseCN_EVENT://有事件掩碼中定義的事件發生
flag=GetCommEventMask(id,EV_BREAK);
if(flag&EV_BREAK)
FlushComm(id,1);
flag=GetCommEventMask(id,EV_RXFLAG);
if(flag&EV_ERR)
FlushComm(id,1);
flag=GetCommEventMask(id,EV_RXFLAG);
if(flag&EV_RXFLAG)//收到了事件字符0xff
{
SendChar=0x0f;
WriteComm(id,&SendChar,1);//向對方回0x0f
}
break;
caseCN_RECEIVE://接收到了規定個字符或超時
do
{
ret=ReadComm(id,p,1);
if(ret>0)
{
p++;
num++;
}
}while((ret>0)&(num<10));
if(num>=10)//接收完一條消息
{
num=0;
//此處處理接收到的消息
p=ReceiveBuff;
SendChar=0x0f;
WriteComm(id,&SendChar,1);//向對方回0x0f
FlushComm(id,1);
}break;
}
flag=GetCommError(id,&stat);//消除錯誤(若有)
return1;
}
voidTCommApp::InitMainWindow()
{
MainWindow=newTCommWin(NULL,"Windows通信示例");
}
intPASCALWinMain(HINSTANCEhInstance,HINSTANCEhPrevI
nstance,LPSTR1pCmdLine,
intnCmdShow)
{
TCommAppCommApp("通信",hInstance,hPrevInstance,1pC
mdLine,nCmdShow);
CommApp.Run();
returnCommApp.Status;}