www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 芯聞號 > 充電吧
[導(dǎo)讀]在工業(yè)控制中,工控機(一般都基于Windows平臺)經(jīng)常需要與智能儀表通過串口進(jìn)行通信。串口通信方便易行,應(yīng)用廣泛。一般情況下,工控機和各智能儀表通過RS485總線進(jìn)行通信。RS485的通信方式是半雙



在工業(yè)控制中,工控機(一般都基于Windows平臺)經(jīng)常需要與智能儀表通過串口進(jìn)行通信。串口通信方便易行,應(yīng)用廣泛。
一般情況下,工控機和各智能儀表通過RS485總線進(jìn)行通信。RS485的通信方式是半雙工的,只能由作為主節(jié)點的工控PC機依次輪詢網(wǎng)絡(luò)上的各智能控制單元子節(jié)點。每次通信都是由PC機通過串口向智能控制單元發(fā)布命令,智能控制單元在接收到正確的命令后作出應(yīng)答。
  在Win32下,可以使用兩種編程方式實現(xiàn)串口通信,其一是使用ActiveX控件,這種方法程序簡單,但欠靈活。其二是調(diào)用Windows的API函數(shù),這種方法可以清楚地掌握串口通信的機制,并且自由靈活。本文我們只介紹API串口通信部分。
  串口的操作可以有兩種操作方式:同步操作方式和重疊操作方式(又稱為異步操作方式)。同步操作時,API函數(shù)會阻塞直到操作完成以后才能返回(在多線程方式中,雖然不會阻塞主線程,但是仍然會阻塞監(jiān)聽線程);而重疊操作方式,API函數(shù)會立即返回,操作在后臺進(jìn)行,避免線程的阻塞。

無論那種操作方式,一般都通過四個步驟來完成:
(1)?打開串口
(2)?配置串口
(3)?讀寫串口
(4)?關(guān)閉串口


(1) 打開串口

  Win32系統(tǒng)把文件的概念進(jìn)行了擴展。無論是文件、通信設(shè)備、命名管道、郵件槽、磁盤、還是控制臺,都是用API函數(shù)CreateFile來打開或創(chuàng)建的。該函數(shù)的原型為:

HANDLE?CreateFile(?LPCTSTR?lpFileName,
??????????????????DWORD?dwDesiredAccess,
??????????????????DWORD?dwShareMode,
??????????????????LPSECURITY_ATTRIBUTES?lpSecurityAttributes,
??????????????????DWORD?dwCreationDistribution,
		???DWORD?dwFlagsAndAttributes,
		???HANDLE?hTemplateFile);

lpFileName:將要打開的串口邏輯名,如“COM1”;dwDesiredAccess:指定串口訪問的類型,可以是讀取、寫入或二者并列;dwShareMode:指定共享屬性,由于串口不能共享,該參數(shù)必須置為0;lpSecurityAttributes:引用安全性屬性結(jié)構(gòu),缺省值為NULL;dwCreationDistribution:創(chuàng)建標(biāo)志,對串口操作該參數(shù)必須置為OPEN_EXISTING;dwFlagsAndAttributes:屬性描述,用于指定該串口是否進(jìn)行異步操作,該值為FILE_FLAG_OVERLAPPED,表示使用異步的I/O;該值為0,表示同步I/O操作;hTemplateFile:對串口而言該參數(shù)必須置為NULL;

同步I/O方式打開串口的示例代碼:

	HANDLE?hCom;??//全局變量,串口句柄
	hCom=CreateFile("COM1",//COM1口
		GENERIC_READ|GENERIC_WRITE,?//允許讀和寫
		0,?//獨占方式
		NULL,
		OPEN_EXISTING,?//打開而不是創(chuàng)建
		0,?//同步方式
		NULL);
	if(hCom==(HANDLE)-1)
	{
		AfxMessageBox("打開COM失敗!");
		return?FALSE;
	}
	return?TRUE;

重疊I/O打開串口的示例代碼:

	HANDLE?hCom;??//全局變量,串口句柄
	hCom?=CreateFile("COM1",??//COM1口
?????????????GENERIC_READ|GENERIC_WRITE,?//允許讀和寫
?????????????0,??//獨占方式
?????????????NULL,
?????????????OPEN_EXISTING,??//打開而不是創(chuàng)建
?????????????FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,?//重疊方式
?????????????NULL);
	if(hCom?==INVALID_HANDLE_VALUE)
	{
		AfxMessageBox("打開COM失敗!");
		return?FALSE;
	}
	???return?TRUE;

