青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

yehao's Blog

Win32串口編程

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

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

(1) 打開串口

  Win32系統(tǒng)把文件的概念進(jìn)行了擴(kuò)展。無論是文件、通信設(shè)備、命名管道、郵件槽、磁盤、還是控制臺(tái),都是用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)志,對(duì)串口操作該參數(shù)必須置為OPEN_EXISTING;
  • dwFlagsAndAttributes:屬性描述,用于指定該串口是否進(jìn)行異步操作,該值為FILE_FLAG_OVERLAPPED,表示使用異步的I/O;該值為0,表示同步I/O操作;
  • hTemplateFile:對(duì)串口而言該參數(shù)必須置為NULL;

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

	HANDLE hCom;  //全局變量,串口句柄
	hCom=CreateFile("COM1",//COM1口
		GENERIC_READ|GENERIC_WRITE, //允許讀和寫
		0, //獨(dú)占方式
		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,  //獨(dú)占方式
             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è)備句柄后,常常需要對(duì)串口進(jìn)行一些初始化配置工作。這需要通過一個(gè)DCB結(jié)構(gòu)來進(jìn)行。DCB結(jié)構(gòu)包含了諸如波特率、數(shù)據(jù)位數(shù)、奇偶校驗(yàn)和停止位數(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)包含了串口的各項(xiàng)參數(shù)設(shè)置,下面僅介紹幾個(gè)該結(jié)構(gòu)常用的變量:

typedef struct _DCB{
   ………
   //波特率,指定通信設(shè)備的傳輸速率。這個(gè)成員可以是實(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; // 指定奇偶校驗(yàn)使能。若此成員為1,允許奇偶校驗(yàn)檢查 
   …
BYTE ByteSize; // 通信字節(jié)位數(shù),4—8
BYTE Parity; //指定奇偶校驗(yàn)方法。此成員可以有下列值:
EVENPARITY 偶校驗(yàn)     NOPARITY 無校驗(yàn)
MARKPARITY 標(biāo)記校驗(yàn)   ODDPARITY 奇校驗(yàn)
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)識(shí)通訊端口的句柄
   LPDCB lpDCB //指向一個(gè)設(shè)備控制塊(DCB結(jié)構(gòu))的指針
  );
SetCommState函數(shù)設(shè)置COM口的設(shè)備控制塊:
BOOL SetCommState(
   HANDLE hFile, 
   LPDCB lpDCB 
  );
  除了在BCD中的設(shè)置外,程序一般還需要設(shè)置I/O緩沖區(qū)的大小和超時(shí)。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讀寫串行口時(shí),需要考慮超時(shí)問題。超時(shí)的作用是在指定的時(shí)間內(nèi)沒有讀入或發(fā)送指定數(shù)量的字符,ReadFile或WriteFile的操作仍然會(huì)結(jié)束。
  要查詢當(dāng)前的超時(shí)設(shè)置應(yīng)調(diào)用GetCommTimeouts函數(shù),該函數(shù)會(huì)填充一個(gè)COMMTIMEOUTS結(jié)構(gòu)。調(diào)用SetCommTimeouts可以用某一個(gè)COMMTIMEOUTS結(jié)構(gòu)的內(nèi)容來設(shè)置超時(shí)。
  讀寫串口的超時(shí)有兩種:間隔超時(shí)和總超時(shí)。間隔超時(shí)是指在接收時(shí)兩個(gè)字符之間的最大時(shí)延。總超時(shí)是指讀寫操作總共花費(fèi)的最大時(shí)間。寫操作只支持總超時(shí),而讀操作兩種超時(shí)均支持。用COMMTIMEOUTS結(jié)構(gòu)可以規(guī)定讀寫操作的超時(shí)。
