3、讀寫串口
我們使用ReadFile和WriteFile讀寫串口,下面是兩個(gè)函數(shù)的聲明:
BOOL ReadFile( HANDLE hFile, //串口的句柄
// 讀入的數(shù)據(jù)存儲(chǔ)的地址,
// 即讀入的數(shù)據(jù)將存儲(chǔ)在以該指針的值為首地址的一片內(nèi)存區(qū)
LPVOID lpBuffer,
// 要讀入的數(shù)據(jù)的字節(jié)數(shù)
DWORD nNumberOfBytesToRead,
// 指向一個(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ǔ)的地址,
// 即以該指針的值為首地址的
LPCVOID lpBuffer,
//要寫入的數(shù)據(jù)的字節(jié)數(shù)
DWORD nNumberOfBytesToWrite,
// 指向指向一個(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操作在后臺進(jìn)行。
ReadFile和WriteFile函數(shù)是同步還是異步由CreateFile函數(shù)決定,如果在調(diào)用CreateFile創(chuàng)建句柄時(shí)指定了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ū),而且要等這些字符從串行口送出去后才算完成操作。
如果操作成功,這兩個(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)被置為無信號狀態(tài);當(dāng)重疊操作完成后,該成員變量會(huì)自動(dòng)被置為有信號狀態(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,F(xiàn)ALSE,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)橛行盘?/p>
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ù)清除錯(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成員,下面我們再演示一段調(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);
評論