123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588 |
- /*-----------------------------------------
- * Copyright (c) 2008 Eric Wong
- * 本版紧供读者参考,不得用于任何商业行为
- *
- * 文件名称: CESeries.cpp
- * 文件标识:
- * 摘要:用于封装WINCE 串口通讯
- *
- * 当前版本: 1.0
- * 作者: 汪兵 Eric Wong
- * 完成日期: 2008年1月17日
- *
- * 取代版本:
- * 原作者:
- * 完成日期:
- ----------------------------------------*/
- #include "StdAfx.h"
- #include "MySeries.h"
- //构造函数
- CMyCESeries::CMyCESeries(UINT portNo, UINT baud, UINT parity, UINT databits, UINT stopbits)
- :m_portNo(portNo)
- ,m_baud(baud)
- ,m_parity(parity)
- ,m_databits(databits)
- ,m_stopbits(stopbits)
- ,m_hReadThread(NULL)
- ,m_hReadCloseEvent(NULL)
- {
- //m_portNo = portNo; //串口号使用串口1 CAN模块和GPS共用
- //m_baud = baud; //波特率
- //m_parity = parity; //奇偶校验,0-None,1-Odd,2-Even
- //m_databits = databits; //数据位
- //m_stopbits = stopbits; //停止位 0-停止位1,1-停止位1.5,2-停止位2
- //初始化内部变量
- m_hComm = INVALID_HANDLE_VALUE;
- m_OnSeriesRead = NULL;
- m_hReadCloseEvent = NULL;
- m_bOpened = false;
- bCanComRead = true;
- m_bSyncOrAsync = true;//使用同步方式或者异步方式(重叠方式),默认值为true:异步方式
- memset(&m_olWrite,0,sizeof(OVERLAPPED));
- }
- //析构函数
- CMyCESeries::~CMyCESeries()
- {
- if (m_bOpened)
- ClosePort();
- }
- //串口读线程函数
- DWORD CMyCESeries::ReadThreadFunc(LPVOID lparam)
- {
- CMyCESeries *ceSeries = (CMyCESeries*)lparam;
-
-
- BYTE * readBuf = NULL;//读取的字节
- DWORD actualReadLen=0;//实际读取的字节数
- DWORD willReadLen;
-
- DWORD dwReadErrors;
- COMSTAT cmState;
- //异步方式下将使用这些变量
- //LPOVERLAPPED
- OVERLAPPED olWait;
- //memset(&olWaite,0,sizeof(olWaite));
- memset(&olWait,0,sizeof(OVERLAPPED));
- olWait.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
-
- //同步方式,将使用该变量
- DWORD evtMask;
-
- // 清空缓冲,并检查串口是否打开。
- ASSERT(ceSeries->m_hComm !=INVALID_HANDLE_VALUE);
-
-
- //清空串口
- PurgeComm(ceSeries->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR );
-
- SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );//设置3个串口事件
-
- //可以查看串口设置了哪些事件
- //DWORD dwMask1;
- //GetCommMask(ceSeries->m_hComm,&dwMask1);
- OVERLAPPED olRead;
- memset(&olRead,0,sizeof(OVERLAPPED));
- olRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
- while(TRUE)
- {
- if(ceSeries->m_bSyncOrAsync)//异步方式下
- {
- DWORD dwCommStatus = 0;
- BOOL bWait1=0;
- bWait1= WaitCommEvent(ceSeries->m_hComm,&dwCommStatus,&olWait);//如果以上设置的3个事件中任何一个发生了,这里主要是监测接收缓冲区中有数据时
- //while(!bWait1);//等待WaitCommEvent返回为TRUE,再往下执行,这种方式行不通,重叠方式(异步方式)时,该函数一直返回为false,
- DWORD dwByte; //norains:It is only suitable for the GetOverlappedResult(),not undefined here.
- if(GetOverlappedResult(ceSeries->m_hComm,&olWait,&dwByte,TRUE) == FALSE)//正常清空下,会一直在该函数阻塞,直到olWaite中的hEvent事件发生了
- //则该函数不阻塞了,并且返回值为True
- {
- if(GetLastError() != ERROR_IO_PENDING)
- {
- return 0x30;
- }
- //Clear the error flag
- DWORD dwErrors;
- COMSTAT comStat;
- memset(&comStat,0,sizeof(comStat));
- ClearCommError(ceSeries->m_hComm,&dwErrors,&comStat);
- return 0x35;
- }
-
- //表示串口收到字符
- if (dwCommStatus & EV_RXCHAR)
- {
- ClearCommError(ceSeries->m_hComm,&dwReadErrors,&cmState);
- willReadLen = cmState.cbInQue ;
- if (willReadLen <= 0)
- {
- continue;
- }
-
- //异步方式
- //分配内存
- readBuf = new BYTE[willReadLen+1];
- ZeroMemory(readBuf,willReadLen+1);
- //读取串口数据
- //异步方式
- if(ReadFile(ceSeries->m_hComm, readBuf, willReadLen, &actualReadLen,&olRead)==FALSE)//异步操作,在读取完成以后返回TRUE
- {
- //释放内存
- delete[] readBuf;
- readBuf = NULL;
- if(GetLastError() != ERROR_IO_PENDING)
- return 0x40;
- if(GetOverlappedResult(ceSeries->m_hComm,&olRead,&actualReadLen,TRUE) == FALSE)
- return 0x45;
- if(actualReadLen == 0)
- return 0x50;
- }
- else//缓冲区中的数据读取完时
- {
- //如果读取的数据大于0,
- if (actualReadLen>0)
- {
- //触发读取回调函数
- if (ceSeries->m_OnSeriesRead)
- {
- ceSeries->m_OnSeriesRead(ceSeries->m_pOwner,readBuf,actualReadLen);
- }
- }
- //释放内存
- delete[] readBuf;
- readBuf = NULL;
- }
- }
-
- }
- else//同步方式下
- {
- if (WaitCommEvent(ceSeries->m_hComm,&evtMask,0))
- {
- SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );
- //表示串口收到字符
- if (evtMask & EV_RXCHAR)
- {
- ClearCommError(ceSeries->m_hComm,&dwReadErrors,&cmState);
- willReadLen = cmState.cbInQue ;
- if (willReadLen <= 0)
- {
- continue;
- }
-
- //分配内存
- readBuf = new BYTE[willReadLen];
- ZeroMemory(readBuf,willReadLen);
- //读取串口数据
- ReadFile(ceSeries->m_hComm, readBuf, willReadLen, &actualReadLen,0);
-
- //如果读取的数据大于0,
- if (actualReadLen>0)
- {
- //触发读取回调函数
- if (ceSeries->m_OnSeriesRead)
- {
- ceSeries->m_OnSeriesRead(ceSeries->m_pOwner,readBuf,actualReadLen);
- }
- }
- //释放内存
- delete[] readBuf;
- readBuf = NULL;
- }
- }
- }
-
-
- //每次读线程执行到该处时,都会等待(或者叫阻塞)10milseconds,看是否关闭串口事件是否发生
- //在ClosePort()函数体中调用了CloseHandle(m_hReadCloseEvent);来使得该事件有信号,我觉得可以用SetEvent(m_hReadCloseEvent)代替,该函数也是使得事件有信号
- //当执行了ClosePotr()函数后,说明该事件已经发生了,则当在读线程函数中执行到此处时,
- //将会跳出while(true),从而退出读线程,
- if (WaitForSingleObject(ceSeries->m_hReadCloseEvent, 10) == WAIT_OBJECT_0)//
- {
- TRACE("线程ReadThreadFunc退出\n");
- break;
- }
- }
- //关闭事件
- CloseHandle( olRead.hEvent );
- CloseHandle( olWait.hEvent );
- //释放内存
- if(readBuf)
- {
- delete[] readBuf;
- readBuf = NULL;
- }
- ExitThread(0);
- return 0;
- }
- //关闭读线程
- void CMyCESeries::CloseReadThread()
- {
- if( m_hComm != INVALID_HANDLE_VALUE )
- {
- // SetEvent(m_hReadCloseEvent);
- // CloseHandle(m_hReadCloseEvent);
- //WaitForSingleObject(m_hReadThread, 1000);
- // DWORD exitCode;
- // BOOL ret = GetExitCodeThread(m_hReadThread, &exitCode);
- //if(ret && exitCode != 0)
- {
- TRACE("强行关闭ReadThreadFunc线程\n");
- TerminateThread(m_hReadThread,0);
- }
- // CloseHandle(m_hReadThread);
- m_hReadThread = NULL;
- }
- /*
- if( m_hComm != INVALID_HANDLE_VALUE )
- {
- SetEvent(m_hReadCloseEvent);
- //设置所有事件无效
- SetCommMask(m_hComm, 0);
- //清空所有将要读的数据
- PurgeComm( m_hComm, PURGE_RXCLEAR );
- //等待4秒,如果读线程没有退出,则强制退出
- if (WaitForSingleObject(m_hReadThread,4000) == WAIT_TIMEOUT)
- {
- TerminateThread(m_hReadThread,0);
- }
- m_hReadThread = NULL;
- }
- //*/
- }
- //函数介绍:打开串口
- //入口参数:pPortOwner :使用此串口类的窗体句柄
- // portNo :串口号
- // baud :波特率
- // parity :奇偶校验
- // databits :数据位
- // stopbits :停止位
- //出口参数:(无)
- //返回值:TRUE:成功打开串口;FALSE:打开串口失败
- BOOL CMyCESeries::OpenPort(void * pOwner,
- UINT portNo , //串口号
- UINT baud , //波特率
- UINT parity , //奇偶校验
- UINT databits , //数据位
- UINT stopbits , //停止位
- bool isASync
- )
- {
- m_portNo = portNo; //串口号使用串口1 CAN模块和GPS共用
- m_baud = baud; //波特率
- m_parity = parity; //奇偶校验,0-None,1-Odd,2-Even
- m_databits = databits; //数据位
- m_stopbits = stopbits; //停止位 0-停止位1,1-停止位1.5,2-停止位2
- m_bSyncOrAsync = isASync;
- DCB commParam;
- TCHAR szPort[15];
- ASSERT(pOwner!=NULL);
- m_pOwner = pOwner;
-
- // 已经打开的话,直接返回
- if (m_hComm != INVALID_HANDLE_VALUE)
- {
- return TRUE;
- }
-
- //设置串口名
- //wsprintf(szPort, _T("\.\COM%d:"), portNo);
- wsprintf(szPort, _T("\\\\.\\COM%d"), portNo);
- //sprintf(szPort, "\\\\.\\COM%d", portNo);
- //打开串口
-
- if(m_bSyncOrAsync)//m_bSyncOrAsync值为0,则用同步方式创建,否则以异步方式创建、
- {
- m_hComm = CreateFile( //以异步方式创建
- szPort,
- GENERIC_READ | GENERIC_WRITE, //允许读和写
- 0, //独占方式(共享模式)
- NULL,
- OPEN_EXISTING, //打开而不是创建(创建方式)
- FILE_FLAG_OVERLAPPED,
- 0
- );
- }
- else
- {
- m_hComm = CreateFile( //以同步方式创建
- szPort,
- GENERIC_READ | GENERIC_WRITE, //允许读和写
- 0, //独占方式(共享模式)
- NULL,
- OPEN_EXISTING, //打开而不是创建(创建方式)
- 0,
- NULL
- );
- }
- if (m_hComm == INVALID_HANDLE_VALUE)
- {
- // 无效句柄,返回。
- TRACE(_T("CreateFile 返回无效句柄\n"));
- DWORD ErrNo = GetLastError();
- return FALSE;
-
- }
-
- // 得到打开串口的当前属性参数,修改后再重新设置串口。
- if (!GetCommState(m_hComm,&commParam))
- {
- //关闭串口
- CloseHandle (m_hComm);
- m_hComm = INVALID_HANDLE_VALUE;
- return FALSE;
- }
- if(m_bSyncOrAsync)//如果是异步方式,每创建一个串口对象,并且成功打开一个串口对象时后,才创建一个相应的发送事件
- {
- //异步写串口
- m_olWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,_T("WriteData"));
- // DWORD dwError = GetLastError();
- // if(dwError !=0)
- // {
- // TRACE("异步方式创建串口对象时,为该串口对象所创建的写串口事件对象失败");
- // return FALSE;
- // }
- }
-
- //设置串口参数
- commParam.BaudRate = baud; // 设置波特率
- commParam.fBinary = TRUE; // 设置二进制模式,此处必须设置TRUE
- commParam.fParity = TRUE; // 支持奇偶校验
- commParam.ByteSize = databits; // 数据位,范围:4-8
- commParam.Parity = parity; // 校验模式
- commParam.StopBits = stopbits; // 停止位
-
- commParam.fOutxCtsFlow = FALSE; // No CTS output flow control
- commParam.fOutxDsrFlow = FALSE; // No DSR output flow control
- commParam.fDtrControl = DTR_CONTROL_ENABLE;
- // DTR flow control type
- commParam.fDsrSensitivity = FALSE; // DSR sensitivity
- commParam.fTXContinueOnXoff = TRUE; // XOFF continues Tx
- commParam.fOutX = FALSE; // No XON/XOFF out flow control
- commParam.fInX = FALSE; // No XON/XOFF in flow control
- commParam.fErrorChar = FALSE; // Disable error replacement
- commParam.fNull = FALSE; // Disable null stripping
- commParam.fRtsControl = RTS_CONTROL_ENABLE;
- // RTS flow control
- commParam.fAbortOnError = FALSE; // 当串口发生错误,并不终止串口读写
-
- //设置串口参数
- if (!SetCommState(m_hComm, &commParam))
- {
- TRACE(_T("SetCommState error\n"));
- DWORD dwError = GetLastError();
- //关闭串口
- CloseHandle (m_hComm);
- m_hComm = INVALID_HANDLE_VALUE;
- return FALSE;
- }
-
- //设置串口读写时间
- COMMTIMEOUTS CommTimeOuts;
- GetCommTimeouts (m_hComm, &CommTimeOuts);
- CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
- CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
- CommTimeOuts.ReadTotalTimeoutConstant = 0;
- CommTimeOuts.WriteTotalTimeoutMultiplier = 10;
- CommTimeOuts.WriteTotalTimeoutConstant = 1000;
- if(!SetCommTimeouts( m_hComm, &CommTimeOuts ))
- {
- TRACE( _T("SetCommTimeouts 返回错误\n") );
- //关闭串口
- CloseHandle (m_hComm);
- m_hComm = INVALID_HANDLE_VALUE;
- return FALSE;
- }
-
- //指定端口监测的事件集
- SetCommMask (m_hComm, EV_RXCHAR);
- //分配串口设备缓冲区
- SetupComm(m_hComm,1024,1024);
- //SetupComm(m_hComm,512,512);
- //初始化缓冲区中的信息
- PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
-
- CString strEvent;
- strEvent.Format(_T("Com_ReadCloseEvent%d"),portNo);
- m_hReadCloseEvent = CreateEvent(NULL,TRUE,FALSE,strEvent);
- //创建串口读数据监听线程
- if( bCanComRead == true )
- {
- m_hReadThread = CreateThread(NULL,0,ReadThreadFunc,this,0,&m_dwReadThreadID);//通过this指针将串口对象传递给读线程函数ReadThreadFunc的形参
- }
-
-
- TRACE(_T("串口%d打开成功\n"), portNo);
- m_bOpened = true;
- return TRUE;
- }
- //函数介绍:关闭串口
- //入口参数:(无)
- //出口参数:(无)
- //返回值: (无)
- void CMyCESeries::ClosePort()
- {
- //表示串口还没有打开
- if (m_hComm == INVALID_HANDLE_VALUE)
- return ;
- //关闭事件
- //TRACE("关闭串口读事件\n");
- Sleep(100);
- //关闭读线程
- CloseReadThread();
- //关闭串口
- TRACE("CMyCESeries关闭串口%d句柄\n", m_portNo);
- CloseHandle (m_hComm);
-
- //关闭事件
- CloseHandle(m_olWrite.hEvent);
- m_hComm = INVALID_HANDLE_VALUE;
- m_bOpened = false;
- }
- //函数介绍:往串口写入数据
- //入口参数:buf :待写入数据缓冲区
- // bufLen : 待写入缓冲区长度
- //出口参数:(无)
- //返回值:TRUE:设置成功;FALSE:设置失败
- BOOL CMyCESeries::WriteSyncPort(const BYTE*buf , DWORD bufLen)
- {
- if( false == m_bSyncOrAsync )//同步方式
- {
- DWORD dwNumBytesWritten;
- DWORD dwHaveNumWritten =0 ; //已经写入多少
-
- int iInc = 0; //如果3次写入不成功,返回FALSE
- ASSERT(m_hComm != INVALID_HANDLE_VALUE);
- do
- {
- if (WriteFile (m_hComm, //串口句柄
- buf+dwHaveNumWritten, //被写数据缓冲区
- bufLen - dwHaveNumWritten, //被写数据缓冲区大小
- &dwNumBytesWritten, //函数执行成功后,返回实际向串口写的个数
- NULL)) //此处必须设置NULL
- {
- dwHaveNumWritten = dwHaveNumWritten + dwNumBytesWritten;
- //写入完成
- if (dwHaveNumWritten == bufLen)
- {
- break;
- }
- iInc++;
- if (iInc >= 3)
- {
- TRACE("CMyCESeries::WriteSyncPort 同步写串口失败3次\n");
- return FALSE;
- }
- Sleep(10);
- }
- else
- {
- TRACE("CMyCESeries::WriteSyncPort 同步写串口失败\n");
- return FALSE;
- }
- }while (TRUE);
-
- return TRUE;
- }
- else//异步方式
- {
- DWORD dwNumBytesWritten;
- DWORD dwHaveNumWritten =0 ; //已经写入多少
- // OVERLAPPED olWrite;
- // memset(&olWrite,0,sizeof(OVERLAPPED));
- // m_olWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,_T("WriteData"));
-
- //异步写串口
- if (WriteFile (m_hComm, //串口句柄
- buf+dwHaveNumWritten, //被写数据缓冲区
- bufLen - dwHaveNumWritten, //被写数据缓冲区大小
- &dwNumBytesWritten, //函数执行成功后,返回实际向串口写的个数
- &m_olWrite) == FALSE)//并不是表示写入失败,而是没有将所有数据写入到缓冲区中,而只是写入了部分而已
- {
- if(GetLastError() != ERROR_IO_PENDING)
- {
- TRACE("CMyCESeries::WriteSyncPort 异步写串口0x20\n");
- return 0x20;
- }
- if(GetOverlappedResult(m_hComm,&m_olWrite,&dwNumBytesWritten,TRUE) == FALSE)//这里将一直阻塞,直到将所有数据写入到缓冲区为止
- {
- TRACE("CMyCESeries::WriteSyncPort 异步写串口0x25\n");
- return 0x25;
- }
- }
- //else//写入成功
- {
- dwHaveNumWritten = dwHaveNumWritten + dwNumBytesWritten;
- //写入完成
- if (dwHaveNumWritten == bufLen)
- return TRUE;
- else
- {
- TRACE("CMyCESeries::WriteSyncPort 异步写串口%d失败\n", m_portNo);
- return FALSE;
- }
- }
- }
- }
- //函数介绍:设置串口读取、写入超时
- //入口参数:CommTimeOuts : 指向COMMTIMEOUTS结构
- //出口参数:(无)
- //返回值:TRUE:设置成功;FALSE:设置失败
- BOOL CMyCESeries::SetSeriesTimeouts(COMMTIMEOUTS CommTimeOuts)
- {
- ASSERT(m_hComm != INVALID_HANDLE_VALUE);
- return SetCommTimeouts(m_hComm,&CommTimeOuts);
- }
- //得到串口是否打开
- BOOL CMyCESeries::GetComOpened()
- {
- return m_bOpened;
- }
|