COMMTIMEOUTS結(jié)構(gòu)的定義為:
typedef struct _COMMTIMEOUTS {   
    DWORD ReadIntervalTimeout; //讀間隔超時(shí)
    DWORD ReadTotalTimeoutMultiplier; //讀時(shí)間系數(shù)
    DWORD ReadTotalTimeoutConstant; //讀時(shí)間常量
    DWORD WriteTotalTimeoutMultiplier; // 寫時(shí)間系數(shù)
    DWORD WriteTotalTimeoutConstant; //寫時(shí)間常量
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
COMMTIMEOUTS結(jié)構(gòu)的成員都以毫秒為單位。總超時(shí)的計(jì)算公式是:
總超時(shí)=時(shí)間系數(shù)×要求讀/寫的字符數(shù)+時(shí)間常量
例如,要讀入10個(gè)字符,那么讀操作的總超時(shí)的計(jì)算公式為:
讀總超時(shí)=ReadTotalTimeoutMultiplier×10+ReadTotalTimeoutConstant
可以看出:間隔超時(shí)和總超時(shí)的設(shè)置是不相關(guān)的,這可以方便通信程序靈活地設(shè)置各種超時(shí)。

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

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

	DCB dcb;
	GetCommState(hCom,&dcb);
	dcb.BaudRate=9600; //波特率為9600
	dcb.ByteSize=8; //每個(gè)字節(jié)有8位
	dcb.Parity=NOPARITY; //無奇偶校驗(yàn)位
	dcb.StopBits=TWOSTOPBITS; //兩個(gè)停止位
	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讀寫串口,下面是兩個(gè)函數(shù)的聲明:

BOOL ReadFile(

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

    HANDLE hFile,	//串口的句柄
    
    // 寫入的數(shù)據(jù)存儲(chǔ)的地址,
    // 即以該指針的值為首地址的nNumberOfBytesToWrite
    // 個(gè)字節(jié)的數(shù)據(jù)將要寫入串口的發(fā)送數(shù)據(jù)緩沖區(qū)。
    LPCVOID lpBuffer,	
    
    DWORD nNumberOfBytesToWrite,	//要寫入的數(shù)據(jù)的字節(jié)數(shù)
    
    // 指向指向一個(gè)DWORD數(shù)值,該數(shù)值返回實(shí)際寫入的字節(jié)數(shù)
    LPDWORD lpNumberOfBytesWritten,	
    
    // 重疊操作時(shí),該參數(shù)指向一個(gè)OVERLAPPED結(jié)構(gòu),
    // 同步操作時(shí),該參數(shù)為NULL。
    LPOVERLAPPED lpOverlapped 	
   );
  在用ReadFile和WriteFile讀寫串口時(shí),既可以同步執(zhí)行,也可以重疊執(zhí)行。在同步執(zhí)行時(shí),函數(shù)直到操作完成后才返回。這意味著同步執(zhí)行時(shí)線程會(huì)被阻塞,從而導(dǎo)致效率下降。在重疊執(zhí)行時(shí),即使操作還未完成,這兩個(gè)函數(shù)也會(huì)立即返回,費(fèi)時(shí)的I/O操作在后臺(tái)進(jìn)行。
  ReadFile和WriteFile函數(shù)是同步還是異步由CreateFile函數(shù)決定,如果在調(diào)用CreateFile創(chuàng)建句柄時(shí)指定了FILE_FLAG_OVERLAPPED標(biāo)志,那么調(diào)用ReadFile和WriteFile對(duì)該句柄進(jìn)行的操作就應(yīng)該是重疊的;如果未指定重疊標(biāo)志,則讀寫操作應(yīng)該是同步的。ReadFile和WriteFile函數(shù)的同步或者異步應(yīng)該和CreateFile函數(shù)相一致。
  ReadFile函數(shù)只要在串口輸入緩沖區(qū)中讀入指定數(shù)量的字符,就算完成操作。而WriteFile函數(shù)不但要把指定數(shù)量的字符拷入到輸出緩沖區(qū),而且要等這些字符從串行口送出去后才算完成操作。
  如果操作成功,這兩個(gè)函數(shù)都返回TRUE。需要注意的是,當(dāng)ReadFile和WriteFile返回FALSE時(shí),不一定就是操作失敗,線程應(yīng)該調(diào)用GetLastError函數(shù)分析返回的結(jié)果。例如,在重疊操作時(shí)如果操作還未完成函數(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í),操作還未完成函數(shù)就返回。

  重疊I/O非常靈活,它也可以實(shí)現(xiàn)阻塞(例如我們可以設(shè)置一定要讀取到一個(gè)數(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重疊操作時(shí),線程需要?jiǎng)?chuàng)建OVERLAPPED結(jié)構(gòu)以供這兩個(gè)函數(shù)使用。線程通過OVERLAPPED結(jié)構(gòu)獲得當(dāng)前的操作狀態(tài),該結(jié)構(gòu)最重要的成員是hEvent。hEvent是讀寫事件。當(dāng)串口使用異步通訊時(shí),函數(shù)返回時(shí)操作可能還沒有完成,程序可以通過檢查該事件得知是否讀寫完畢。
  當(dāng)調(diào)用ReadFile, WriteFile 函數(shù)的時(shí)候,該成員會(huì)自動(dòng)被置為無信號(hào)狀態(tài);當(dāng)重疊操作完成后,該成員變量會(huì)自動(dòng)被置為有信號(hào)狀態(tài)。
GetOverlappedResult函數(shù)
BOOL GetOverlappedResult(
    HANDLE hFile,	// 串口的句柄  
    
    // 指向重疊操作開始時(shí)指定的OVERLAPPED結(jié)構(gòu)
    LPOVERLAPPED lpOverlapped,	
    
    // 指向一個(gè)32位變量,該變量的值返回實(shí)際讀寫操作傳輸?shù)淖止?jié)數(shù)。
    LPDWORD lpNumberOfBytesTransferred,	
    
    // 該參數(shù)用于指定函數(shù)是否一直等到重疊操作結(jié)束。
    // 如果該參數(shù)為TRUE,函數(shù)直到操作結(jié)束才返回。
    // 如果該參數(shù)為FALSE,函數(shù)直接返回,這時(shí)如果操作沒有完成,
    // 通過調(diào)用GetLastError()函數(shù)會(huì)返回ERROR_IO_INCOMPLETE。
    BOOL bWait 	
   );	
該函數(shù)返回重疊操作的結(jié)果,用來判斷異步操作是否完成,它是通過判斷OVERLAPPED結(jié)構(gòu)中的hEvent是否被置位來實(shí)現(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ù)等待,直到讀操作完成或延時(shí)已達(dá)到2秒鐘
		//當(dāng)串口讀操作進(jìn)行完畢后,m_osRead的hEvent事件會(huì)變?yōu)橛行盘?hào)
		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;
  對(duì)以上代碼再作簡要說明:在使用ReadFile 函數(shù)進(jìn)行讀操作前,應(yīng)先使用ClearCommError函數(shù)清除錯(cuò)誤。ClearCommError函數(shù)的原型如下:
BOOL ClearCommError(

    HANDLE hFile,	// 串口句柄
    LPDWORD lpErrors,	// 指向接收錯(cuò)誤碼的變量
    LPCOMSTAT lpStat	// 指向通訊狀態(tài)緩沖區(qū)
   );	
該函數(shù)獲得通信錯(cuò)誤并報(bào)告串口的當(dāng)前狀態(tài),同時(shí),該函數(shù)清除串口的錯(cuò)誤標(biāo)志以便繼續(xù)輸入、輸出操作。
參數(shù)lpStat指向一個(gè)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成員,下面我們?cè)傺菔疽欢握{(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ù)的最后一個(gè)參數(shù)設(shè)為TRUE,
           //函數(shù)會(huì)一直等待,直到讀操作完成或由于錯(cuò)誤而返回。

			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 
);
串口編程的一個(gè)實(shí)例

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