(2),配置串口

  在打開通訊設(shè)備句柄后,常常需要對串口進(jìn)行一些初始化配置工作。這需要通過一個DCB結(jié)構(gòu)來進(jìn)行。DCB結(jié)構(gòu)包含了諸如波特率、數(shù)據(jù)位數(shù)、奇偶校驗和停止位數(shù)等信息。在查詢或配置串口的屬性時,都要用DCB結(jié)構(gòu)來作為緩沖區(qū)。
  一般用CreateFile打開串口后,可以調(diào)用GetCommState函數(shù)來獲取串口的初始配置。要修改串口的配置,應(yīng)該先修改DCB結(jié)構(gòu),然后再調(diào)用SetCommState函數(shù)設(shè)置串口。
  DCB結(jié)構(gòu)包含了串口的各項參數(shù)設(shè)置,下面僅介紹幾個該結(jié)構(gòu)常用的變量:

typedef?struct?_DCB{
???………
???//波特率,指定通信設(shè)備的傳輸速率。這個成員可以是實際波特率值或者下面的常量值之一:
???DWORD?BaudRate;?
CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_19200,?CBR_38400,?
CBR_56000,?CBR_57600,?CBR_115200,?CBR_128000,?CBR_256000,?CBR_14400

DWORD?fParity;?//?指定奇偶校驗使能。若此成員為1,允許奇偶校驗檢查?
???…
BYTE?ByteSize;?//?通信字節(jié)位數(shù),4—8
BYTE?Parity;?//指定奇偶校驗方法。此成員可以有下列值:
EVENPARITY?偶校驗?????NOPARITY?無校驗
MARKPARITY?標(biāo)記校驗???ODDPARITY?奇校驗
BYTE?StopBits;?//指定停止位的位數(shù)。此成員可以有下列值:
ONESTOPBIT?1位停止位???TWOSTOPBITS?2位停止位
ONE5STOPBITS???1.5位停止位
???………
??}?DCB;
winbase.h文件中定義了以上用到的常量。如下:
#define?NOPARITY????????????0
#define?ODDPARITY???????????1
#define?EVENPARITY??????????2
#define?ONESTOPBIT??????????0
#define?ONE5STOPBITS????????1
#define?TWOSTOPBITS?????????2
#define?CBR_110?????????????110
#define?CBR_300?????????????300
#define?CBR_600?????????????600
#define?CBR_1200????????????1200
#define?CBR_2400????????????2400
#define?CBR_4800????????????4800
#define?CBR_9600????????????9600
#define?CBR_14400???????????14400
#define?CBR_19200???????????19200
#define?CBR_38400???????????38400
#define?CBR_56000???????????56000
#define?CBR_57600???????????57600
#define?CBR_115200??????????115200
#define?CBR_128000??????????128000
#define?CBR_256000??????????256000

GetCommState函數(shù)可以獲得COM口的設(shè)備控制塊,從而獲得相關(guān)參數(shù):

BOOL?GetCommState(
???HANDLE?hFile,?//標(biāo)識通訊端口的句柄
???LPDCB?lpDCB?//指向一個設(shè)備控制塊(DCB結(jié)構(gòu))的指針
??);
SetCommState函數(shù)設(shè)置COM口的設(shè)備控制塊:
BOOL?SetCommState(
???HANDLE?hFile,?
???LPDCB?lpDCB?
??);

   除了在BCD中的設(shè)置外,程序一般還需要設(shè)置I/O緩沖區(qū)的大小和超時。Windows用I/O緩沖區(qū)來暫存串口輸入和輸出的數(shù)據(jù)。如果通信的速率較高,則應(yīng)該設(shè)置較大的緩沖區(qū)。調(diào)用SetupComm函數(shù)可以設(shè)置串行口的輸入和輸出緩沖區(qū)的大小。

