• <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>

            yehao's Blog

            Win32串口編程

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

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

            (1) 打開串口

              Win32系統(tǒng)把文件的概念進行了擴展。無論是文件、通信設(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)建標志,對串口操作該參數(shù)必須置為OPEN_EXISTING;
            • dwFlagsAndAttributes:屬性描述,用于指定該串口是否進行異步操作,該值為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è)備句柄后,常常需要對串口進行一些初始化配置工作。這需要通過一個DCB結(jié)構(gòu)來進行。DCB結(jié)構(gòu)包含了諸如波特率、數(shù)據(jù)位數(shù)、奇偶校驗和停止位數(shù)等信息。在查詢或配置串口的屬性時,都要用DCB結(jié)構(gòu)來作為緩沖區(qū)。
              一般用CreateFile打開串口后,可以調(diào)用GetCommState函數(shù)來獲取串口的初始配置。要修改串口的配置,應該先修改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 標記校驗   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, //標識通訊端口的句柄
               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ù)。如果通信的速率較高,則應該設(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é)束。
              要查詢當前的超時設(shè)置應調(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)的成員都以毫秒為單位。總超時的計算公式是:
            總超時=時間系數(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í)行時線程會被阻塞,從而導致效率下降。在重疊執(zhí)行時,即使操作還未完成,這兩個函數(shù)也會立即返回,費時的I/O操作在后臺進行。
              ReadFile和WriteFile函數(shù)是同步還是異步由CreateFile函數(shù)決定,如果在調(diào)用CreateFile創(chuàng)建句柄時指定了FILE_FLAG_OVERLAPPED標志,那么調(diào)用ReadFile和WriteFile對該句柄進行的操作就應該是重疊的;如果未指定重疊標志,則讀寫操作應該是同步的。ReadFile和WriteFile函數(shù)的同步或者異步應該和CreateFile函數(shù)相一致。
              ReadFile函數(shù)只要在串口輸入緩沖區(qū)中讀入指定數(shù)量的字符,就算完成操作。而WriteFile函數(shù)不但要把指定數(shù)量的字符拷入到輸出緩沖區(qū),而且要等這些字符從串行口送出去后才算完成操作。
              如果操作成功,這兩個函數(shù)都返回TRUE。需要注意的是,當ReadFile和WriteFile返回FALSE時,不一定就是操作失敗,線程應該調(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ù)才能進行到下一步操作)。有兩種方法可以等待操作完成:一種方法是用象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)獲得當前的操作狀態(tài),該結(jié)構(gòu)最重要的成員是hEvent。hEvent是讀寫事件。當串口使用異步通訊時,函數(shù)返回時操作可能還沒有完成,程序可以通過檢查該事件得知是否讀寫完畢。
              當調(diào)用ReadFile, WriteFile 函數(shù)的時候,該成員會自動被置為無信號狀態(tài);當重疊操作完成后,該成員變量會自動被置為有信號狀態(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,表明串口正在進行讀操作	
            	{
            		WaitForSingleObject(m_osRead.hEvent,2000);
            		//使用WaitForSingleObject函數(shù)等待,直到讀操作完成或延時已達到2秒鐘
            		//當串口讀操作進行完畢后,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ù)進行讀操作前,應先使用ClearCommError函數(shù)清除錯誤。ClearCommError函數(shù)的原型如下:
            BOOL ClearCommError(
            
                HANDLE hFile,	// 串口句柄
                LPDWORD lpErrors,	// 指向接收錯誤碼的變量
                LPCOMSTAT lpStat	// 指向通訊狀態(tài)緩沖區(qū)
               );	
            
            該函數(shù)獲得通信錯誤并報告串口的當前狀態(tài),同時,該函數(shù)清除串口的錯誤標志以便繼續(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接口進行的串口通信。其中第一個例程采用同步串口操作,第二個例程采用異步串口操作。
              我們只介紹軟件部分,RS485接口接線方法不作介紹,感興趣的讀者可以查閱相關(guān)資料。

            例程1

              打開VC++6.0,新建基于對話框的工程RS485Comm,在主對話框窗口IDD_RS485COMM_DIALOG上添加兩個按鈕,ID分別為IDC_SEND和IDC_RECEIVE,標題分別為“發(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按鈕,添加兩個按鈕的響應函數(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是標準ASCII碼的一個控制符號,碼值為11H(十進制的17)
            	//在XMA5000的通訊協(xié)議中,DC1表示讀瞬時值
            	//AAA是從機地址碼,也就是XMA5000顯示儀表的通訊地址
            	//BB為通道號,讀瞬時值時該值為01
            	//ETX也是標準ASCII碼的一個控制符號,碼值為03H
            	//在XMA5000的通訊協(xié)議中,ETX表示主機結(jié)束符
            
            	char lpOutBuffer[7];
            	memset(lpOutBuffer,''\0'',7); //前7個字節(jié)先清零
            	lpOutBuffer[0]=''\x11'';  //發(fā)送緩沖區(qū)的第1個字節(jié)為DC1
            	lpOutBuffer[1]=''0'';  //第2個字節(jié)為字符0(30H)
            	lpOutBuffer[2]=''0''; //第3個字節(jié)為字符0(30H)
            	lpOutBuffer[3]=''1''; // 第4個字節(jié)為字符1(31H)
            	lpOutBuffer[4]=''0''; //第5個字節(jié)為字符0(30H)
            	lpOutBuffer[5]=''1''; //第6個字節(jié)為字符1(31H)
            	lpOutBuffer[6]=''\x03''; //第7個字節(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);
            	
            }
            
            您可以觀察返回的字符串,其中有和儀表顯示值相同的部分,您可以進行相應的字符串操作取出儀表的顯示值。
            打開ClassWizard,為靜態(tài)文本框IDC_DISP添加CString類型變量m_disp,同時添加WM_CLOSE的相應函數(shù):
            void CRS485CommDlg::OnClose() 
            {
            	// TODO: Add your message handler code here and/or call default
                CloseHandle(hCom);	//程序退出時關(guān)閉串口
            	CDialog::OnClose();
            }
            
            程序的相應部分已經(jīng)在代碼內(nèi)部作了詳細介紹。連接好硬件部分,編譯運行程序,細心體會串口同步操作部分。

            例程2

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

            HANDLE hCom; //全局變量,

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

            	hCom=CreateFile("COM1",//COM1口
            		GENERIC_READ|GENERIC_WRITE, //允許讀和寫
            		0, //獨占方式
            		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è)定讀超時
            	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按鈕,添加兩個按鈕的響應函數(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,表明串口正在進行讀操作
            		{
            			WaitForSingleObject(m_osRead.hEvent,2000);
            		    //使用WaitForSingleObject函數(shù)等待,直到讀操作完成或延時已達到2秒鐘
            		    //當串口讀操作進行完畢后,m_osRead的hEvent事件會變?yōu)橛行盘?
            		}
            	}
            
            	PurgeComm(hCom, PURGE_TXABORT|
            		PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
            	m_disp=str;
            	UpdateData(FALSE);
            }
            
            打開ClassWizard,為靜態(tài)文本框IDC_DISP添加CString類型變量m_disp,同時添加WM_CLOSE的相應函數(shù):
            void CRS485CommDlg::OnClose() 
            {
            	// TODO: Add your message handler code here and/or call default
                CloseHandle(hCom);	//程序退出時關(guān)閉串口
            	CDialog::OnClose();
            }
            
            您可以仔細對照這兩個例程,細心體會串口同步操作和異步操作的區(qū)別。

            好了,就到這吧,祝您好運。

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

            導航

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統(tǒng)計

            常用鏈接

            留言簿

            隨筆分類

            文章分類

            文章檔案

            搜索

            最新評論

            蜜臀av性久久久久蜜臀aⅴ麻豆| 亚洲va中文字幕无码久久| 久久综合五月丁香久久激情| 久久99热这里只有精品66| 久久久久人妻一区二区三区vr| 久久se精品一区精品二区| 四虎国产精品成人免费久久| 亚洲午夜无码久久久久| 久久99精品国产99久久6男男| 久久久久国产一级毛片高清板 | 狠狠久久综合伊人不卡| 欧美精品国产综合久久| 久久亚洲国产精品一区二区| 国内精品伊人久久久影院| 人人狠狠综合久久亚洲88| 久久这里只精品99re66| 免费观看久久精彩视频| 久久久一本精品99久久精品66 | 狠狠人妻久久久久久综合蜜桃| 亚洲国产精品久久久天堂| 亚洲国产一成久久精品国产成人综合 | 亚洲国产精品热久久| 亚洲国产精品无码久久98| 性高朝久久久久久久久久| 亚洲国产精品久久久久| 91精品国产综合久久婷婷 | 国产午夜精品理论片久久| 久久亚洲AV成人无码电影| 久久只有这里有精品4| 亚洲精品国产综合久久一线| 国内精品免费久久影院| 精品久久久无码中文字幕天天| 久久精品国产69国产精品亚洲| 久久久久免费看成人影片| 国产精品久久久久久久久久影院| 久久久久黑人强伦姧人妻| 精品久久久久久99人妻| 99久久99久久精品国产| 国产女人aaa级久久久级| 亚洲天堂久久精品| 激情综合色综合久久综合|