VC實現串口通訊常式
WIN95介面下的VC++串口通訊程式在WIN32下是不建議對連接埠進行操作的,在WIN32中所有的裝置都被看成是檔案,串列口也不例外也是作為檔案來進行處理的。這是我的一份關於串口編程的讀書筆記,對於使 用VC進行編程的同行應該有一定的協助。
1.開啟串口: 在Window 95下串列口作為檔案處理,使用檔案操作對串列口進行處理。使用CreateFile()開啟串口,CreateFile()將返回串口的控制代碼。 HANDLE CreateFile( LPCTSTR lpFileName, // pointer to name of the file DWORD dwDesiredAccess, // access (read-write) mode DWORD dwShareMode, // share mode LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes DWORD dwCreationDistribution, // how to create DWORD dwFlagsAndAttributes, // file attributes HANDLE hTemplateFile // handle to file with attributes to copy ); lpFileName: 指明串口製備,例:COM1,COM2 dwDesiredAccess: 指明串口存取方式,例:GENERIC_READ|GENERIC_WRITE dwShareMode: 指明串口共用方式 lpSecurityAttributes: 指明串口的安全屬性結構,NULL為預設安全屬性 dwCreateionDistribution: 必須為OPEN_EXISTIN dwFlagAndAttributes: 對串口唯一有意義的是FILE_FLAG_OVERLAPPED hTemplateFile: 必須為NULL 2.關閉串口: CloseHandle(hCommDev); 3.設定緩衝區長度: BOOL SetupComm( HANDLE hFile, // handle of communications device DWORD dwInQueue, // size of input buffer DWORD dwOutQueue // size of output buffer ); 4.COMMPROP結構: 可使用GetCommProperties()取得COMMPROP結構,COMMPROP結構中記載了系統支援的各項設定。 typedef struct _COMMPROP { // cmmp WORD wPacketLength; // packet size, in bytes WORD wPacketVersion; // packet version DWORD dwServiceMask; // services implemented DWORD dwReserved1; // reserved DWORD dwMaxTxQueue; // max Tx bufsize, in bytes DWORD dwMaxRxQueue; // max Rx bufsize, in bytes DWORD dwMaxBaud; // max baud rate, in bps DWORD dwProvSubType; // specific provider type DWORD dwProvCapabilities; // capabilities supported DWORD dwSettableParams; // changeable parameters DWORD dwSettableBaud; // allowable baud rates WORD wSettableData; // allowable byte sizes WORD wSettableStopParity; // stop bits/parity allowed DWORD dwCurrentTxQueue; // Tx buffer size, in bytes DWORD dwCurrentRxQueue; // Rx buffer size, in bytes DWORD dwProvSpec1; // provider-specific data DWORD dwProvSpec2; // provider-specific data WCHAR wcProvChar[1]; // provider-specific data } COMMPROP; dwMaxBaud: BAUD_075 75 bps BAUD_110 110 bps BAUD_134_5 134.5 bps BAUD_150 150 bps BAUD_300 300 bps BAUD_600 600 bps BAUD_1200 1200 bps BAUD_1800 1800 bps BAUD_2400 2400 bps BAUD_4800 4800 bps BAUD_7200 7200 bps BAUD_9600 9600 bps BAUD_14400 14400 bps BAUD_19200 19200 bps BAUD_38400 38400 bps BAUD_56K 56K bps BAUD_57600 57600 bps BAUD_115200 115200 bps BAUD_128K 128K bps BAUD_USER Programmable baud rates available dwProvSubType: PST_FAX 傳真裝置 PST_LAT LAT協議 PST_MODEM 數據機裝置 PST_NETWORK_BRIDGE 未指定的橋接器 PST_PARALLELPORT 並口 PST_RS232 RS-232口 PST_RS422 RS-422口 PST_RS423 RS-432口 PST_RS449 RS-449口 PST_SCANNER 掃描器裝置 PST_TCPIP_TELNET TCP/IP Telnet協議 PST_UNSPECIFIED 未指定 PST_X25 X.25標準 dwProvCapabilities PCF_16BITMODE 支援特殊的16位元模式 PCF_DTRDSR 支援DTR(資料終端機就緒)/DSR(資料裝置就緒) PCF_INTTIMEOUTS 支援區間逾時 PCF_PARITY_CHECK 支援同位 PCF_RLSD 支援RLSD(接收線訊號檢測) PCF_RTSCTS 支援RTS(請求發送)/CTS(清除發送) PCF_SETXCHAR 支援可設定的XON/XOFF PCF_SPECIALCHARS 支援特殊字元 PCF_TOTALTIMEOUTS 支援總(佔用時間)逾時 PCF_XONXOFF 支援XON/XOFF流量控制 標準RS-232和WINDOW支援除PCF_16BITMODE和PCF_SPECIALCHAR外的所有功能 dwSettableParams SP_BAUD 可配置傳輸速率 SP_DATABITS 可配置資料位元個數 SP_HANDSHAKING 可配置握手(流量控制) SP_PARITY 可配置同位模式 SP_PARITY_CHECK 可配置同位允許/禁止 SP_RLSD 可配置RLSD(接收訊號檢測) SP_STOPBITS 可配置停止位個數 標準RS-232和WINDOW支援以上所有功能 wSettableData DATABITS_5 5個資料位元 DATABITS_6 6個資料位元 DATABITS_7 7個資料位元 DATABITS_8 8個資料位元 DATABITS_16 16個資料位元 DATABITS_16X 通過串列硬體線路的特殊寬度路徑 WINDOWS 95支援16的所有設定 5.DCB結構: typedef struct _DCB {// dcb DWORD DCBlength; // sizeof(DCB) DWORD BaudRate; // current baud rate 指定當前的傳輸速率 DWORD fBinary: 1; // binary mode, no EOF check 指定是否允許二進位模式, WINDOWS 95中必須為TRUE DWORD fParity: 1; // enable parity checking 指定同位是否允許 DWORD fOutxCtsFlow:1; // CTS output flow control 指定CTS是否用於檢測發送控制。 當為TRUE是CTS為OFF,發送將被掛起。 DWORD fOutxDsrFlow:1; // DSR output flow control 指定CTS是否用於檢測發送控制。 當為TRUE是CTS為OFF,發送將被掛起。 DWORD fDtrControl:2; // DTR flow control type DTR_CONTROL_DISABLE值將DTR置為OFF, DTR_CONTROL_ENABLE值將DTR置為ON, DTR_CONTROL_HANDSHAKE允許DTR"握手",DWORD fDsrSensitivity:1; // DSR sensitivity 當該值為TRUE時DSR為OFF時接收的位元組被忽略 DWORD fTXContinueOnXoff:1; // XOFF continues Tx 指定當接收緩衝區已滿,並且驅動程式已經發 送出XoffChar字元時發送是否停止。 TRUE時,在接收緩衝區接收到緩衝區已滿的位元組XoffLim且驅動程式已經發送出XoffChar字元中止接收位元組之後,發送繼續進行。 FALSE時,在接收緩衝區接收到代表緩衝區已空的位元組XonChar且驅動程式已經發送出恢複發送的XonChar之後,發送繼續進行。 DWORD fOutX: 1; // XON/XOFF out flow control TRUE時,接收到XoffChar之後便停止發送 接收到XonChar之後將重新開始 DWORD fInX: 1; // XON/XOFF in flow control TRUE時,接收緩衝區接收到代表緩衝區滿的XoffLim之後,XoffChar發送出去 接收緩衝區接收到代表緩衝區空的XonLim之後,XonChar發送出去 DWORD fErrorChar: 1; // enable error replacement 該值為TRUE且fParity為TRUE時,用ErrorChar 成員指定的字元代替同位錯誤的接收字元 DWORD fNull: 1; // enable null stripping TRUE時,接收時去掉空(0值)位元組 DWORD fRtsControl:2; // RTS flow control RTS_CONTROL_DISABLE時,RTS置為OFF RTS_CONTROL_ENABLE時, RTS置為ON RTS_CONTROL_HANDSHAKE時, 當接收緩衝區小於半滿時RTS為ON 當接收緩衝區超過四分之三滿時RTS為OFF RTS_CONTROL_TOGGLE時, 當接收緩衝區仍有剩餘位元組時RTS為ON ,否則預設為OFF DWORD fAbortOnError:1; // abort reads/writes on error TRUE時,有錯誤發生時中止讀和寫操作 DWORD fDummy2:17; // reserved 未使用 WORD wReserved; // not currently used 未使用,必須為0 WORD XonLim; // transmit XON threshold 指定在XON字元發送這前接收緩衝區中可允許的最小位元組數 WORD XoffLim; // transmit XOFF threshold 指定在XOFF字元發送這前接收緩衝區中可允許的最小位元組數 BYTE ByteSize; // number of bits/byte, 4-8 指定連接埠當前使用的資料位元 BYTE Parity; // 0-4=no,odd,even,mark,space 指定連接埠當前使用的同位方法,可能為: EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY BYTE StopBits; // 0,1,2 = 1, 1.5, 2 指定連接埠當前使用的停止位元,可能為: ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS char XonChar; // Tx and Rx XON character 指定用於發送和接收字元XON的值 char XoffChar; // Tx and Rx XOFF character 指定用於發送和接收字元XOFF值 char ErrorChar; // error replacement character 本字元用來代替接收到的同位發生錯誤時的值 char EofChar; // end of input character 當沒有使用二進位模式時,本字元可用來指示資料的結束 char EvtChar; // received event character 當接收到此字元時,會產生一個事件 WORD wReserved1; // reserved; do not use 未使用 } DCB; 6.改變連接埠設定 使用如下的兩個方法 BOOL GetCommState(hComm,&dcb); BOOL SetCommState(hComm,&dcb); 7.改變普通設定 BuildCommDCB(szSettings,&DCB); szSettings的格式:baud parity data stop 例: "baud=96 parity=n data=8 stop=1" 簡寫:"96,N,8,1" szSettings 的有效值 baud: 11 or 110 = 110 bps 15 or 150 = 150 bps 30 or 300 = 300 bps 60 or 600 = 600 bps 12 or 1200 = 1200 bps 24 or 2400 = 2400 bps 48 or 4800 = 4800 bps 96 or 9600 = 9600 bps 19 or 19200= 19200bps parity: n=none e=even o=odd m=mark s=space data: 5,6,7,8 StopBit 1,1.5,2 8.COMMCONFIG結構: typedef struct _COMM_CONFIG { DWORD dwSize; WORD wVersion; WORD wReserved; DCB dcb; DWORD dwProviderSubType; DWORD dwProviderOffset; DWORD dwProviderSize; WCHAR wcProviderData[1]; } COMMCONFIG, *LPCOMMCONFIG; 可方便的使用BOOL CommConfigDialog( LPTSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC); 來設定串列口。 9.逾時設定: 可通過COMMTIMEOUTS結構設定逾時, typedef struct _COMMTIMEOUTS { DWORD ReadIntervalTimeout; DWORD ReadTotalTimeoutMultiplier; DWORD ReadTotalTimeoutConstant; DWORD WriteTotalTimeoutMultiplier; DWORD WriteTotalTimeoutConstant; } COMMTIMEOUTS,*LPCOMMTIMEOUTS; 區間逾時:(僅對從連接埠中讀取資料有用)它指定在讀取兩個字元之間要經曆的時間 總逾時: 當讀或寫特定的位元組數需要的總時間超過某一閾值時,逾時觸發. 逾時公式: ReadTotalTimeout = (ReadTotalTimeoutMultiplier * bytes_to_read) + ReadToTaltimeoutConstant WriteTotalTimeout = (WriteTotalTimeoutMuliplier * bytes_to_write) + WritetoTotalTimeoutConstant NOTE:在設定逾時時參數0為無限等待,既無逾時 參數MAXDWORD為立即返回 逾時設定: GetCommTimeouts(hComm,&timeouts); SetCommTimeouts(hComm,&timeouts); 10.查詢方式讀寫資料 常式: COMMTIMEOUTS to; DWORD ReadThread(LPDWORD lpdwParam) { BYTE inbuff[100]; DWORD nBytesRead; if(!(cp.dwProvCapabilities&PCF_INTTIMEOUTS)) return 1L; memset(&to,0,sizeof(to)); to.ReadIntervalTimeout = MAXDWORD; SetCommTimeouts(hComm,&to); while(bReading) { if(!ReadFile(hComm,inbuff,100,&nBytesRead,NULL)) locProcessCommError(GetLastError()); else if(nBytesRead) locProcessBytes(inbuff,nBytesRead); } PurgeComm(hComm,PURGE_RXCLEAR); return 0L; } NOTE: PurgeComm()是一個清除函數,它可以中止任何未決的後台讀或寫,並且可以衝掉I/O緩衝區. BOOL PurgeComm(HANDLE hFile,DWORD dwFlags); dwFlages的有效值: PURGE_TXABORT: 中止後台寫操作 PRUGE_RXABORT: 中止後台讀操作 PRUGE_TXCLEAR: 清除發送緩衝區 PRUGE_RXCLEAR: 清除接收緩衝區 技巧: 可通過ClearCommError()來確定接收緩區中處於等待的位元組數。 BOOL ClearCommError( HANDLE hFile, // handle to communications device LPDWORD lpErrors, // pointer to variable to receive error codes LPCOMSTAT lpStat // pointer to buffer for communications status ); ClearCommError()將返回一個COMSTAT結構: 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和cbOutQue中即為緩衝區位元組。 11.同步I/O讀寫資料 COMMTIOMOUTS to; DWORD ReadThread(LPDWORD lpdwParam) { BYTE inbuff[100]; DWORD nByteRead,dwErrorMask,nToRead; COMSTAT comstat; if(!cp.dwProvCapabilities&PCF_TOTALTIMEOUTS) return 1L; memset(&to,0,sizeof(to)); to.ReadTotalTimeoutMultiplier = 5; to.ReadTotalTimeoutConstant = 50; SetCommTimeouts(hComm,&to); while(bReading) { ClearCommError(hComm,&dwErrorMask,&comstat); if(dwErrorMask) locProcessCommError(dwErrorMask); if(comstat.cbInQue >100) nToRead = 100; else nToRead = comstat.cbInQue; if(nToRead == 0) continue; if(!ReadFile(hComm,inbuff,nToRead,&nBytesRead,NULL)) locProcessCommError(GetLastError()); else if(nBytesRead) locProcessBytes(inbuff,nBytesRead); } return 0L; } 12.非同步I/O讀寫資料 當CreateFile()中的fdwAttrsAndFlags參數為FILE_FLAG_OVERLAPPEN時, 連接埠是為非同步I/O開啟的,此時可以在ReadFile的最後一個參數中指定一個OVERLAPPED結構,使資料的讀操作在後台進行。WINDOWS 95包括了非同步I/O的許多變種。 typedef struct _OVERLAPPED { DWORD Internal; DWORD InternalHigh; DWORD Offset; DWORD OffsetHigh; HANDLE hEvent; } OVERLAPPED; 對於串列口僅hEvent成員有效,其於成員必須為0。 常式: COMMTIMEOUTS to; ... DWORD ReadThread((LPDWORD lpdwParam) { BYTE inbuff[100]; DWORD nRytesRead,endtime,lrc; static OVERLAPPED o; if(!cp.dwProvCapabilities & PCF_TOTALTIMEOUTS) return 1L; memset(&to,0,sizeof(to)); to.ReadTotalTimeoutMultiplier = 5; to.ReadTotalTimeoutConstant = 1000; SetCommTimeouts(hComm,&to); o.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); while(bReading) { if(!ReadFile(hComm,inbuff,10,&nBytesRead,&o)) { nBytesRead = 0; if(lrc=GetLastError() == ERROR_IO_PENDING) { endtime = GetTickCount() + 1000; while(!GetOverlappedResult(hComm,&o,&nBytesRead,FALSE)) if(GetTickCount() > endtime) break; } if(nBytesRead) locProcessBytes(inbuff,nBytesRead); } else { if(nBytesRead) locProcessBytes(inbuff,nBytesRead); ResetEvent(o.hEvent); } } PurgeComm(hComm,PURGE_RXCLEAR); return 0L; } 這一常式是對一開始讀緩衝區就讀到所需的位元組時的處理: while(bReading) { if(!ReadFile(hComm,inbuff,10,&nBytesRead,&o)) { if((lrc=GetLastError()) ==ERROR_IO_PENDING) { if(GetOverlappedResult(hComm,&o,&nBytesRead,TRUE)) { if(nBytesRead) locProcessBytesa(inbuff,nBytesRead); } else locProcessCommError(GetLastError()); } else locProcessCommError(GetLastError)); } else if(nBytesRead) locProcessBytes(inbuff,nBytesRead); ResetEvent(o.hEvent); } 13.事件驅I/O讀寫: GetCommMask(hComm,&dwMask) Windows 95報告給應用程式的事件由此方法返回。 SetCommMasl(hComm,&dwMask) 添加或修改Windows 95所報告的事件列表。 事件掩碼如下: EV_BREAK 檢測到輸入為止 EV_CTS CTS(清除發送)訊號改變狀態 EV_DSR DSR(資料設定就緒)訊號改變狀態 EV_ERR 發生了線路狀態錯誤. 線路狀態錯誤為: CE_FRAME(幀錯誤) CE_OVERRUN(接收緩衝區超限) CE_RXPARITY(同位錯誤) EV_RING 檢測到響鈴 EV_RLSD RLSD(接收線路訊號檢測)訊號改變狀態 EV_EXCHAR 接收到一個字元,並放入輸入緩衝區 EV_RXFLAG 接收到事件字元(DCB成員的EvtChar成員),度放入輸入緩衝區 EV_TXEMPTY 輸出緩衝區中最後一個字元發送出去 在用SetCommMask指定了有用的事件後,應用程式可調用WaitCommEvent()來等待事件發生. BOOL WaitCommEvent( HANDLE hFile, // handle of communications device LPDWORD lpEvtMask, // address of variable for event that occurred LPOVERLAPPED lpOverlapped, // address of overlapped structure ); 此方法可以以同步或非同步方式操作 常式: COMMTIMEOUTS to; ... DWORD ReadTherad(LPDWORD lpdwParam) { BYTE binbuff[100]; DWORD nBytesRead,dwEvent,dwError; COMSTAT cs; SetCommMask(hComm,EV_RXHAR); while(bReading) { if(WaitCommEvent(hComm,&dwEvent,NULL)) { ClearCommError(hComm,&dwError,&cs); if((dwEvent&EV_RXCHAR)&&cs.cbInQue) { if(!ReadFile(hComm,inbuff,cs.cbInQue,&nBytesRead,NULL) locProcessCommError(GetLastError()); } else { if(nByteRead) locProcessBytes(inbuff,nBytesRead); } else locProcessCommError(GetLastError()); } PurgeComm(hComm,PURGE_RXCLEAR); return 0L; } NOTE: SetCommMask(hComm,0)可使WaitCommEvent()中止. 可使用GetCommmodemStatus()方法,常式: if(cp.dwProvCapabilities&PCF_RTSCTS) { SetCommMask(hComm,EV_CTS); WaitCommEvent(hComm,&dwMask,NULL); if(dwMask&EV_CTS) { GetCommModemStatus(hComm,&dwStatus) if(dwStatus&MS_CTS_ON) /* CTS stransition OFF-ON */ else /* CTS stransition ON-OFF */ } } MS_CTS_ON CTS為ON MS_DSR_ON DSR為ON MS_RING_ON RING為ON MS_ELSD_ON RLSD為ON 14.錯誤 當發生錯誤時應用方法ClearCommError(hComm,&dwErrorMask,&constat)得到錯誤掩碼。 CE_BREAK 中止條件 CE_FRAME 幀錯誤 CW_IOE 一般I/O錯誤,常伴有更為詳細的錯誤標誌 CE_MODE 不支援要求的模式 CE_OVERRUN 緩衝區超限下一個字元將丟失 CE_RXOVER 接收緩衝區超限 CE_RXPARITY 同位錯誤 CE_TXFULL 發送緩衝區滿 CE_DNS 沒有選擇平行裝置 CE_PTO 平行裝置發生逾時 CE_OOP 平行裝置缺紙 15.控制命令 EscapeCommFunction()可將硬體訊號置ON或OFF,類比XON或XOFF BOOL EscapeCommFunction( HANDLE hFile, // handle to communications device DWORD dwFunc // extended function to perform ); dwFunc的有效值(可用'|'同時使用多個值) CLRDTR DTR置OFF CLRRTS RTS置OFF SETDTR STR置ON SETRTS TRS置ON SETXOFF 類比XOFF字元的接收 SETXON 類比XON字元的接收 SETBREAK 在發送中產生一個中止 CLRBREAK 在發送中清除中止
作者會員名:ruan_bangqiu |
|