BOOL?SetupComm(

????HANDLE?hFile,	//?通信設(shè)備的句柄?
????DWORD?dwInQueue,	//?輸入緩沖區(qū)的大小(字節(jié)數(shù))?
????DWORD?dwOutQueue	//?輸出緩沖區(qū)的大?。ㄗ止?jié)數(shù))
???);

  在用ReadFile和WriteFile讀寫串行口時,需要考慮超時問題。超時的作用是在指定的時間內(nèi)沒有讀入或發(fā)送指定數(shù)量的字符,ReadFile或WriteFile的操作仍然會結(jié)束。
  要查詢當(dāng)前的超時設(shè)置應(yīng)調(diào)用GetCommTimeouts函數(shù),該函數(shù)會填充一個COMMTIMEOUTS結(jié)構(gòu)。調(diào)用SetCommTimeouts可以用某一個COMMTIMEOUTS結(jié)構(gòu)的內(nèi)容來設(shè)置超時。
  讀寫串口的超時有兩種:間隔超時和總超時。間隔超時是指在接收時兩個字符之間的最大時延??偝瑫r是指讀寫操作總共花費的最大時間。寫操作只支持總超時,而讀操作兩種超時均支持。用COMMTIMEOUTS結(jié)構(gòu)可以規(guī)定讀寫操作的超時。
COMMTIMEOUTS結(jié)構(gòu)的定義為:

typedef?struct?_COMMTIMEOUTS?{???
????DWORD?ReadIntervalTimeout;?//讀間隔超時
????DWORD?ReadTotalTimeoutMultiplier;?//讀時間系數(shù)
????DWORD?ReadTotalTimeoutConstant;?//讀時間常量
????DWORD?WriteTotalTimeoutMultiplier;?//?寫時間系數(shù)
????DWORD?WriteTotalTimeoutConstant;?//寫時間常量
}?COMMTIMEOUTS,*LPCOMMTIMEOUTS;

COMMTIMEOUTS結(jié)構(gòu)的成員都以毫秒為單位??偝瑫r的計算公式是:
總超時=時間系數(shù)×要求讀/寫的字符數(shù)+時間常量?
例如,要讀入10個字符,那么讀操作的總超時的計算公式為:
讀總超時=ReadTotalTimeoutMultiplier×10+ReadTotalTimeoutConstant?
可以看出:間隔超時和總超時的設(shè)置是不相關(guān)的,這可以方便通信程序靈活地設(shè)置各種超時。?

如果所有寫超時參數(shù)均為0,那么就不使用寫超時。如果ReadIntervalTimeout為0,那么就不使用讀間隔超時。如果ReadTotalTimeoutMultiplier 和 ReadTotalTimeoutConstant 都為0,則不使用讀總超時。如果讀間隔超時被設(shè)置成MAXDWORD并且讀時間系數(shù)和讀時間常量都為0,那么在讀一次輸入緩沖區(qū)的內(nèi)容后讀操作就立即返回,而不管是否讀入了要求的字符。
  在用重疊方式讀寫串口時,雖然ReadFile和WriteFile在完成操作以前就可能返回,但超時仍然是起作用的。在這種情況下,超時規(guī)定的是操作的完成時間,而不是ReadFile和WriteFile的返回時間。
配置串口的示例代碼:

	SetupComm(hCom,1024,1024);?//輸入緩沖區(qū)和輸出緩沖區(qū)的大小都是1024

	COMMTIMEOUTS?TimeOuts;
	//設(shè)定讀超時
	TimeOuts.ReadIntervalTimeout=1000;
	TimeOuts.ReadTotalTimeoutMultiplier=500;
	TimeOuts.ReadTotalTimeoutConstant=5000;
	//設(shè)定寫超時
	TimeOuts.WriteTotalTimeoutMultiplier=500;
	TimeOuts.WriteTotalTimeoutConstant=2000;
	SetCommTimeouts(hCom,&TimeOuts);?//設(shè)置超時

	DCB?dcb;
	GetCommState(hCom,&dcb);
	dcb.BaudRate=9600;?//波特率為9600
	dcb.ByteSize=8;?//每個字節(jié)有8位
	dcb.Parity=NOPARITY;?//無奇偶校驗位
	dcb.StopBits=TWOSTOPBITS;?//兩個停止位
	SetCommState(hCom,&dcb);

	PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);

在讀寫串口之前,還要用PurgeComm()函數(shù)清空緩沖區(qū),該函數(shù)原型:

BOOL?PurgeComm(

????HANDLE?hFile,	//串口句柄
????DWORD?dwFlags	//?需要完成的操作
???);	

參數(shù)dwFlags指定要完成的操作,可以是下列值的組合:

PURGE_TXABORT	??中斷所有寫操作并立即返回,即使寫操作還沒有完成。
PURGE_RXABORT	??中斷所有讀操作并立即返回,即使讀操作還沒有完成。
PURGE_TXCLEAR	??清除輸出緩沖區(qū)
PURGE_RXCLEAR	??清除輸入緩沖區(qū)

