Win32 串口编程笔记1
在Win32中,串口是作為文件處理的,使用CreateFile()函數可以打開串口,進行讀寫訪問操作。CreateFile()返回串口句柄,可以在以后的端口操作中使用。關閉端口使用CloseHandle()函數來完成。
HANDLE WINAPI CreateFile(_In_??????LPCTSTR lpFileName,//要打開或創建的文件名_In_??????DWORD dwDesiredAccess,//訪問類型_In_??????DWORD dwShareMode,//共享方式_In_opt_??LPSECURITY_ATTRIBUTES lpSecurityAttributes,//安全屬性_In_??????DWORD dwCreationDisposition,//指定要打開的文件已存在或不存在的動作_In_??????DWORD dwFlagsAndAttributes,//文件屬性和標志_In_opt_??HANDLE hTemplateFile//一個指向模板文件的句柄 ); lpFileName:要打開或創建的文件名。打開串口設備可以直接寫串口號,如"COM4",需要注意的是COM10及以上的串口格式應為: "\\\\.\\COM10"。dwDesiredAccess:訪問類型,0:設備查詢訪問權限(程序可以不訪問該設備就能查詢到設備屬性)
??????????????????????????????????????????????????????? GENERIC_READ:讀訪問權限
??????????????????????????????????????????????????????? GENERIC_WRITE:寫訪問權限
dwShareMode:共享方式,0:文件不能被共享,其它打開操作會失敗。
????????????????????????????????????????????????? FILE_SHARE_DELETE:其它刪除操作會成功。
????????????????????????????????????????????????? FILE_SHARE_READ:其它讀操作會成功。
????????????????????????????????????????????????? FILE_SHARE_WRITE:其它寫操作會成功。
lpSecurityAttributes:安全屬性,一個指向SECURITY_ATTRIBUTES結構的指針。
dwCreationDisposition:指定要打開的文件以存在或不存在的動作,
??????????????????????????????????????????? CREATE_NEW :創建文件;如文件存在則會失敗
??????????????????????????????????????????? CREATE_ALWAYS:創建文件,如果文件已存在則清空
??????????????????????????????????????????? OPEN_EXISTING:打開文件,文件必須已經存在,否則會失敗
??????????????????????????????????????????? OPEN_ALWAYS:打開文件,如果文件不存在則創建它
??????????????????????????????????????????? TRUNCATE_EXISTING 打開文件,且將現有文件縮短為零長度(需要GENERIC_WRITE權限),如果文件不存在則失敗
dwFlagsAndAttributes:文件屬性和標志,
???????????????????????????????????
???????????????????????????????? ? ?
??????????????????????????????????????? 如果CreateFile()打開的是命名管道客戶端,那么dwFlagsAndAttributes參數也可以包含服務信息的安全特性,
????????????????????????????????????? 當程序指定了SECURITY_SQOS_PRESENT標志,dwFlagsAndAttributes可以包含下表中一個或多個值。
???????????????????????????????????
hTemplateFile:一個指向模板文件的句柄,且該模板必須是以GENERIC_READ訪問方式打開的。如果此參數不是NULL,則會使用hTemplateFile關聯的文件的屬性和標志來創建文件。另外,如果是打開一個現有文件,則該參數被忽略。
以下為打開COM1串口的示例代碼:
HANDLE hCom; DWORD dwError; hCom = CreateFile("COM1",//對串口1進行操作GENERIC_READ|GENERIC_WRITE,//允許讀和寫0,//獨占方式NULL,//默認安全屬性OPEN_EXISTING,//串口必須存在FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//重疊方式NULL); if(hCom == INVALID_HANDLE_VALUE) {dwError = GetLastError();...... }......CloseHandle(hCom);2、串口配置和屬性
串口打開后就可以設置接收緩沖區和發送緩沖區,這可以通過SetupComm()函數實現,如果通信的速率較高,則應該設置較大的緩沖區:
??? BOOL WINAPI SetupComm(
??? __in HANDLE hFile,//串口句柄
??? __in DWORD dwInQueue,//輸入緩沖區大小
??? __in DWORD dwOutQueue//輸出緩沖區大小
??? );
可以通過函數GetCommState()和SetCommState()來獲得和設置串口的配置:
BOOL WINAPI GetCommState(
??? __in? HANDLE hFile,//串口句柄
??? __out LPDCB lpDCB//保存串口配置信息
??? );
BOOL WINAPI SetCommState(
??? __in HANDLE hFile,//串口句柄
??? __in LPDCB lpDCB//設置串口配置信息
??? );
DCB結構:
????????????????
DCB結構中常用成員:
?????????????????????????????????????????????????? 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為激活奇偶校驗檢查?
?????????????????????????????????????????????????? DWORD Parity:校驗方式,值0~4分別對應無校驗、奇校驗、偶校驗、校驗?
????????????????????????????????????????????????????????????????????????????????? 置位(標記校驗)、校驗清零?
?????????????????????????????????????????????????? DWORD ByteSize:一個字節的數據位個數,范圍是5~8?
?????????????????????????????????????????????????? DWORD StopBits:停止位個數,0~2分別對應1位停止位、1.5位停止位、2位停止位
操作舉例:
DCB ComDCB; GetCommState(hComm,&ComDCB);//取得當前串口狀態 ComDCB.BaudRate=9600;//更改為9600bps,該值即為你要修改后的波特率 SetCommState(hComm,&ComDCB;//將更改后的參數寫入串口GetCommProperties()可以獲得串口的屬性:
BOOL WINAPI GetCommProperties(
??? __in? HANDLE hFile,//串口句柄
??? __out LPCOMMPROP lpCommProp//保存串口屬性
??? );
CommConfigDialog()用來對通信設備進行配置,從而改變數據傳輸速率、數據位、奇偶校方法、停止位和流控制方法,當函數返回時,選定的設置在COMMCONFIG結構的DCB參數中返回:
??? BOOL WINAPI CommConfigDialog(
??? __in???? LPCWSTR lpszName,//端口名
??? __in_opt HWND hWnd,//擁有對話框的窗口句柄
??? __inout? LPCOMMCONFIG lpCC//指向一個COMMCONFIG結構
??? );
對于已經打開的串口,對端口設置進行更改應通過SetCommState()來進行。
3、讀寫串口
一般在程序中使用WriteFile()向串口中寫數據,調用ReadFile()從串口讀數據。
BOOL WINAPI WriteFile(_In_?????????HANDLE hFile,//文件句柄_In_?????????LPCVOID lpBuffer,//指向一個緩沖區,包含要寫入的數據_In_?????????DWORD nNumberOfBytesToWrite,//要寫入數據的字節數_Out_opt_????LPDWORD lpNumberOfBytesWritten,//實際寫入的字節數_Inout_opt_??LPOVERLAPPED lpOverlapped//指向一個OVERLAPPEN結構體 );如果想要異步讀寫操作,則lpOverlappen參數不能為NULL,且在CreateFile()打開文件時應指定FILE_FLAG_OVERLAPPEN標記。
需要注意的是,當ReadFile和WriteFile返回FALSE時,不一定就是操作失敗,線程應該調用GetLastError函數分析返回的結果。例如,在重疊操作時如果操作還未完成函數就返回,那么函數就返回FALSE,而且GetLastError函數返回ERROR_IO_PENDING。這說明重疊操作還未完成。
WriteFileEx()與ReadFileEx()只能用于異步讀寫操作,且可以設置一個讀寫完成后自動調用的回調函數。
eg:
HANDLE hFile; char DataBuffer[] = "This is some test data to write to the file.";DWORD dwBytesToWrite = (DWORD)strlen(DataBuffer);DWORD dwBytesWritten = 0;BOOL bErrorFlag = FALSE;hFile = CreateFile(argv[1], // name of the writeGENERIC_WRITE, // open for writing0, // do not shareNULL, // default securityCREATE_NEW, // create new file onlyFILE_ATTRIBUTE_NORMAL, // normal fileNULL); // no attr. templateif (hFile == INVALID_HANDLE_VALUE) { DisplayError(TEXT("CreateFile"));_tprintf(TEXT("Terminal failure: Unable to open file \"%s\" for write.\n"), argv[1]);return;}bErrorFlag = WriteFile( hFile, // open file handleDataBuffer, // start of data to writedwBytesToWrite, // number of bytes to write&dwBytesWritten, // number of bytes that were writtenNULL); // no overlapped structureif (FALSE == bErrorFlag){DisplayError(TEXT("WriteFile"));printf("Terminal failure: Unable to write to file.\n");}else{if (dwBytesWritten != dwBytesToWrite){// This is an error because a synchronous write that results in// success (WriteFile returns TRUE) should write all data as// requested. This would not necessarily be the case for// asynchronous writes.printf("Error: dwBytesWritten != dwBytesToWrite\n");}else{_tprintf(TEXT("Wrote %d bytes to %s successfully.\n"), dwBytesWritten, argv[1]);}}CloseHandle(hFile); BOOL WINAPI ReadFile(_In_?????????HANDLE hFile,//文件句柄_Out_????????LPVOID lpBuffer,//指向一個緩沖區,保存讀取的數據_In_?????????DWORD nNumberOfBytesToRead,//要讀取數據的字節數_Out_opt_????LPDWORD lpNumberOfBytesRead,//實際讀取的字節數_Inout_opt_??LPOVERLAPPED lpOverlapped//指向一個OVERLAPPED結構 ); eg: #define BUFFERSIZE 5 DWORD g_BytesTransferred = 0;......VOID CALLBACK FileIOCompletionRoutine(__in DWORD dwErrorCode,__in DWORD dwNumberOfBytesTransfered,__in LPOVERLAPPED lpOverlapped ){_tprintf(TEXT("Error code:\t%x\n"), dwErrorCode);_tprintf(TEXT("Number of bytes:\t%x\n"), dwNumberOfBytesTransfered);g_BytesTransferred = dwNumberOfBytesTransfered;}......HANDLE hFile; DWORD dwBytesRead = 0;char ReadBuffer[BUFFERSIZE] = {0};OVERLAPPED ol = {0};hFile = CreateFile(L"test1.dat", // file to openGENERIC_READ, // open for readingFILE_SHARE_READ, // share for readingNULL, // default securityOPEN_EXISTING, // existing file onlyFILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // normal fileNULL); // no attr. templateif (hFile == INVALID_HANDLE_VALUE) { _tprintf(TEXT("Terminal failure: unable to open file for read.\n") );return FALSE; }if( FALSE == ReadFileEx(hFile, ReadBuffer, BUFFERSIZE-1, &ol, FileIOCompletionRoutine) ){printf("Terminal failure: Unable to read from file.\n GetLastError=%08x\n", GetLastError());CloseHandle(hFile);return FALSE;}SleepEx(5000, TRUE);dwBytesRead = g_BytesTransferred;if (dwBytesRead > 0 && dwBytesRead <= BUFFERSIZE-1){ReadBuffer[dwBytesRead]='\0'; // NULL character_tprintf(TEXT("Data read from test1.dat (%d bytes): \n"), dwBytesRead);printf("%s\n", ReadBuffer);}else if (dwBytesRead == 0){_tprintf(TEXT("No data read from file test1.dat\n"));}else{printf("\n Unexpected value for dwBytesRead \n");}CloseHandle(hFile);4、超時處理
以下轉自:http://blog.csdn.net/augusdi/article/details/10220911
在用ReadFile和WriteFile讀寫串行口時,需要考慮超時問題,如果在指定的時間內沒有讀出或寫入指定數量的字符,那么ReadFile或WriteFile的操作就會結束。要查詢當前的超時設置應調用GetCommTimeouts函數,該函數會填充一個COMMTIMEOUTS結構。調用SetCommTimeouts可以用某一個COMMTIMEOUTS結構的內容來設置超時。
BOOL GetCommTimeouts(_In_ HANDLE hFile,_Out_ LPCOMMTIMEOUTS lpCommTimeouts ); BOOL SetCommTimeouts(_In_ HANDLE hFile,_In_ LPCOMMTIMEOUTS lpCommTimeouts );有兩種超時:間隔超時和總超時。間隔超時是指在接收時兩個字符之間的最大時延,總超時是指讀寫操作總共花費的最大時間。寫操作只支持總超時,而讀操作兩種超時均支持。
用COMMTIMEOUTS結構可以規定讀/寫操作的超時,該結構的定義為:
typedef struct _COMMTIMEOUTS{ DWORD ReadIntervalTimeout; // 讀間隔超時:接收時,兩字符間最大的時延。 DWORD ReadTotalTimeoutMultiplier; // 讀時間系數:讀取每字節的超時。 DWORD ReadTotalTimeoutConstant; // 讀時間常量:讀串口數據的固定超時。 //讀總超時 = ReadTotalTimeoutMultiplier*字節數 + ReadTotalTimeoutConstant DWORD WriteTotalTimeoutMultiplier;// 寫時間系數:寫每字節的超時。 DWORD WriteTotalTimeoutConstant; // 寫時間常量:寫串口數據的固定超時。 //寫總超時 = WriteTotalTimeoutMultiplier*字節數 + WriteTotalTimeoutConstant } COMMTIMEOUTS,*LPCOMMTIMEOUTS; 如果ReadIntervalTimeout為MAXDWORD,???并且ReadTotalTimeoutConstant和ReadTotalTimeoutMultiplier都為0,???則指定讀操作攜帶已經收到的字符立即返回,即使沒有收到任何字符;如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都為0,則在讀操作時忽略總超時數;如果WriteTotalTimeoutMultiplier和WriteTotalTimeoutConstant都為0,則在寫操作時忽略總超時數。COMMTIMEOUTS結構的成員都以毫秒為單位,用戶設置通訊超時后,如沒有出錯,串口已經被打開。
可以看出,間隔超時和總超時的設置是不相關的,這可以方便通信程序靈活地設置各種超時。如果所有寫超時參數均為0,那么就不使用寫超時。如果ReadIntervalTimeout為0,那么就不使用讀間隔超時,如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都為0,則不使用讀總超時。如果讀間隔超時被設置成MAXDWORD并且兩個讀總超時為0,那么在讀一次輸入緩沖區中的內容后讀操作就立即完成,而不管是否讀入了要求的字符。?在用重疊方式讀寫串行口時,雖然ReadFile和WriteFile在完成操作以前就可能返回,但超時仍然是起作用的。在這種情況下,超時規定的是操作的完成時間,而不是ReadFile和WriteFile的返回時間。
5、通信狀態和通信錯誤
如果在串口通信中發生錯誤,如終端錯誤、奇偶錯誤等,I/O操作將會終止。如果程序要進一步執行I/O操作,必須調用ClearCommError()函數。ClearCommError()可以清除(獲得)通信錯誤和獲得串口的當前通信狀態。
BOOL WINAPI ClearCommError(_In_???????HANDLE hFile,//串口句柄_Out_opt_??LPDWORD lpErrors,//錯誤碼_Out_opt_??LPCOMSTAT lpStat//通訊狀態 ); lpErrors錯誤碼解釋如下:?1-CE_BREAK:檢測到中斷信號。意思是說檢測到某個字節數據缺少合法的停止位。?
2-CE_FRAME:硬件檢測到幀錯誤。?
3-CE_IOE:通信設備發生輸入/輸出錯誤。?
4-CE_MODE:設置模式錯誤,或是hFile值錯誤。?
5-CE_OVERRUN:溢出錯誤,緩沖區容量不足,數據將丟失。?
6-CE_RXOVER:溢出錯誤。?
7-CE_RXPARITY:硬件檢查到校驗位錯誤。?
8-CE_TXFULL:發送緩沖區已滿。
lpStat通訊狀態結構體如下:
typedef struct _COMSTAT{ ... ... DWORD cbInQue; //輸入緩沖區中的字節數 DWORD cbOutQue;//輸出緩沖區中的字節數 }COMSTAT,*LPCOMSTAT; 該結構中對我們很重要的只有上面兩個參數,其他的我們可以不用管。舉例:
unsigned char ucRxBuff[20]; COMSTAT ComStat; DWORD dwError=0; DWORD BytesRead=0; OVERLAPPED ov_Read; ov_Read.hEvent=CreateEvent(NULL, true, false, NULL);//必須創建有效事件 ClearCommError(hComm,&dwError,&ComStat);//檢查串口接收緩沖區中的數據個數 bResult=ReadFile(hComm,ucRxBuff, ComStat.cbInQue, &BytesRead, &ov_Read); //假如當前串口中有5個字節數據的話,那么執行完ClearCommError()函數后,ComStat \ 結構中的ComStat.cbInQue將被填充為5,此值在ReadFile函數中可被直接利用。 6、其它串口通信常用API以下轉自:http://blog.csdn.net/vodomine/article/details/6542089
PurgeComm()
用途:清空串口緩沖區,在讀寫串口之前,還要用PurgeComm()函數清空緩沖區。
原型:BOOL PurgeComm(HANDLE hFile,? DWORD dwFlags );?
參數說明:?
?? -hFile:串口句柄?
?? -dwFlags:指定串口執行的動作,由以下參數組成:?
?? -PURGE_TXABORT:停止目前所有的傳輸工作立即返回不管是否完成傳輸動作。?
?? -PURGE_RXABORT:停止目前所有的讀取工作立即返回不管是否完成讀取動作。?
?? -PURGE_TXCLEAR:清除發送緩沖區的所有數據。?
?? -PURGE_RXCLEAR:清除接收緩沖區的所有數據。?
操作舉例:
SetCommMask()
用途:設置串口通信事件。?
原型:BOOL SetCommMask(HANDLE hFile,? DWORD dwEvtMask);?
參數說明:?
?? -hFile:串口句柄?
?? -dwEvtMask:準備監視的串口事件掩碼?
注:在用api函數撰寫串口通信函數時大體上有兩種方法,一種是查尋法,另外一種是事件通知法。 這兩種方法的區別在于收串口數據時,前一種方法是主動的周期性的查詢串口中當前有沒有 數據;后一種方法是事先設置好需要監視的串口通信事件,然后依靠單獨開設的輔助線程進行 監視該事件是否已發生,如果沒有發生的話該線程就一直不停的等待直到該事件發生后,將該串口事件以消息的方式通知主窗體,然后主窗體收到該消息后依據不同的事件性質進行處理。 比如說當主窗體收到監視線程發來的RX_CHAR(串口中有數據)的消息后,就可以用ReadFile() 函數去讀串口。
dwEvtMask參數有如下信息掩碼位值:?
EV_BREAK:收到BREAK信號?
EV_CTS:CTS(dear to send)線路發生變化?
EV_DSR:DST(Data Set Ready)線路發生變化?
EV_ERR:線路狀態錯誤,包括了CE_FRAME/CE_OVERRUN/CE_RXPARITY 3鐘錯誤。?
EV_RING:檢測到振鈴信號。?
EV_RLSD:CD(Carrier Detect)線路信號發生變化。?
EV_RXCHAR:輸入緩沖區中已收到數據。?
EV_RXFLAG:使用SetCommState()函數設置的DCB結構中的等待字符已被傳入輸入緩沖區中。?
EV_TXEMPTY:輸出緩沖區中的數據已被完全送出。?
操作舉例:
用途:用來判斷用SetCommMask()函數設置的串口通信事件是否已發生。?
原型:
?? -hFile:串口句柄?
?? -lpEvtMask:函數執行完后如果檢測到串口通信事件的話就將其寫入該參數中。?
?? -lpOverlapped:指向重疊結構,如果串口打開時指定了FILE_FLAG_OVERLAPPED標志 ,則改參數不能為NULL,且重疊結構中 應該包含一個手工重置對象句柄(通過CreateEvent()創建)。
操作舉例: DWORD dwMask,dwTrans,dwError=0,err; OVERLAPPED os; memset(&os,0,sizeof(OVERLAPPED)); os.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(!WaitCommEvent(hComm,&dwMask,&os)) { //如果異步操作不能立即完成的話,函數返回FALSE,并且調用GetLastError()函 //數分析錯誤原因后返回ERROR_IO_PENDING,指示異步操作正在后臺進行.這種情 //況下,在函數返回之前系統設置OVERLAPPED結構中的事件為無信號狀態,該函數 //等待用SetCommMask()函數設置的串口事件發生,共有9種事件可被監視: //EV_BREAK,EV_CTS,EV_DSR,EV_ERR,EV_RING,EV_RLSD,EV_RXCHAR, //EV_RXFLAG,EV_TXEMPTY;當其中一個事件發生或錯誤發生時,函數將 //OVERLAPPED結構中的事件置為有信號狀態,并將事件掩碼填充到dwMask參數中 if(GetLastError()==ERROR_IO_PENDING){ // /*在此等待異步操作結果,直到異步操作結束時才返回.實際上此時 */ /*WaitCommEvent()函數一直在等待串口監控的事件之一發生,當事件發*/ /*生時該函數將OVERLAPPED結構中的事件句柄置為有信號狀態,此時 */ /*GetOverlappedResult()函數發現此事件有信號后馬上返回,然后下面*/ /*的程序馬上分析WaitCommEvent()函數等到的事件是被監視的串口事 */ /*件中的哪一個,然后執行相應的動作并發出相應消息. */ // GetOverlappedResult(hComm,&os,&dwTrans,true); switch(dwMask){ case EV_RXCHAR: PostMessage(Parent,WM_COMM_RXCHAR,0,0); break; case EV_TXEMPTY: PostMessage(Parent,WM_COMM_TXEMPTY,0,0); break; case EV_ERR: switch(dwError){ case CE_FRAME: err=0; break; case CE_OVERRUN: err=1; break; case CE_RXPARITY: err=2; break; default:break; } PostMessage(Parent,WM_COMM_ERR,(WPARAM)0,(LPARAM)err); break; case EV_BREAK: PostMessage(Parent,WM_COMM_BREAK,0,0); break; case ...://其他用SetCommMask()函數設置的被監視的串口通信事件。 ... ... break; default:break; } } } GetOverlappedResult()可以判斷一個重疊操作當前的狀態,用來判斷異步操作是否完成。 BOOL WINAPI GetOverlappedResult(_In_???HANDLE hFile,//文件句柄_In_???LPOVERLAPPED lpOverlapped,//指向欲檢查的重疊結構_Out_??LPDWORD lpNumberOfBytesTransferred,//讀或寫操作的字節數_In_???BOOL bWait ); 如果參數bWait為TRUE則函數會一直等待直到重疊機構中的hEvent變成有信號;FALSE為如果檢測到pending狀態則立即返回,此時函數返回FALSE,GetLastError()返回值為ERROR_IO_INCOMPLETE。
MSDN上關于WaitCommEvent的說明及例子:
WaitCommEvent()用來檢測指定通信設備上一組事件的發生,可以通過SetCommMask()函數來設置通信設備上的事件掩碼,GetCommMask()函數獲得通信設備上的事件掩碼。
如果重疊操作不能立即完成,則WaitCommEvent()返回FALSE,GetLastError()會返回ERROR_IO_PENDING,表示操作正在后臺進行。在WaitCommEvent返回之前,重疊結構中的hEvent成員會被設置為無信號狀態,如果當事件發生或錯誤發生時,其被設置為有信號狀態,應用程序可以調用wait functions(WaitForSingleObject、WaitForSingleObjectEx等)來判斷事件對象的狀態,然后調用GetOverlappedResult()來判斷WaitCommEvent()操作的結果,GetOverlappedResult會報告操作成功或失敗,而參數lpEvtMask會保存具體發生的事件。
#include <windows.h> #include <tchar.h> #include <assert.h> #include <stdio.h>void _tmain(int argc, TCHAR *argv[]) {HANDLE hCom;OVERLAPPED o;BOOL fSuccess;DWORD dwEvtMask;hCom = CreateFile( TEXT("\\\\.\\COM1"),GENERIC_READ | GENERIC_WRITE,0, // exclusive access NULL, // default security attributes OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL );if (hCom == INVALID_HANDLE_VALUE) {// Handle the error. printf("CreateFile failed with error %d.\n", GetLastError());return;}// Set the event mask. fSuccess = SetCommMask(hCom, EV_CTS | EV_DSR);if (!fSuccess) {// Handle the error. printf("SetCommMask failed with error %d.\n", GetLastError());return;}// Create an event object for use by WaitCommEvent. o.hEvent = CreateEvent(NULL, // default security attributes TRUE, // manual-reset event FALSE, // not signaled NULL // no name);// Initialize the rest of the OVERLAPPED structure to zero.o.Internal = 0;o.InternalHigh = 0;o.Offset = 0;o.OffsetHigh = 0;assert(o.hEvent);if (WaitCommEvent(hCom, &dwEvtMask, &o)) {if (dwEvtMask & EV_DSR) {// To do.}if (dwEvtMask & EV_CTS) {// To do. }}else{DWORD dwRet = GetLastError();if( ERROR_IO_PENDING == dwRet){printf("I/O is pending...\n");// To do.}else printf("Wait failed with error %d.\n", GetLastError());} }7、串口通信API流程
????? 無論那種操作方式,一般都通過四個步驟來完成:??????
???????? (1) 打開串口
???????? (2) 設置和配置串口
???????? (3) 讀寫串口
???????? (4) 關閉串口
??????? 打開串口:
HANDLE hCom; DWORD dwError; hCom = CreateFile("COM1",//對串口1進行操作GENERIC_READ|GENERIC_WRITE,//允許讀和寫0,//獨占方式NULL,//默認安全屬性OPEN_EXISTING,//串口必須存在0,//同步方式,重疊方式:FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPEDNULL); if(hCom == INVALID_HANDLE_VALUE) {AfxMessageBox(L"打開串口失敗");return FALSE; }??????? 設置和配置串口:
<span style=";">//設置緩沖區大小 SetupComm(hCom,1024,1024); //輸入緩沖區和輸出緩沖區的大小都是1024 //設置超時 COMMTIMEOUTS TimeOuts; TimeOuts.ReadIntervalTimeout=1000; //設定讀超時 TimeOuts.ReadTotalTimeoutMultiplier=500; TimeOuts.ReadTotalTimeoutConstant=5000; TimeOuts.WriteTotalTimeoutMultiplier=500; //設定寫超時 TimeOuts.WriteTotalTimeoutConstant=5000; SetCommTimeouts(hCom,&TimeOuts); //設置超時 //配置串口 DCB dcb; GetCommState(hCom,&dcb); dcb.BaudRate=9600; //波特率為9600 dcb.ByteSize=8; //每個字節有8位 dcb.Parity=NOPARITY; //無校驗 dcb.StopBits=TWOSTOPBITS; //兩個停止位 SetCommState(hCom,&dcb);//清空緩沖區 PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);//清空接收和發送緩沖區 </span>???????????? 讀寫串口(異步):
//重疊I/O非常靈活,它也可以實現阻塞(例如我們可以設置一定要讀取到一個數據才能進行到下一步操作)。有兩種方法可以等待操作完成:一種方法是用 //像WaitForSingleObject這樣的等待函數來等待OVERLAPPED結構的hEvent成員;另一種方法是調用GetOverlappedResult函數等待,后面將演示說明。/*使用WaitForSingleObject函數來等待OVERLAPPED結構的hEvent成員*///在使用ReadFile和WriteFile重疊操作時,線程需要創建OVERLAPPED結構以供這兩個函數使用。 //線程通過OVERLAPPED結構獲得當前的操作狀態,該結構最重要的成員是hEvent。hEvent是讀寫事件。 //當串口使用異步通訊時,函數返回時操作可能還沒有完成,程序可以通過檢查該事件得知是否讀寫完畢。 //當調用ReadFile, WriteFile 函數的時候,該成員會自動被置為無信號狀態;當重疊操作完成后, //該成員變量會自動被置為有信號狀態。 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函數返回FALSE { if(GetLastError()==ERROR_IO_PENDING) //GetLastError()函數返回ERROR_IO_PENDING,表明串口正在進行讀操作 { WaitForSingleObject(m_osRead.hEvent,2000); //使用WaitForSingleObject函數等待,直到讀操作完成或延時已達到2秒鐘 //當串口讀操作進行完畢后,m_osRead的hEvent事件會變為有信號 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; /使用GetOverlappedResult()函數*///函數返回重疊操作的結果,用來判斷異步操作是否完成,它是通過判斷OVERLAPPED結構中的 //hEvent是否被置位來實現的。 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函數返回FALSE { if(GetLastError()==ERROR_IO_PENDING) { GetOverlappedResult(hCom, &m_osRead,&dwBytesRead,TRUE); // GetOverlappedResult函數的最后一個參數設為TRUE, //函數會一直等待,直到讀操作完成或由于錯誤而返回。 return dwBytesRead; } return 0; } return dwBytesRead;??????? 關閉串口:CloseHandle(hComm);
總結
以上是生活随笔為你收集整理的Win32 串口编程笔记1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 认识 UART 接口
- 下一篇: classmethod 继承_让人眼花缭