例程1

  打開VC++6.0,新建基于對(duì)話框的工程RS485Comm,在主對(duì)話框窗口IDD_RS485COMM_DIALOG上添加兩個(gè)按鈕,ID分別為IDC_SEND和IDC_RECEIVE,標(biāo)題分別為“發(fā)送”和“接收”;添加一個(gè)靜態(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, //獨(dú)占方式
		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è)定讀超時(shí)
	TimeOuts.ReadIntervalTimeout=MAXDWORD;
	TimeOuts.ReadTotalTimeoutMultiplier=0;
	TimeOuts.ReadTotalTimeoutConstant=0;
	//在讀一次輸入緩沖區(qū)的內(nèi)容后讀操作就立即返回,
	//而不管是否讀入了要求的字符。


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

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

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

	char lpOutBuffer[7];
	memset(lpOutBuffer,''\0'',7); //前7個(gè)字節(jié)先清零
	lpOutBuffer[0]=''\x11'';  //發(fā)送緩沖區(qū)的第1個(gè)字節(jié)為DC1
	lpOutBuffer[1]=''0'';  //第2個(gè)字節(jié)為字符0(30H)
	lpOutBuffer[2]=''0''; //第3個(gè)字節(jié)為字符0(30H)
	lpOutBuffer[3]=''1''; // 第4個(gè)字節(jié)為字符1(31H)
	lpOutBuffer[4]=''0''; //第5個(gè)字節(jié)為字符0(30H)
	lpOutBuffer[5]=''1''; //第6個(gè)字節(jié)為字符1(31H)
	lpOutBuffer[6]=''\x03''; //第7個(gè)字節(jié)為字符ETX
	//從該段代碼可以看出,儀表的通訊地址為001	
	DWORD dwBytesWrite=7;
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	BOOL bWriteStat;
	ClearCommError(hCom,&dwErrorFlags,&ComStat);
	bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL);
	if(!bWriteStat)
	{
		AfxMessageBox("寫串口失敗!");
	}

}
void CRS485CommDlg::OnReceive() 
{
	// TODO: Add your control notification handler code here

	char str[100];
	memset(str,''\0'',100);
	DWORD wCount=100;//讀取的字節(jié)數(shù)
	BOOL bReadStat;
	bReadStat=ReadFile(hCom,str,wCount,&wCount,NULL);
	if(!bReadStat)
		AfxMessageBox("讀串口失敗!");
	PurgeComm(hCom, PURGE_TXABORT|
		PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
	m_disp=str;
	UpdateData(FALSE);
	
}
您可以觀察返回的字符串,其中有和儀表顯示值相同的部分,您可以進(jìn)行相應(yīng)的字符串操作取出儀表的顯示值。
打開ClassWizard,為靜態(tài)文本框IDC_DISP添加CString類型變量m_disp,同時(shí)添加WM_CLOSE的相應(yīng)函數(shù):
void CRS485CommDlg::OnClose() 
{
	// TODO: Add your message handler code here and/or call default
    CloseHandle(hCom);	//程序退出時(shí)關(guān)閉串口
	CDialog::OnClose();
}
程序的相應(yīng)部分已經(jīng)在代碼內(nèi)部作了詳細(xì)介紹。連接好硬件部分,編譯運(yùn)行程序,細(xì)心體會(huì)串口同步操作部分。

例程2

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

HANDLE hCom; //全局變量,

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

	hCom=CreateFile("COM1",//COM1口
		GENERIC_READ|GENERIC_WRITE, //允許讀和寫
		0, //獨(dú)占方式
		NULL,
		OPEN_EXISTING, //打開而不是創(chuàng)建
		FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重疊方式
		NULL);
	if(hCom==(HANDLE)-1)
	{
		AfxMessageBox("打開COM失敗!");
		return FALSE;
	}

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

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


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

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

	PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
分別雙擊IDC_SEND按鈕和IDC_RECEIVE按鈕,添加兩個(gè)按鈕的響應(yīng)函數(shù):
void CRS485CommDlg::OnSend() 
{
	// TODO: Add your control notification handler code here
	OVERLAPPED m_osWrite;
	memset(&m_osWrite,0,sizeof(OVERLAPPED));
	m_osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);


	char lpOutBuffer[7];
	memset(lpOutBuffer,''\0'',7);
	lpOutBuffer[0]=''\x11'';
	lpOutBuffer[1]=''0'';
	lpOutBuffer[2]=''0'';
	lpOutBuffer[3]=''1'';
	lpOutBuffer[4]=''0'';
	lpOutBuffer[5]=''1'';
	lpOutBuffer[6]=''\x03'';
	
	DWORD dwBytesWrite=7;
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	BOOL bWriteStat;
	ClearCommError(hCom,&dwErrorFlags,&ComStat);
	bWriteStat=WriteFile(hCom,lpOutBuffer,
		dwBytesWrite,& dwBytesWrite,&m_osWrite);

	if(!bWriteStat)
	{
		if(GetLastError()==ERROR_IO_PENDING)
		{
			WaitForSingleObject(m_osWrite.hEvent,1000);
		}
	}

}