(3),讀寫串口

我們使用ReadFile和WriteFile讀寫串口,下面是兩個函數(shù)的聲明:

BOOL?ReadFile(

????HANDLE?hFile,	//串口的句柄
????
????//?讀入的數(shù)據(jù)存儲的地址,
????//?即讀入的數(shù)據(jù)將存儲在以該指針的值為首地址的一片內(nèi)存區(qū)
????LPVOID?lpBuffer,	
????DWORD?nNumberOfBytesToRead,	//?要讀入的數(shù)據(jù)的字節(jié)數(shù)
????
????//?指向一個DWORD數(shù)值,該數(shù)值返回讀操作實際讀入的字節(jié)數(shù)
????LPDWORD?lpNumberOfBytesRead,	
????
????//?重疊操作時,該參數(shù)指向一個OVERLAPPED結(jié)構(gòu),同步操作時,該參數(shù)為NULL。
????LPOVERLAPPED?lpOverlapped?	
???);	
BOOL?WriteFile(

????HANDLE?hFile,	//串口的句柄
????
????//?寫入的數(shù)據(jù)存儲的地址,
????//?即以該指針的值為首地址的nNumberOfBytesToWrite
????//?個字節(jié)的數(shù)據(jù)將要寫入串口的發(fā)送數(shù)據(jù)緩沖區(qū)。
????LPCVOID?lpBuffer,	
????
????DWORD?nNumberOfBytesToWrite,	//要寫入的數(shù)據(jù)的字節(jié)數(shù)
????
????//?指向指向一個DWORD數(shù)值,該數(shù)值返回實際寫入的字節(jié)數(shù)
????LPDWORD?lpNumberOfBytesWritten,	
????
????//?重疊操作時,該參數(shù)指向一個OVERLAPPED結(jié)構(gòu),
????//?同步操作時,該參數(shù)為NULL。
????LPOVERLAPPED?lpOverlapped?	
???);

  在用ReadFile和WriteFile讀寫串口時,既可以同步執(zhí)行,也可以重疊執(zhí)行。在同步執(zhí)行時,函數(shù)直到操作完成后才返回。這意味著同步執(zhí)行時線程會被阻塞,從而導(dǎo)致效率下降。在重疊執(zhí)行時,即使操作還未完成,這兩個函數(shù)也會立即返回,費時的I/O操作在后臺進(jìn)行。
  ReadFile和WriteFile函數(shù)是同步還是異步由CreateFile函數(shù)決定,如果在調(diào)用CreateFile創(chuàng)建句柄時指定了FILE_FLAG_OVERLAPPED標(biāo)志,那么調(diào)用ReadFile和WriteFile對該句柄進(jìn)行的操作就應(yīng)該是重疊的;如果未指定重疊標(biāo)志,則讀寫操作應(yīng)該是同步的。ReadFile和WriteFile函數(shù)的同步或者異步應(yīng)該和CreateFile函數(shù)相一致。
  ReadFile函數(shù)只要在串口輸入緩沖區(qū)中讀入指定數(shù)量的字符,就算完成操作。而WriteFile函數(shù)不但要把指定數(shù)量的字符拷入到輸出緩沖區(qū),而且要等這些字符從串行口送出去后才算完成操作。
  如果操作成功,這兩個函數(shù)都返回TRUE。需要注意的是,當(dāng)ReadFile和WriteFile返回FALSE時,不一定就是操作失敗,線程應(yīng)該調(diào)用GetLastError函數(shù)分析返回的結(jié)果。例如,在重疊操作時如果操作還未完成函數(shù)就返回,那么函數(shù)就返回FALSE,而且GetLastError函數(shù)返回ERROR_IO_PENDING。這說明重疊操作還未完成。

同步方式讀寫串口比較簡單,下面先例舉同步方式讀寫串口的代碼:

//同步讀串口
char?str[100];
DWORD?wCount;//讀取的字節(jié)數(shù)
BOOL?bReadStat;
bReadStat=ReadFile(hCom,str,100,&wCount,NULL);
if(!bReadStat)
{
	AfxMessageBox("讀串口失敗!");
	return?FALSE;
}
return?TRUE;

