图一、串口通信示例程序 |
//串行设备句柄; HANDLE hComDev=0; //串口打开标志; BOOL bOpen=FALSE; //线程同步事件句柄; HANDLE hEvent=0; DCB dcb; COMMTIMEOUTS timeouts; //设备已打开 if(bOpen) return FALSE; //打开COM1 if((hComDev=CreateFile(“COM1”,GENERICREAD|GENERICWRITE,0,NULL,OPENEXISTING,FILEATTRIBUTENORMAL,NULL))==INVALIDHANDLEVALUE) return FALSE; //设置超时控制 SetCommTimeouts(hComDev,&timeouts); //设置接收缓冲区和输出缓冲区的大小 SetupComm(hComDev,1024,512); //获取缺省的DCB结构的值 GetCommState(hComDev,&dcb); //设定波特率为9600 bps dcb.BaudRate=CBR9600; //设定无奇偶校验 dcb.fParity=NOPARITY; //设定数据位为8 dcb.ByteSize=8; //设定一个停止位 dcb.StopBits=ONESTOPBIT; //监视串口的错误和接收到字符两种事件 SetCommMask(hComDev,EVERR|EVRXCHAR); //设置串行设备控制参数 SetCommState(hComDev,&dcb); //设备已打开 bOpen=TRUE; //创建人工重设、未发信号的事件 hEvent=CreateEvent(NULL,FALSE,FALSE, “WatchEvent”); //创建一个事件监视线程来监视串口事件 AfxBeginThread(CommWatchProc,pParam); } |
BOOL WriteComm(LPCVOID lpSndBuffer,DWORD dwBytesToWrite) { //lpSndBuffer为发送数据缓冲区指针, dwBytesToWrite为将要发送的字节长度 //设备已打开 BOOL bWriteState; //实际发送的字节数 DWORD dwBytesWritten; //设备未打开 if(!bOpen) return FALSE; bWriteState=WriteFile(hComDev,lpSndBuffer,dwBytesToWrite,&dwBytesWritten,NULL); if(!bWriteState || dwBytesToWrite!=dwBytesWritten) //发送失败 return FALSE; else //发送成功 return TRUE; } |
DWORD ReadComm(LPVOID lpInBuffer,DWORD dwBytesToRead) { //lpInBuffer为接收数据的缓冲区指针, dwBytesToRead为准备读取的数据长度(字节数) //串行设备状态结构 COMSTAT ComStat; DWORD dwBytesRead,dwErrorFlags; //设备未打开 if(!bOpen) return 0; //读取串行设备的当前状态 ClearCommError(hComDev,&dwErrorFlags,&ComStat); //应该读取的数据长度 dwBytesRead=min(dwBytesToRead,ComStat.cbInQue); if(dwBytesRead>0) //读取数据 if(!ReadFile(hComDev,lpInBuffer,dwBytesRead,&dwBytesRead,NULL)) dwBytesRead=0; return dwBytesRead; } |
UINT CommWatchProc(LPVOID pParam) { DWORD dwEventMask=0; //发生的事件; while(bOpen) { //等待监视的事件发生 WaitCommEvent(hComDev, &dwEventMask,NULL); if ((dwEventMask & EVRXCHAR)==EVRXCHAR) ……//接收到字符事件后,可以将此消息登记到由pParam有指定的窗口类中进行处理 if(dwEventMask & EVERR)==EVERROR) ……//发生错误时的处理 } SetEvent(hEvent); //发信号,指示监视线程结束 return 0; } |
void CloseSynComm() { if(!bOpen) return; //结束事件监视线程 bOpen=FALSE; SetCommMask(hComDev,0); //取消事件监视,此时监视线程中的WaitCommEvent将返回 WaitForSingleObject(hEvent,INFINITE); //等待监视线程结束 CloseHandle(hEvent); //关闭事件句柄 //停止发送和接收数据,并清除发送和接收缓冲区 PurgeComm(hComDev,PURGETXABORT| PURGERXABORT|PURGETXCLEAR|PURGERXCLEAR); //关闭设备句柄 CloseHandle(hComDev); } |
二、编程步骤
1、 启动Visual C++6.0,生成一个基于对话框的的应用程序,将该程序命名为“SerealCom”;
2、 按照图一的界面设计对话框,具体设置参见代码部分;
3、 使用Class Wizard为对话框的按钮添加鼠标单击消息响应函数;
4、 添加代码,编译运行程序。
三、程序代码
////////////////////////////////////////////////////////////// #if !defined(_COMM_ACCESS_FUNCTIONS_AND_DATA) #define _COMM_ACCESS_FUNCTIONS_AND_DATA #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #define EVENTCHAR 0x0d #define MAXBLOCKLENGTH 59 extern BYTE XwCom; extern BYTE sCom1[5],sCom2[MAXBLOCKLENGTH+12]; extern sCom3[MAXBLOCKLENGTH+12]; extern BYTE opation; extern short ComNum; #define FC_DTRDSR 0x01 #define FC_RTSCTS 0x02 #define FC_XONXOFF 0x04 #define ASCII_BEL 0x07 #define ASCII_BS 0x08 #define ASCII_LF 0x0A #define ASCII_CR 0x0D #define ASCII_XON 0x11 #define ASCII_XOFF 0x13 class CComStatus { public: HANDLE m_hCom; BYTE m_bComId; BYTE m_bByteSize; BYTE m_bStopBits; BYTE m_bParity; DWORD m_dwBaudRate; //WORD m_fChEvt; char m_bEvtChar; DWORD m_fBinary; BOOL m_bConnected; BOOL m_fXonXoff; BOOL m_bFlowCtrl; OVERLAPPED m_rdos; OVERLAPPED m_wtos; //functions CComStatus(); CComStatus(BYTE bComId,BYTE bByteSize,BYTE bStopBits,BYTE bParity, DWORD dwBaudRate,/*WORD fChEvt,*/char bEvtChar,DWORD fBinary); BOOL OpenConnection(); BOOL CloseConnection(); BOOL SetupConnection(); BOOL IsConnected(); }; UINT CommWatchProc( LPVOID lpData ); BOOL WriteCommBlock( CComStatus& comDev, LPSTR lpByte , DWORD dwBytesToWrite); int ReadCommBlock(CComStatus& comDev,LPSTR lpszBlock, int nMaxLength ); int ReadCommBlockEx(CComStatus& comDev,LPSTR lpszBlock, int nMaxLength,DWORD dwTimeOut); #endif /////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "com232.h" BYTE XwCom=0x40; BYTE sCom1[5],sCom2[MAXBLOCKLENGTH+12],sCom3[MAXBLOCKLENGTH+12]; BYTE opation; short ComNum; CComStatus::CComStatus() { m_hCom = NULL; m_bComId = (char)ComNum;//COM1 m_bByteSize=8; m_bStopBits=ONESTOPBIT; m_bParity=NOPARITY; m_dwBaudRate=9600; m_bEvtChar=EVENTCHAR; m_fBinary=1; m_bConnected = FALSE; m_bFlowCtrl = FC_XONXOFF ; m_fXonXoff = FALSE; } CComStatus::CComStatus(BYTE bComId,BYTE bByteSize,BYTE bStopBits,BYTE bParity,DWORD dwBaudRate,/*WORD fChEvt,*/char bEvtChar,DWORD fBinary) { m_hCom = NULL; m_bComId = bComId; m_bByteSize=bByteSize; m_bStopBits=bStopBits; m_bParity=bParity; m_dwBaudRate=dwBaudRate; m_bEvtChar=bEvtChar; m_fBinary=fBinary; m_bConnected = FALSE; m_bFlowCtrl = FC_XONXOFF ; m_fXonXoff = FALSE; } BOOL CComStatus::OpenConnection() { char csCom[10]; COMMTIMEOUTS CommTimeOuts ; if((m_bComId < 0) || (m_bComId > 4)) return FALSE;//从COM1到COM4 if(m_hCom)//if already open return FALSE; //OVERLAPPED包含异步I/O信息 m_rdos.Offset = 0; m_rdos.OffsetHigh = 0; m_rdos.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); if(m_rdos.hEvent == NULL) return FALSE; m_wtos.Offset = 0; m_wtos.OffsetHigh = 0; m_wtos.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); if(m_wtos.hEvent == NULL) { CloseHandle(m_rdos.hEvent); return FALSE; } wsprintf(csCom,"COM%d",m_bComId); m_hCom = CreateFile(csCom,GENERIC_READ | GENERIC_WRITE, 0,NULL, OPEN_EXISTING,ILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,NULL); if(m_hCom == INVALID_HANDLE_VALUE) { //dwError = GetLastError(); // handle error return FALSE; } else { SetCommMask( m_hCom, EV_RXCHAR ) ; // get any early notifications SetupComm( m_hCom, 4096, 4096 ) ; // setup device buffers // purge any information in the buffer PurgeComm( m_hCom, PURGE_TXABORT | PURGE_RXABORT |PURGE_TXCLEAR | PURGE_RXCLEAR ) ; // set up for overlapped I/O DWORD dwTemp = 1000 / (this->m_dwBaudRate / 8); CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF ; CommTimeOuts.ReadTotalTimeoutMultiplier = 0;//((dwTemp > 0) ? dwTemp : 1); CommTimeOuts.ReadTotalTimeoutConstant = 1000 ; // CBR_9600 is approximately 1byte/ms. For our purposes, allow // double the expected time per character for a fudge factor. CommTimeOuts.WriteTotalTimeoutMultiplier =2*CBR_9600/this->m_dwBaudRate;//( npTTYInfo ) ; CommTimeOuts.WriteTotalTimeoutConstant = 0;//1000 ; SetCommTimeouts( m_hCom, &CommTimeOuts ) ; } if(!SetupConnection()) { CloseConnection(); return FALSE; } EscapeCommFunction( m_hCom, SETDTR ); m_bConnected = TRUE; return TRUE; } BOOL CComStatus::CloseConnection() { if (NULL == m_hCom) return ( TRUE ) ; // set connected flag to FALSE m_bConnected = FALSE; // disable event notification and wait for thread // to halt SetCommMask( m_hCom, 0 ) ; EscapeCommFunction( m_hCom, CLRDTR ) ; // purge any outstanding reads/writes and close device handle PurgeComm( m_hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ; CloseHandle( m_hCom ) ; m_hCom = NULL; // change the selectable items in the menu CloseHandle(m_rdos.hEvent); CloseHandle(m_wtos.hEvent); return ( TRUE ) ; } BOOL CComStatus::SetupConnection() { BOOL fRetVal ; BYTE bSet ; DCB dcb ; if(m_hCom == NULL) return FALSE; dcb.DCBlength = sizeof( DCB ) ; GetCommState( m_hCom, &dcb ) ; dcb.BaudRate = this->m_dwBaudRate; dcb.ByteSize = this->m_bByteSize; dcb.Parity = this->m_bParity; dcb.StopBits = this->m_bStopBits ; dcb.EvtChar = this->m_bEvtChar ; // setup hardware flow control bSet = (BYTE) ((m_bFlowCtrl & FC_DTRDSR) != 0) ; dcb.fOutxDsrFlow = bSet ; if (bSet) dcb.fDtrControl = DTR_CONTROL_HANDSHAKE ; else dcb.fDtrControl = DTR_CONTROL_ENABLE ; bSet = (BYTE) ((m_bFlowCtrl & FC_RTSCTS) != 0) ; dcb.fOutxCtsFlow = bSet ; if (bSet) dcb.fRtsControl = RTS_CONTROL_HANDSHAKE ; else dcb.fRtsControl = RTS_CONTROL_ENABLE ; // setup software flow control bSet = (BYTE) ((m_bFlowCtrl & FC_XONXOFF) != 0) ; dcb.fInX = dcb.fOutX = bSet ; dcb.XonChar = ASCII_XON ; char xon = ASCII_XON ; dcb.XoffChar = ASCII_XOFF ; char xoff = ASCII_XOFF ; dcb.XonLim = 100 ; dcb.XoffLim = 100 ; // other various settings dcb.fBinary = TRUE ; dcb.fParity = TRUE ; fRetVal = SetCommState( m_hCom, &dcb ) ; return ( fRetVal ) ; } // end of SetupConnection() BOOL CComStatus::IsConnected() { return m_bConnected; } UINT CommWatchProc( LPVOID lpData ) { DWORD dwEvtMask ; //NPTTYINFO npTTYInfo = (NPTTYINFO) lpData ; OVERLAPPED os ; int nLength ; //BYTE abIn[ MAXBLOCK + 1] ; CComStatus * pCom = (CComStatus *)lpData; memset( &os, 0, sizeof( OVERLAPPED ) ) ; // create I/O event used for overlapped read os.hEvent = CreateEvent( NULL, // no security TRUE, // explicit reset req FALSE, // initial event reset NULL ) ; // no name if (os.hEvent == NULL) { MessageBox( NULL, "Failed to create event for thread!", "TTY Error!",MB_ICONEXCLAMATION | MB_OK ) ; return ( FALSE ) ; } if (!SetCommMask( pCom->m_hCom, EV_RXCHAR )) return ( FALSE ) ; char buf[256]; while ( pCom->m_bConnected ) { dwEvtMask = 0 ; WaitCommEvent( pCom->m_hCom, &dwEvtMask, NULL ); if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR) { if ((nLength = ReadCommBlock( *pCom, (LPSTR) buf, 255 ))) { //WriteTTYBlock( hTTYWnd, (LPSTR) abIn, nLength ) ; buf[nLength]='/0'; AfxMessageBox(buf); } } } CloseHandle( os.hEvent ) ; return( TRUE ) ; } // end of CommWatchProc() int ReadCommBlock(CComStatus& comDev,LPSTR lpszBlock, int nMaxLength ) { BOOL fReadStat ; COMSTAT ComStat ; DWORD dwErrorFlags; DWORD dwLength; DWORD dwError; char szError[ 10 ] ; // only try to read number of bytes in queue ClearCommError( comDev.m_hCom, &dwErrorFlags, &ComStat ) ; dwLength = min( (DWORD) nMaxLength, ComStat.cbInQue ) ; if (dwLength > 0) { fReadStat = ReadFile( comDev.m_hCom, lpszBlock,dwLength, &dwLength, &(comDev.m_rdos) ) ; if (!fReadStat) { if (GetLastError() == ERROR_IO_PENDING) { OutputDebugString("/n/rIO Pending"); while(!GetOverlappedResult( comDev.m_hCom ,&(comDev.m_rdos), &dwLength, TRUE )) { dwError = GetLastError(); if(dwError == ERROR_IO_INCOMPLETE) // normal result if not finished continue; else { // an error occurred, try to recover wsprintf( szError, " ClearCommError( comDev.m_hCom , &dwErrorFlags, &ComStat ) ; break; } } } else { // some other error occurred dwLength = 0 ; ClearCommError( comDev.m_hCom , &dwErrorFlags, &ComStat ) ; } } } return ( dwLength ) ; } // end of ReadCommBlock() int ReadCommBlockEx(CComStatus& comDev,LPSTR lpszBlock, int nMaxLength,DWORD dwTimeOut) { LPSTR lpOffset=lpszBlock; int nReadCount = 0; char chBuf; //time_t beginTime,endTime; if(!comDev.m_hCom) return 0; if(dwTimeOut <= 0) return 0; MSG msg; //time(&beginTime); DWORD dwLastTick,dwNowTick,dwGoneTime; dwGoneTime = 0; dwLastTick = GetTickCount(); dwNowTick = dwLastTick; // double diftime; do { if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } if(ReadCommBlock(comDev,&chBuf,1) > 0) { //TRACE("----get a char----/n"); *lpOffset = chBuf; lpOffset ++; nReadCount ++; } dwNowTick = GetTickCount(); if(dwNowTick < dwLastTick) { dwLastTick = dwNowTick; } dwGoneTime = dwNowTick - dwLastTick; //TRACE("gon time = %lu/n",dwGoneTime); }while((nReadCount < nMaxLength) && (dwGoneTime < dwTimeOut)); return (nReadCount); }//end ReadCommBlockEx BOOL WriteCommBlock( CComStatus& comDev, LPSTR lpByte , DWORD dwBytesToWrite) { BOOL fWriteStat ; DWORD dwBytesWritten ; DWORD dwErrorFlags; DWORD dwError; DWORD dwBytesSent=0; COMSTAT ComStat; char szError[ 128 ] ; fWriteStat = WriteFile( comDev.m_hCom , lpByte, dwBytesToWrite,&dwBytesWritten, &( comDev.m_wtos) ) ; if (!fWriteStat) { if(GetLastError() == ERROR_IO_PENDING) { while(!GetOverlappedResult( comDev.m_hCom,&(comDev.m_wtos), &dwBytesWritten, TRUE )) { dwError = GetLastError(); if(dwError == ERROR_IO_INCOMPLETE) { // normal result if not finished dwBytesSent += dwBytesWritten; continue; } else { // an error occurred, try to recover wsprintf( szError, " ClearCommError( comDev.m_hCom, &dwErrorFlags, &ComStat ) ; break; } } dwBytesSent += dwBytesWritten; if( dwBytesSent != dwBytesToWrite ) wsprintf(szError,"/nProbable Write Timeout: Total of %ld bytes sent", dwBytesSent); else wsprintf(szError,"/n%ld bytes written", dwBytesSent); OutputDebugString(szError); } else { // some other error occurred ClearCommError( comDev.m_hCom, &dwErrorFlags, &ComStat ) ; return ( FALSE ); } } return ( TRUE ) ; } // end of WriteCommBlock() |