void CRS485CommDlg::OnReceive() 
{
	// TODO: Add your control notification handler code here
	OVERLAPPED m_osRead;
	memset(&m_osRead,0,sizeof(OVERLAPPED));
	m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

	COMSTAT ComStat;
	DWORD dwErrorFlags;
	
	char str[100];
	memset(str,''\0'',100);
	DWORD dwBytesRead=100;//讀取的字節(jié)數(shù)
	BOOL bReadStat;

	ClearCommError(hCom,&dwErrorFlags,&ComStat);
	dwBytesRead=min(dwBytesRead, (DWORD)ComStat.cbInQue);
	bReadStat=ReadFile(hCom,str,
		dwBytesRead,&dwBytesRead,&m_osRead);
	if(!bReadStat)
	{
		if(GetLastError()==ERROR_IO_PENDING)
	    //GetLastError()函數(shù)返回ERROR_IO_PENDING,表明串口正在進(jìn)行讀操作
		{
			WaitForSingleObject(m_osRead.hEvent,2000);
		    //使用WaitForSingleObject函數(shù)等待,直到讀操作完成或延時(shí)已達(dá)到2秒鐘
		    //當(dāng)串口讀操作進(jìn)行完畢后,m_osRead的hEvent事件會(huì)變?yōu)橛行盘?hào)
		}
	}

	PurgeComm(hCom, PURGE_TXABORT|
		PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
	m_disp=str;
	UpdateData(FALSE);
}
打開ClassWizard,為靜態(tài)文本框IDC_DISP添加CString類型變量m_disp,同時(shí)添加WM_CLOSE的相應(yīng)函數(shù):
void CRS485CommDlg::OnClose() 
{
	// TODO: Add your message handler code here and/or call default
    CloseHandle(hCom);	//程序退出時(shí)關(guān)閉串口
	CDialog::OnClose();
}
您可以仔細(xì)對(duì)照這兩個(gè)例程,細(xì)心體會(huì)串口同步操作和異步操作的區(qū)別。

好了,就到這吧,祝您好運(yùn)。

posted on 2011-09-09 18:13 厚積薄發(fā) 閱讀(338) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Windows編程

導(dǎo)航

<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

統(tǒng)計(jì)

常用鏈接

留言簿

隨筆分類

文章分類

文章檔案

搜索

最新評(píng)論

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美主播一区二区三区美女 久久精品人| 99在线|亚洲一区二区| 欧美私人啪啪vps| 欧美成人首页| 国内精品久久久久国产盗摄免费观看完整版 | 一区二区欧美国产| 亚洲日本欧美| 久久综合国产精品台湾中文娱乐网| 久久成人在线| 国产精自产拍久久久久久蜜| 99国产精品私拍| 亚洲精品日韩在线| 久久久久久网站| 久久久天天操| 国产一区二区三区电影在线观看| 亚洲愉拍自拍另类高清精品| 亚洲欧美不卡| 国产精品高潮呻吟久久| 一区二区激情视频| 亚洲欧美成人一区二区三区| 欧美天堂亚洲电影院在线播放| 亚洲欧洲精品一区二区精品久久久| 伊人成人在线| 欧美h视频在线| 亚洲丁香婷深爱综合| 亚洲国产毛片完整版| 久久人人爽人人爽| 欧美成人激情视频免费观看| 亚洲国产精品久久久久婷婷老年| 久久久久一区| 亚洲国产欧美日韩精品| 亚洲美女视频在线免费观看| 欧美日韩国产综合视频在线| 日韩亚洲在线观看| 性欧美18~19sex高清播放| 国产精品丝袜久久久久久app| 亚洲综合国产| 久久亚洲精品一区二区| 亚洲国语精品自产拍在线观看| 免费久久99精品国产自| 亚洲乱码一区二区| 欧美一区二区福利在线| 国产一区二区三区成人欧美日韩在线观看 | 欧美成年人视频| 99riav1国产精品视频| 国产精品久久久久免费a∨| 午夜免费电影一区在线观看| 猛男gaygay欧美视频| 99精品热视频| 国产欧美视频一区二区| 久久亚洲高清| 最近中文字幕mv在线一区二区三区四区 | 免费在线欧美视频| 亚洲黄色一区| 国产精品成人一区二区三区夜夜夜| 亚洲视频在线一区观看| 久久人人爽人人| 99在线精品视频在线观看| 国产精品亚洲一区二区三区在线| 欧美中文字幕在线观看| 亚洲日本中文字幕| 欧美在线亚洲在线| 亚洲伦理中文字幕| 国产午夜精品理论片a级大结局| 美女脱光内衣内裤视频久久网站| 亚洲天堂av在线免费| 欧美成人午夜视频| 性亚洲最疯狂xxxx高清| 亚洲激情网站| 国内精品久久久| 国产精品v欧美精品v日本精品动漫| 久久成人羞羞网站| 99精品99| 亚洲国产精品成人一区二区| 欧美在线1区| 亚洲视频免费在线观看| 亚洲动漫精品| 国产午夜精品久久| 欧美午夜一区二区三区免费大片| 免费精品视频| 久久精品国产亚洲精品| 亚洲高清在线| 国产午夜精品麻豆| 国产精品多人| 欧美日韩国语| 欧美电影在线免费观看网站| 久久久久国色av免费观看性色| 一区二区三区高清在线观看| 亚洲黄色毛片| 欧美激情精品久久久久| 久久久久久久一区二区三区| 亚洲综合日本| 一区二区三区免费看| 亚洲日本乱码在线观看| 亚洲高清中文字幕| 在线免费日韩片| 在线观看国产日韩| 韩日欧美一区二区三区| 国产亚洲成精品久久| 国产嫩草一区二区三区在线观看 | 久久亚洲精品一区二区| 欧美一级黄色网| 午夜在线不卡| 午夜精品久久久久久久白皮肤 | 一区二区三区免费观看| 99成人免费视频| 日韩亚洲欧美精品| 在线一区免费观看| 亚洲免费网址| 欧美一区综合| 久久久久久久综合狠狠综合| 久久黄色小说| 欧美成人一区二区三区片免费| 免费美女久久99| 欧美日韩国产精品一区二区亚洲| 欧美日韩精品欧美日韩精品一| 欧美伦理91| 国产精品激情电影| 国产日韩欧美在线播放| 激情六月婷婷久久| 亚洲日韩第九十九页| 99re亚洲国产精品| 亚洲免费网站| 久久米奇亚洲| 亚洲激情成人网| 在线视频日韩精品| 欧美在线亚洲一区| 欧美国产在线电影| 欧美网站在线观看| 国产一区二区三区精品欧美日韩一区二区三区 | 国产综合香蕉五月婷在线| 伊人色综合久久天天| 亚洲人成欧美中文字幕| 亚洲一区三区视频在线观看| 欧美一区综合| 欧美国产另类| 亚洲色诱最新| 久久久久久精| 欧美日韩午夜剧场| 国产亚洲精品一区二555| 亚洲高清毛片| 亚洲欧美在线看| 欧美刺激午夜性久久久久久久| 亚洲伦理中文字幕| 亚洲欧美日韩天堂| 欧美成人在线网站| 国产欧美日韩亚洲精品| 亚洲人成人一区二区在线观看| 亚洲欧美日产图| 欧美激情久久久| 亚洲综合色噜噜狠狠| 免费亚洲一区| 国产日韩精品一区二区三区| 亚洲欧洲一区二区三区久久| 午夜精品成人在线| 亚洲第一主播视频| 亚洲欧美日韩国产另类专区| 欧美成人免费在线| 国产永久精品大片wwwapp| 亚洲无毛电影| 亚洲国产色一区| 久久福利毛片| 国产精品一级二级三级| 中文国产一区| 亚洲国产高潮在线观看| 欧美一区二区视频免费观看| 欧美日韩一区视频| 亚洲精品美女91| 美国三级日本三级久久99| 亚洲一级网站| 欧美午夜片在线观看| 亚洲乱码国产乱码精品精天堂| 久久青草欧美一区二区三区| 亚洲主播在线观看| 欧美新色视频| 一区二区三区精密机械公司 | 蜜桃视频一区| 亚洲国产1区| 免费观看成人www动漫视频| 亚洲自拍偷拍视频| 国产精品欧美日韩久久| 亚洲一区二区三区乱码aⅴ蜜桃女| 亚洲国产免费看| 欧美高清视频一区| 亚洲精品在线电影| 亚洲高清资源| 欧美搞黄网站| 亚洲精品系列| 亚洲精品久久| 欧美美女福利视频| 一级日韩一区在线观看| 亚洲美女精品一区| 欧美日韩亚洲系列| 亚洲午夜一区二区三区| 亚洲精选大片| 国产精品久久久久永久免费观看 | 欧美一区二区三区免费观看| 亚洲女人av| 国内自拍一区| 欧美黄色一区二区|