//同步寫串口

	char?lpOutBuffer[100];
	DWORD?dwBytesWrite=100;
	COMSTAT?ComStat;
	DWORD?dwErrorFlags;
	BOOL?bWriteStat;
	ClearCommError(hCom,&dwErrorFlags,&ComStat);
	bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,&?dwBytesWrite,NULL);
	if(!bWriteStat)
	{
		AfxMessageBox("寫串口失敗!");
	}
	PurgeComm(hCom,?PURGE_TXABORT|
		PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

在重疊操作時,操作還未完成函數(shù)就返回。?

  重疊I/O非常靈活,它也可以實現(xiàn)阻塞(例如我們可以設(shè)置一定要讀取到一個數(shù)據(jù)才能進(jìn)行到下一步操作)。有兩種方法可以等待操作完成:一種方法是用象WaitForSingleObject這樣的等待函數(shù)來等待OVERLAPPED結(jié)構(gòu)的hEvent成員;另一種方法是調(diào)用GetOverlappedResult函數(shù)等待,后面將演示說明。
下面我們先簡單說一下OVERLAPPED結(jié)構(gòu)和GetOverlappedResult函數(shù):
OVERLAPPED結(jié)構(gòu)
OVERLAPPED結(jié)構(gòu)包含了重疊I/O的一些信息,定義如下:

typedef?struct?_OVERLAPPED?{?//?o??
????DWORD??Internal;?
????DWORD??InternalHigh;?
????DWORD??Offset;?
????DWORD??OffsetHigh;?
????HANDLE?hEvent;?
}?OVERLAPPED;

   在使用ReadFile和WriteFile重疊操作時,線程需要創(chuàng)建OVERLAPPED結(jié)構(gòu)以供這兩個函數(shù)使用。線程通過OVERLAPPED結(jié)構(gòu)獲得當(dāng)前的操作狀態(tài),該結(jié)構(gòu)最重要的成員是hEvent。hEvent是讀寫事件。當(dāng)串口使用異步通訊時,函數(shù)返回時操作可能還沒有完成,程序可以通過檢查該事件得知是否讀寫完畢。
  當(dāng)調(diào)用ReadFile, WriteFile 函數(shù)的時候,該成員會自動被置為無信號狀態(tài);當(dāng)重疊操作完成后,該成員變量會自動被置為有信號狀態(tài)。

GetOverlappedResult函數(shù)
BOOL?GetOverlappedResult(
????HANDLE?hFile,	//?串口的句柄??
????
????//?指向重疊操作開始時指定的OVERLAPPED結(jié)構(gòu)
????LPOVERLAPPED?lpOverlapped,	
????
????//?指向一個32位變量,該變量的值返回實際讀寫操作傳輸?shù)淖止?jié)數(shù)。
????LPDWORD?lpNumberOfBytesTransferred,	
????
????//?該參數(shù)用于指定函數(shù)是否一直等到重疊操作結(jié)束。
????//?如果該參數(shù)為TRUE,函數(shù)直到操作結(jié)束才返回。
????//?如果該參數(shù)為FALSE,函數(shù)直接返回,這時如果操作沒有完成,
????//?通過調(diào)用GetLastError()函數(shù)會返回ERROR_IO_INCOMPLETE。
????BOOL?bWait?	
???);	

該函數(shù)返回重疊操作的結(jié)果,用來判斷異步操作是否完成,它是通過判斷OVERLAPPED結(jié)構(gòu)中的hEvent是否被置位來實現(xiàn)的。

異步讀串口的示例代碼:

char?lpInBuffer[1024];
DWORD?dwBytesRead=1024;
COMSTAT?ComStat;
DWORD?dwErrorFlags;
OVERLAPPED?m_osRead;
memset(&m_osRead,0,sizeof(OVERLAPPED));
m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

ClearCommError(hCom,&dwErrorFlags,&ComStat);
dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);
if(!dwBytesRead)
return?FALSE;
BOOL?bReadStatus;
bReadStatus=ReadFile(hCom,lpInBuffer,
					?dwBytesRead,&dwBytesRead,&m_osRead);

