Win32 串口编程笔记1
在Win32中,串口是作為文件處理的,使用CreateFile()函數(shù)可以打開串口,進(jìn)行讀寫訪問操作。CreateFile()返回串口句柄,可以在以后的端口操作中使用。關(guān)閉端口使用CloseHandle()函數(shù)來完成。
HANDLE WINAPI CreateFile(_In_??????LPCTSTR lpFileName,//要打開或創(chuàng)建的文件名_In_??????DWORD dwDesiredAccess,//訪問類型_In_??????DWORD dwShareMode,//共享方式_In_opt_??LPSECURITY_ATTRIBUTES lpSecurityAttributes,//安全屬性_In_??????DWORD dwCreationDisposition,//指定要打開的文件已存在或不存在的動(dòng)作_In_??????DWORD dwFlagsAndAttributes,//文件屬性和標(biāo)志_In_opt_??HANDLE hTemplateFile//一個(gè)指向模板文件的句柄 ); lpFileName:要打開或創(chuàng)建的文件名。打開串口設(shè)備可以直接寫串口號(hào),如"COM4",需要注意的是COM10及以上的串口格式應(yīng)為: "\\\\.\\COM10"。dwDesiredAccess:訪問類型,0:設(shè)備查詢?cè)L問權(quán)限(程序可以不訪問該設(shè)備就能查詢到設(shè)備屬性)
??????????????????????????????????????????????????????? GENERIC_READ:讀訪問權(quán)限
??????????????????????????????????????????????????????? GENERIC_WRITE:寫訪問權(quán)限
dwShareMode:共享方式,0:文件不能被共享,其它打開操作會(huì)失敗。
????????????????????????????????????????????????? FILE_SHARE_DELETE:其它刪除操作會(huì)成功。
????????????????????????????????????????????????? FILE_SHARE_READ:其它讀操作會(huì)成功。
????????????????????????????????????????????????? FILE_SHARE_WRITE:其它寫操作會(huì)成功。
lpSecurityAttributes:安全屬性,一個(gè)指向SECURITY_ATTRIBUTES結(jié)構(gòu)的指針。
dwCreationDisposition:指定要打開的文件以存在或不存在的動(dòng)作,
??????????????????????????????????????????? CREATE_NEW :創(chuàng)建文件;如文件存在則會(huì)失敗
??????????????????????????????????????????? CREATE_ALWAYS:創(chuàng)建文件,如果文件已存在則清空
??????????????????????????????????????????? OPEN_EXISTING:打開文件,文件必須已經(jīng)存在,否則會(huì)失敗
??????????????????????????????????????????? OPEN_ALWAYS:打開文件,如果文件不存在則創(chuàng)建它
??????????????????????????????????????????? TRUNCATE_EXISTING 打開文件,且將現(xiàn)有文件縮短為零長(zhǎng)度(需要GENERIC_WRITE權(quán)限),如果文件不存在則失敗
dwFlagsAndAttributes:文件屬性和標(biāo)志,
???????????????????????????????????
???????????????????????????????? ? ?
??????????????????????????????????????? 如果CreateFile()打開的是命名管道客戶端,那么dwFlagsAndAttributes參數(shù)也可以包含服務(wù)信息的安全特性,
????????????????????????????????????? 當(dāng)程序指定了SECURITY_SQOS_PRESENT標(biāo)志,dwFlagsAndAttributes可以包含下表中一個(gè)或多個(gè)值。
???????????????????????????????????
hTemplateFile:一個(gè)指向模板文件的句柄,且該模板必須是以GENERIC_READ訪問方式打開的。如果此參數(shù)不是NULL,則會(huì)使用hTemplateFile關(guān)聯(lián)的文件的屬性和標(biāo)志來創(chuàng)建文件。另外,如果是打開一個(gè)現(xiàn)有文件,則該參數(shù)被忽略。
以下為打開COM1串口的示例代碼:
HANDLE hCom; DWORD dwError; hCom = CreateFile("COM1",//對(duì)串口1進(jìn)行操作GENERIC_READ|GENERIC_WRITE,//允許讀和寫0,//獨(dú)占方式NULL,//默認(rèn)安全屬性O(shè)PEN_EXISTING,//串口必須存在FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//重疊方式NULL); if(hCom == INVALID_HANDLE_VALUE) {dwError = GetLastError();...... }......CloseHandle(hCom);2、串口配置和屬性
串口打開后就可以設(shè)置接收緩沖區(qū)和發(fā)送緩沖區(qū),這可以通過SetupComm()函數(shù)實(shí)現(xiàn),如果通信的速率較高,則應(yīng)該設(shè)置較大的緩沖區(qū):
??? BOOL WINAPI SetupComm(
??? __in HANDLE hFile,//串口句柄
??? __in DWORD dwInQueue,//輸入緩沖區(qū)大小
??? __in DWORD dwOutQueue//輸出緩沖區(qū)大小
??? );
可以通過函數(shù)GetCommState()和SetCommState()來獲得和設(shè)置串口的配置:
BOOL WINAPI GetCommState(
??? __in? HANDLE hFile,//串口句柄
??? __out LPDCB lpDCB//保存串口配置信息
??? );
BOOL WINAPI SetCommState(
??? __in HANDLE hFile,//串口句柄
??? __in LPDCB lpDCB//設(shè)置串口配置信息
??? );
DCB結(jié)構(gòu):
????????????????
DCB結(jié)構(gòu)中常用成員:
?????????????????????????????????????????????????? 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)檢查?
?????????????????????????????????????????????????? DWORD Parity:校驗(yàn)方式,值0~4分別對(duì)應(yīng)無校驗(yàn)、奇校驗(yàn)、偶校驗(yàn)、校驗(yàn)?
????????????????????????????????????????????????????????????????????????????????? 置位(標(biāo)記校驗(yàn))、校驗(yàn)清零?
?????????????????????????????????????????????????? DWORD ByteSize:一個(gè)字節(jié)的數(shù)據(jù)位個(gè)數(shù),范圍是5~8?
?????????????????????????????????????????????????? DWORD StopBits:停止位個(gè)數(shù),0~2分別對(duì)應(yīng)1位停止位、1.5位停止位、2位停止位
操作舉例:
DCB ComDCB; GetCommState(hComm,&ComDCB);//取得當(dāng)前串口狀態(tài) ComDCB.BaudRate=9600;//更改為9600bps,該值即為你要修改后的波特率 SetCommState(hComm,&ComDCB;//將更改后的參數(shù)寫入串口GetCommProperties()可以獲得串口的屬性:
BOOL WINAPI GetCommProperties(
??? __in? HANDLE hFile,//串口句柄
??? __out LPCOMMPROP lpCommProp//保存串口屬性
??? );
CommConfigDialog()用來對(duì)通信設(shè)備進(jìn)行配置,從而改變數(shù)據(jù)傳輸速率、數(shù)據(jù)位、奇偶校方法、停止位和流控制方法,當(dāng)函數(shù)返回時(shí),選定的設(shè)置在COMMCONFIG結(jié)構(gòu)的DCB參數(shù)中返回:
??? BOOL WINAPI CommConfigDialog(
??? __in???? LPCWSTR lpszName,//端口名
??? __in_opt HWND hWnd,//擁有對(duì)話框的窗口句柄
??? __inout? LPCOMMCONFIG lpCC//指向一個(gè)COMMCONFIG結(jié)構(gòu)
??? );
對(duì)于已經(jīng)打開的串口,對(duì)端口設(shè)置進(jìn)行更改應(yīng)通過SetCommState()來進(jìn)行。
3、讀寫串口
一般在程序中使用WriteFile()向串口中寫數(shù)據(jù),調(diào)用ReadFile()從串口讀數(shù)據(jù)。
BOOL WINAPI WriteFile(_In_?????????HANDLE hFile,//文件句柄_In_?????????LPCVOID lpBuffer,//指向一個(gè)緩沖區(qū),包含要寫入的數(shù)據(jù)_In_?????????DWORD nNumberOfBytesToWrite,//要寫入數(shù)據(jù)的字節(jié)數(shù)_Out_opt_????LPDWORD lpNumberOfBytesWritten,//實(shí)際寫入的字節(jié)數(shù)_Inout_opt_??LPOVERLAPPED lpOverlapped//指向一個(gè)OVERLAPPEN結(jié)構(gòu)體 );如果想要異步讀寫操作,則lpOverlappen參數(shù)不能為NULL,且在CreateFile()打開文件時(shí)應(yīng)指定FILE_FLAG_OVERLAPPEN標(biāo)記。
需要注意的是,當(dāng)ReadFile和WriteFile返回FALSE時(shí),不一定就是操作失敗,線程應(yīng)該調(diào)用GetLastError函數(shù)分析返回的結(jié)果。例如,在重疊操作時(shí)如果操作還未完成函數(shù)就返回,那么函數(shù)就返回FALSE,而且GetLastError函數(shù)返回ERROR_IO_PENDING。這說明重疊操作還未完成。
WriteFileEx()與ReadFileEx()只能用于異步讀寫操作,且可以設(shè)置一個(gè)讀寫完成后自動(dòng)調(diào)用的回調(diào)函數(shù)。
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,//指向一個(gè)緩沖區(qū),保存讀取的數(shù)據(jù)_In_?????????DWORD nNumberOfBytesToRead,//要讀取數(shù)據(jù)的字節(jié)數(shù)_Out_opt_????LPDWORD lpNumberOfBytesRead,//實(shí)際讀取的字節(jié)數(shù)_Inout_opt_??LPOVERLAPPED lpOverlapped//指向一個(gè)OVERLAPPED結(jié)構(gòu) ); 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、超時(shí)處理
以下轉(zhuǎn)自:http://blog.csdn.net/augusdi/article/details/10220911
在用ReadFile和WriteFile讀寫串行口時(shí),需要考慮超時(shí)問題,如果在指定的時(shí)間內(nèi)沒有讀出或?qū)懭胫付〝?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í)。
BOOL GetCommTimeouts(_In_ HANDLE hFile,_Out_ LPCOMMTIMEOUTS lpCommTimeouts ); BOOL SetCommTimeouts(_In_ HANDLE hFile,_In_ LPCOMMTIMEOUTS lpCommTimeouts );有兩種超時(shí):間隔超時(shí)和總超時(shí)。間隔超時(shí)是指在接收時(shí)兩個(gè)字符之間的最大時(shí)延,總超時(shí)是指讀寫操作總共花費(fèi)的最大時(shí)間。寫操作只支持總超時(shí),而讀操作兩種超時(shí)均支持。
用COMMTIMEOUTS結(jié)構(gòu)可以規(guī)定讀/寫操作的超時(shí),該結(jié)構(gòu)的定義為:
typedef struct _COMMTIMEOUTS{ DWORD ReadIntervalTimeout; // 讀間隔超時(shí):接收時(shí),兩字符間最大的時(shí)延。 DWORD ReadTotalTimeoutMultiplier; // 讀時(shí)間系數(shù):讀取每字節(jié)的超時(shí)。 DWORD ReadTotalTimeoutConstant; // 讀時(shí)間常量:讀串口數(shù)據(jù)的固定超時(shí)。 //讀總超時(shí) = ReadTotalTimeoutMultiplier*字節(jié)數(shù) + ReadTotalTimeoutConstant DWORD WriteTotalTimeoutMultiplier;// 寫時(shí)間系數(shù):寫每字節(jié)的超時(shí)。 DWORD WriteTotalTimeoutConstant; // 寫時(shí)間常量:寫串口數(shù)據(jù)的固定超時(shí)。 //寫總超時(shí) = WriteTotalTimeoutMultiplier*字節(jié)數(shù) + WriteTotalTimeoutConstant } COMMTIMEOUTS,*LPCOMMTIMEOUTS; 如果ReadIntervalTimeout為MAXDWORD,???并且ReadTotalTimeoutConstant和ReadTotalTimeoutMultiplier都為0,???則指定讀操作攜帶已經(jīng)收到的字符立即返回,即使沒有收到任何字符;如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都為0,則在讀操作時(shí)忽略總超時(shí)數(shù);如果WriteTotalTimeoutMultiplier和WriteTotalTimeoutConstant都為0,則在寫操作時(shí)忽略總超時(shí)數(shù)。COMMTIMEOUTS結(jié)構(gòu)的成員都以毫秒為單位,用戶設(shè)置通訊超時(shí)后,如沒有出錯(cuò),串口已經(jīng)被打開。
可以看出,間隔超時(shí)和總超時(shí)的設(shè)置是不相關(guān)的,這可以方便通信程序靈活地設(shè)置各種超時(shí)。如果所有寫超時(shí)參數(shù)均為0,那么就不使用寫超時(shí)。如果ReadIntervalTimeout為0,那么就不使用讀間隔超時(shí),如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都為0,則不使用讀總超時(shí)。如果讀間隔超時(shí)被設(shè)置成MAXDWORD并且兩個(gè)讀總超時(shí)為0,那么在讀一次輸入緩沖區(qū)中的內(nèi)容后讀操作就立即完成,而不管是否讀入了要求的字符。?在用重疊方式讀寫串行口時(shí),雖然ReadFile和WriteFile在完成操作以前就可能返回,但超時(shí)仍然是起作用的。在這種情況下,超時(shí)規(guī)定的是操作的完成時(shí)間,而不是ReadFile和WriteFile的返回時(shí)間。
5、通信狀態(tài)和通信錯(cuò)誤
如果在串口通信中發(fā)生錯(cuò)誤,如終端錯(cuò)誤、奇偶錯(cuò)誤等,I/O操作將會(huì)終止。如果程序要進(jìn)一步執(zhí)行I/O操作,必須調(diào)用ClearCommError()函數(shù)。ClearCommError()可以清除(獲得)通信錯(cuò)誤和獲得串口的當(dāng)前通信狀態(tài)。
BOOL WINAPI ClearCommError(_In_???????HANDLE hFile,//串口句柄_Out_opt_??LPDWORD lpErrors,//錯(cuò)誤碼_Out_opt_??LPCOMSTAT lpStat//通訊狀態(tài) ); lpErrors錯(cuò)誤碼解釋如下:?1-CE_BREAK:檢測(cè)到中斷信號(hào)。意思是說檢測(cè)到某個(gè)字節(jié)數(shù)據(jù)缺少合法的停止位。?
2-CE_FRAME:硬件檢測(cè)到幀錯(cuò)誤。?
3-CE_IOE:通信設(shè)備發(fā)生輸入/輸出錯(cuò)誤。?
4-CE_MODE:設(shè)置模式錯(cuò)誤,或是hFile值錯(cuò)誤。?
5-CE_OVERRUN:溢出錯(cuò)誤,緩沖區(qū)容量不足,數(shù)據(jù)將丟失。?
6-CE_RXOVER:溢出錯(cuò)誤。?
7-CE_RXPARITY:硬件檢查到校驗(yàn)位錯(cuò)誤。?
8-CE_TXFULL:發(fā)送緩沖區(qū)已滿。
lpStat通訊狀態(tài)結(jié)構(gòu)體如下:
typedef struct _COMSTAT{ ... ... DWORD cbInQue; //輸入緩沖區(qū)中的字節(jié)數(shù) DWORD cbOutQue;//輸出緩沖區(qū)中的字節(jié)數(shù) }COMSTAT,*LPCOMSTAT; 該結(jié)構(gòu)中對(duì)我們很重要的只有上面兩個(gè)參數(shù),其他的我們可以不用管。舉例:
unsigned char ucRxBuff[20]; COMSTAT ComStat; DWORD dwError=0; DWORD BytesRead=0; OVERLAPPED ov_Read; ov_Read.hEvent=CreateEvent(NULL, true, false, NULL);//必須創(chuàng)建有效事件 ClearCommError(hComm,&dwError,&ComStat);//檢查串口接收緩沖區(qū)中的數(shù)據(jù)個(gè)數(shù) bResult=ReadFile(hComm,ucRxBuff, ComStat.cbInQue, &BytesRead, &ov_Read); //假如當(dāng)前串口中有5個(gè)字節(jié)數(shù)據(jù)的話,那么執(zhí)行完ClearCommError()函數(shù)后,ComStat \ 結(jié)構(gòu)中的ComStat.cbInQue將被填充為5,此值在ReadFile函數(shù)中可被直接利用。 6、其它串口通信常用API以下轉(zhuǎn)自:http://blog.csdn.net/vodomine/article/details/6542089
PurgeComm()
用途:清空串口緩沖區(qū),在讀寫串口之前,還要用PurgeComm()函數(shù)清空緩沖區(qū)。
原型:BOOL PurgeComm(HANDLE hFile,? DWORD dwFlags );?
參數(shù)說明:?
?? -hFile:串口句柄?
?? -dwFlags:指定串口執(zhí)行的動(dòng)作,由以下參數(shù)組成:?
?? -PURGE_TXABORT:停止目前所有的傳輸工作立即返回不管是否完成傳輸動(dòng)作。?
?? -PURGE_RXABORT:停止目前所有的讀取工作立即返回不管是否完成讀取動(dòng)作。?
?? -PURGE_TXCLEAR:清除發(fā)送緩沖區(qū)的所有數(shù)據(jù)。?
?? -PURGE_RXCLEAR:清除接收緩沖區(qū)的所有數(shù)據(jù)。?
操作舉例:
SetCommMask()
用途:設(shè)置串口通信事件。?
原型:BOOL SetCommMask(HANDLE hFile,? DWORD dwEvtMask);?
參數(shù)說明:?
?? -hFile:串口句柄?
?? -dwEvtMask:準(zhǔn)備監(jiān)視的串口事件掩碼?
注:在用api函數(shù)撰寫串口通信函數(shù)時(shí)大體上有兩種方法,一種是查尋法,另外一種是事件通知法。 這兩種方法的區(qū)別在于收串口數(shù)據(jù)時(shí),前一種方法是主動(dòng)的周期性的查詢串口中當(dāng)前有沒有 數(shù)據(jù);后一種方法是事先設(shè)置好需要監(jiān)視的串口通信事件,然后依靠單獨(dú)開設(shè)的輔助線程進(jìn)行 監(jiān)視該事件是否已發(fā)生,如果沒有發(fā)生的話該線程就一直不停的等待直到該事件發(fā)生后,將該串口事件以消息的方式通知主窗體,然后主窗體收到該消息后依據(jù)不同的事件性質(zhì)進(jìn)行處理。 比如說當(dāng)主窗體收到監(jiān)視線程發(fā)來的RX_CHAR(串口中有數(shù)據(jù))的消息后,就可以用ReadFile() 函數(shù)去讀串口。
dwEvtMask參數(shù)有如下信息掩碼位值:?
EV_BREAK:收到BREAK信號(hào)?
EV_CTS:CTS(dear to send)線路發(fā)生變化?
EV_DSR:DST(Data Set Ready)線路發(fā)生變化?
EV_ERR:線路狀態(tài)錯(cuò)誤,包括了CE_FRAME/CE_OVERRUN/CE_RXPARITY 3鐘錯(cuò)誤。?
EV_RING:檢測(cè)到振鈴信號(hào)。?
EV_RLSD:CD(Carrier Detect)線路信號(hào)發(fā)生變化。?
EV_RXCHAR:輸入緩沖區(qū)中已收到數(shù)據(jù)。?
EV_RXFLAG:使用SetCommState()函數(shù)設(shè)置的DCB結(jié)構(gòu)中的等待字符已被傳入輸入緩沖區(qū)中。?
EV_TXEMPTY:輸出緩沖區(qū)中的數(shù)據(jù)已被完全送出。?
操作舉例:
用途:用來判斷用SetCommMask()函數(shù)設(shè)置的串口通信事件是否已發(fā)生。?
原型:
?? -hFile:串口句柄?
?? -lpEvtMask:函數(shù)執(zhí)行完后如果檢測(cè)到串口通信事件的話就將其寫入該參數(shù)中。?
?? -lpOverlapped:指向重疊結(jié)構(gòu),如果串口打開時(shí)指定了FILE_FLAG_OVERLAPPED標(biāo)志 ,則改參數(shù)不能為NULL,且重疊結(jié)構(gòu)中 應(yīng)該包含一個(gè)手工重置對(duì)象句柄(通過CreateEvent()創(chuàng)建)。
操作舉例: 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)) { //如果異步操作不能立即完成的話,函數(shù)返回FALSE,并且調(diào)用GetLastError()函 //數(shù)分析錯(cuò)誤原因后返回ERROR_IO_PENDING,指示異步操作正在后臺(tái)進(jìn)行.這種情 //況下,在函數(shù)返回之前系統(tǒng)設(shè)置OVERLAPPED結(jié)構(gòu)中的事件為無信號(hào)狀態(tài),該函數(shù) //等待用SetCommMask()函數(shù)設(shè)置的串口事件發(fā)生,共有9種事件可被監(jiān)視: //EV_BREAK,EV_CTS,EV_DSR,EV_ERR,EV_RING,EV_RLSD,EV_RXCHAR, //EV_RXFLAG,EV_TXEMPTY;當(dāng)其中一個(gè)事件發(fā)生或錯(cuò)誤發(fā)生時(shí),函數(shù)將 //OVERLAPPED結(jié)構(gòu)中的事件置為有信號(hào)狀態(tài),并將事件掩碼填充到dwMask參數(shù)中 if(GetLastError()==ERROR_IO_PENDING){ // /*在此等待異步操作結(jié)果,直到異步操作結(jié)束時(shí)才返回.實(shí)際上此時(shí) */ /*WaitCommEvent()函數(shù)一直在等待串口監(jiān)控的事件之一發(fā)生,當(dāng)事件發(fā)*/ /*生時(shí)該函數(shù)將OVERLAPPED結(jié)構(gòu)中的事件句柄置為有信號(hào)狀態(tài),此時(shí) */ /*GetOverlappedResult()函數(shù)發(fā)現(xiàn)此事件有信號(hào)后馬上返回,然后下面*/ /*的程序馬上分析WaitCommEvent()函數(shù)等到的事件是被監(jiān)視的串口事 */ /*件中的哪一個(gè),然后執(zhí)行相應(yīng)的動(dòng)作并發(fā)出相應(yīng)消息. */ // 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()函數(shù)設(shè)置的被監(jiān)視的串口通信事件。 ... ... break; default:break; } } } GetOverlappedResult()可以判斷一個(gè)重疊操作當(dāng)前的狀態(tài),用來判斷異步操作是否完成。 BOOL WINAPI GetOverlappedResult(_In_???HANDLE hFile,//文件句柄_In_???LPOVERLAPPED lpOverlapped,//指向欲檢查的重疊結(jié)構(gòu)_Out_??LPDWORD lpNumberOfBytesTransferred,//讀或?qū)懖僮鞯淖止?jié)數(shù)_In_???BOOL bWait ); 如果參數(shù)bWait為TRUE則函數(shù)會(huì)一直等待直到重疊機(jī)構(gòu)中的hEvent變成有信號(hào);FALSE為如果檢測(cè)到pending狀態(tài)則立即返回,此時(shí)函數(shù)返回FALSE,GetLastError()返回值為ERROR_IO_INCOMPLETE。
MSDN上關(guān)于WaitCommEvent的說明及例子:
WaitCommEvent()用來檢測(cè)指定通信設(shè)備上一組事件的發(fā)生,可以通過SetCommMask()函數(shù)來設(shè)置通信設(shè)備上的事件掩碼,GetCommMask()函數(shù)獲得通信設(shè)備上的事件掩碼。
如果重疊操作不能立即完成,則WaitCommEvent()返回FALSE,GetLastError()會(huì)返回ERROR_IO_PENDING,表示操作正在后臺(tái)進(jìn)行。在WaitCommEvent返回之前,重疊結(jié)構(gòu)中的hEvent成員會(huì)被設(shè)置為無信號(hào)狀態(tài),如果當(dāng)事件發(fā)生或錯(cuò)誤發(fā)生時(shí),其被設(shè)置為有信號(hào)狀態(tài),應(yīng)用程序可以調(diào)用wait functions(WaitForSingleObject、WaitForSingleObjectEx等)來判斷事件對(duì)象的狀態(tài),然后調(diào)用GetOverlappedResult()來判斷WaitCommEvent()操作的結(jié)果,GetOverlappedResult會(huì)報(bào)告操作成功或失敗,而參數(shù)lpEvtMask會(huì)保存具體發(fā)生的事件。
#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流程
????? 無論那種操作方式,一般都通過四個(gè)步驟來完成:??????
???????? (1) 打開串口
???????? (2) 設(shè)置和配置串口
???????? (3) 讀寫串口
???????? (4) 關(guān)閉串口
??????? 打開串口:
HANDLE hCom; DWORD dwError; hCom = CreateFile("COM1",//對(duì)串口1進(jìn)行操作GENERIC_READ|GENERIC_WRITE,//允許讀和寫0,//獨(dú)占方式NULL,//默認(rèn)安全屬性O(shè)PEN_EXISTING,//串口必須存在0,//同步方式,重疊方式:FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPEDNULL); if(hCom == INVALID_HANDLE_VALUE) {AfxMessageBox(L"打開串口失敗");return FALSE; }??????? 設(shè)置和配置串口:
<span style=";">//設(shè)置緩沖區(qū)大小 SetupComm(hCom,1024,1024); //輸入緩沖區(qū)和輸出緩沖區(qū)的大小都是1024 //設(shè)置超時(shí) COMMTIMEOUTS TimeOuts; TimeOuts.ReadIntervalTimeout=1000; //設(shè)定讀超時(shí) TimeOuts.ReadTotalTimeoutMultiplier=500; TimeOuts.ReadTotalTimeoutConstant=5000; TimeOuts.WriteTotalTimeoutMultiplier=500; //設(shè)定寫超時(shí) TimeOuts.WriteTotalTimeoutConstant=5000; 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);//清空緩沖區(qū) PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);//清空接收和發(fā)送緩沖區(qū) </span>???????????? 讀寫串口(異步):
//重疊I/O非常靈活,它也可以實(shí)現(xiàn)阻塞(例如我們可以設(shè)置一定要讀取到一個(gè)數(shù)據(jù)才能進(jìn)行到下一步操作)。有兩種方法可以等待操作完成:一種方法是用 //像WaitForSingleObject這樣的等待函數(shù)來等待OVERLAPPED結(jié)構(gòu)的hEvent成員;另一種方法是調(diào)用GetOverlappedResult函數(shù)等待,后面將演示說明。/*使用WaitForSingleObject函數(shù)來等待OVERLAPPED結(jié)構(gòu)的hEvent成員*///在使用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)。 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);//清除錯(cuò)誤,獲得輸入緩沖區(qū)中字節(jié)數(shù) 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);//情況緩沖區(qū) return dwBytesRead; } return 0; } PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); return dwBytesRead; /使用GetOverlappedResult()函數(shù)*///函數(shù)返回重疊操作的結(jié)果,用來判斷異步操作是否完成,它是通過判斷OVERLAPPED結(jié)構(gòu)中的 //hEvent是否被置位來實(shí)現(xiàn)的。 char lpInBuffer[1024]; DWORD dwBytesRead=1024; BOOL bReadStatus; DWORD dwErrorFlags; COMSTAT ComStat; OVERLAPPED m_osRead;ClearCommError(hCom,&dwErrorFlags,&ComStat);//清除錯(cuò)誤,獲得輸入緩沖區(qū)中字節(jié)數(shù) 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;??????? 關(guān)閉串口:CloseHandle(hComm);
總結(jié)
以上是生活随笔為你收集整理的Win32 串口编程笔记1的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 认识 UART 接口
- 下一篇: c语言交通违章编程代码,C语言程序设计之