if(!bReadStatus)?//如果ReadFile函數(shù)返回FALSE
{
	if(GetLastError()==ERROR_IO_PENDING)
	//GetLastError()函數(shù)返回ERROR_IO_PENDING,表明串口正在進(jìn)行讀操作	
	{
		WaitForSingleObject(m_osRead.hEvent,2000);
		//使用WaitForSingleObject函數(shù)等待,直到讀操作完成或延時已達(dá)到2秒鐘
		//當(dāng)串口讀操作進(jìn)行完畢后,m_osRead的hEvent事件會變?yōu)橛行盘?		PurgeComm(hCom,?PURGE_TXABORT|
			PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
		return?dwBytesRead;
	}
	return?0;
}
PurgeComm(hCom,?PURGE_TXABORT|
		??PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
return?dwBytesRead;

  對以上代碼再作簡要說明: 在使用ReadFile 函數(shù)進(jìn)行讀操作前,應(yīng)先使用ClearCommError函數(shù)清除錯誤。ClearCommError函數(shù)的原型如下:

BOOL?ClearCommError(

????HANDLE?hFile,	//?串口句柄
????LPDWORD?lpErrors,	//?指向接收錯誤碼的變量
????LPCOMSTAT?lpStat	//?指向通訊狀態(tài)緩沖區(qū)
???);	

該函數(shù)獲得通信錯誤并報告串口的當(dāng)前狀態(tài),同時,該函數(shù)清除串口的錯誤標(biāo)志以便繼續(xù)輸入、輸出操作。
參數(shù)lpStat指向一個COMSTAT結(jié)構(gòu),該結(jié)構(gòu)返回串口狀態(tài)信息。 COMSTAT結(jié)構(gòu) COMSTAT結(jié)構(gòu)包含串口的信息,結(jié)構(gòu)定義如下:

typedef?struct?_COMSTAT?{?//?cst??
????DWORD?fCtsHold?:?1;???//?Tx?waiting?for?CTS?signal?
????DWORD?fDsrHold?:?1;???//?Tx?waiting?for?DSR?signal?
????DWORD?fRlsdHold?:?1;??//?Tx?waiting?for?RLSD?signal?
????DWORD?fXoffHold?:?1;??//?Tx?waiting,?XOFF?char?rec''d?
????DWORD?fXoffSent?:?1;??//?Tx?waiting,?XOFF?char?sent?
????DWORD?fEof?:?1;???????//?EOF?character?sent?
????DWORD?fTxim?:?1;??????//?character?waiting?for?Tx?
????DWORD?fReserved?:?25;?//?reserved?
????DWORD?cbInQue;????????//?bytes?in?input?buffer?
????DWORD?cbOutQue;???????//?bytes?in?output?buffer?
}?COMSTAT,?*LPCOMSTAT;

本文只用到了cbInQue成員變量,該成員變量的值代表輸入緩沖區(qū)的字節(jié)數(shù)。

  最后用PurgeComm函數(shù)清空串口的輸入輸出緩沖區(qū)。

  這段代碼用WaitForSingleObject函數(shù)來等待OVERLAPPED結(jié)構(gòu)的hEvent成員,下面我們再演示一段調(diào)用GetOverlappedResult函數(shù)等待的異步讀串口示例代碼:

char?lpInBuffer[1024];
DWORD?dwBytesRead=1024;
	BOOL?bReadStatus;
	DWORD?dwErrorFlags;
	COMSTAT?ComStat;
OVERLAPPED?m_osRead;

	ClearCommError(hCom,&dwErrorFlags,&ComStat);
	if(!ComStat.cbInQue)
		return?0;
	dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);
	bReadStatus=ReadFile(hCom,?lpInBuffer,dwBytesRead,
		&dwBytesRead,&m_osRead);
	if(!bReadStatus)?//如果ReadFile函數(shù)返回FALSE
	{
		if(GetLastError()==ERROR_IO_PENDING)
		{
			GetOverlappedResult(hCom,
				&m_osRead,&dwBytesRead,TRUE);
???????????//?GetOverlappedResult函數(shù)的最后一個參數(shù)設(shè)為TRUE,
???????????//函數(shù)會一直等待,直到讀操作完成或由于錯誤而返回。

			return?dwBytesRead;
		}
		return?0;
	}
	return?dwBytesRead;

異步寫串口的示例代碼:

char?buffer[1024];
DWORD?dwBytesWritten=1024;
	DWORD?dwErrorFlags;
	COMSTAT?ComStat;
OVERLAPPED?m_osWrite;
	BOOL?bWriteStat;

	bWriteStat=WriteFile(hCom,buffer,dwBytesWritten,
		&dwBytesWritten,&m_OsWrite);
	if(!bWriteStat)
	{
		if(GetLastError()==ERROR_IO_PENDING)
		{
			WaitForSingleObject(m_osWrite.hEvent,1000);
			return?dwBytesWritten;
		}
		return?0;
	}
	return?dwBytesWritten;

(4),關(guān)閉串口

  利用API函數(shù)關(guān)閉串口非常簡單,只需使用CreateFile函數(shù)返回的句柄作為參數(shù)調(diào)用CloseHandle即可:

BOOL?CloseHandle(
????HANDLE?hObject;?//handle?to?object?to?close?
);

串口編程的一個實例

  為了讓您更好地理解串口編程,下面我們分別編寫兩個例程(見附帶的源碼部分),這兩個例程都實現(xiàn)了工控機與百特顯示儀表通過RS485接口進(jìn)行的串口通信。其中第一個例程采用同步串口操作,第二個例程采用異步串口操作。
   我們只介紹軟件部分,RS485接口接線方法不作介紹,感興趣的讀者可以查閱相關(guān)資料。

例程1

  打開VC++6.0,新建基于對話框的工程RS485Comm,在主對話框窗口IDD_RS485COMM_DIALOG上添加兩個按鈕,ID分別為IDC_SEND和IDC_RECEIVE,標(biāo)題分別為“發(fā)送”和“接收”;添加一個靜態(tài)文本框IDC_DISP,用于顯示串口接收到的內(nèi)容。

在RS485CommDlg.cpp文件中添加全局變量:

HANDLE?hCom;??//全局變量,串口句柄

在RS485CommDlg.cpp文件中的OnInitDialog()函數(shù)添加如下代碼:

	//?TODO:?Add?extra?initialization?here
	hCom=CreateFile("COM1",//COM1口
		GENERIC_READ|GENERIC_WRITE,?//允許讀和寫
		0,?//獨占方式
		NULL,
		OPEN_EXISTING,?//打開而不是創(chuàng)建
		0,?//同步方式
		NULL);
	if(hCom==(HANDLE)-1)
	{
		AfxMessageBox("打開COM失敗!");
		return?FALSE;
	}

	SetupComm(hCom,100,100);?//輸入緩沖區(qū)和輸出緩沖區(qū)的大小都是1024

	COMMTIMEOUTS?TimeOuts;
	//設(shè)定讀超時
	TimeOuts.ReadIntervalTimeout=MAXDWORD;
	TimeOuts.ReadTotalTimeoutMultiplier=0;
	TimeOuts.ReadTotalTimeoutConstant=0;
	//在讀一次輸入緩沖區(qū)的內(nèi)容后讀操作就立即返回,
	//而不管是否讀入了要求的字符。


	//設(shè)定寫超時
	TimeOuts.WriteTotalTimeoutMultiplier=100;
	TimeOuts.WriteTotalTimeoutConstant=500;
	SetCommTimeouts(hCom,&TimeOuts);?//設(shè)置超時

	DCB?dcb;
	GetCommState(hCom,&dcb);
	dcb.BaudRate=9600;?//波特率為9600
	dcb.ByteSize=8;?//每個字節(jié)有8位
	dcb.Parity=NOPARITY;?//無奇偶校驗位
	dcb.StopBits=TWOSTOPBITS;?//兩個停止位
	SetCommState(hCom,&dcb);

	PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);

分別雙擊IDC_SEND按鈕和IDC_RECEIVE按鈕,添加兩個按鈕的響應(yīng)函數(shù):

void?CRS485CommDlg::OnSend()?
{
	//?TODO:?Add?your?control?notification?handler?code?here
	//?在此需要簡單介紹百特公司XMA5000的通訊協(xié)議:
	//該儀表RS485通訊采用主機廣播方式通訊。
	//串行半雙工,幀11位,1個起始位(0),8個數(shù)據(jù)位,2個停止位(1)
	//如:讀儀表顯示的瞬時值,主機發(fā)送:DC1?AAA?BB?ETX
	//其中:DC1是標(biāo)準(zhǔn)ASCII碼的一個控制符號,碼值為11H(十進(jìn)制的17)
	//在XMA5000的通訊協(xié)議中,DC1表示讀瞬時值
	//AAA是從機地址碼,也就是XMA5000顯示儀表的通訊地址
	//BB為通道號,讀瞬時值時該值為01
	//ETX也是標(biāo)準(zhǔn)ASCII碼的一個控制符號,碼值為03H
	//在XMA5000的通訊協(xié)議中,ETX表示主機結(jié)束符

	char?lpOutBuffer[7];
	memset(lpOutBuffer,''