2008年(909)
分类:
2008-05-06 21:43:32
下载示例源代码
描述:一个串口通讯类
应用平台:Windows
版本: v1.0
主要功能:设计了一个简洁易用的多线程串行通讯接口,可以切换查询和自动接收模式,进行对串口数据收发的类
接触VC,很不习惯mscomm等Active控件老让人去注册的方式,所以参照Delphi中的SpComm设计了一个类CComPort,对PJ
Naughter 的CSerialPort(http://www.vckbase.com/document/viewdoc.asp?id=612)进行了2次封装,主要目的是简化串口的使用.使其用简单的代码就可以完成串口通讯的过程.做了一个Demo程序演示了CComPort的使用,附图如下:
下面我从如何使用和类的设计两个方面说明一下:
一 如何使用:
考虑到使用过程尽可能简洁,实用,为了满足不同的使用要求设计4种接收模式, 前两种为手动接收方式,后两种为自动类回调方式,下面是使用代码
1.ManualReceiveByQuery 手动查询接收
#include "ComPort.h" LsComm::CComPort m_ComPort; //LsComm is namespace in c m_ComPort.Open(2,LsComm::CComPort::AutoReceiveByquery); //ReCeive Com Data: 接收语句 DWORD InBufferCount; byte pbuffer[2048]; InBufferCount = m_ComPort.GetInBufferCount(); if(InBufferCount>0) { m_ComPort.GetInput(pbuffer,InBufferCount); } //Write Com Data: 写串口数据 char a[10]="abcdefg"; this->m_ComPort.Output(a,sizeof(a));2.ManualReceiveByConst(异步模式) 手动定常数接收模式
#include "ComPort.h" LsComm::CComPort m_ComPort;//LsComm is namespace in c m_ComPort.Open(2,LsComm::CComPort::AutoReceiveByConst); //ReCeive Com Data: //接收数据 DWORD InBufferCount=0; byte pbuffer[2048]; InBufferCount=this->m_ComPort.GetInput(pbuffer,10,1000); //上面我要在1000毫秒内接收10字节的数据,IbufferCount返回实际得到的数据 if(InBufferCount==0) return; CString c; char a[4]; for(int i=0;i<(int)InBufferCount;i ) { ::sprintf(a,"%2.2X",pbuffer[i]); c =a; c =" "; } c="接收(Receive):" c;写串口数据的过程同上
/* 回调函数定义 */ void OnReceiveData(LPVOID pSender,void* pBuf,DWORD InBufferCount) { CString c; byte a[100]; char b[4]=""; memcpy(a,pBuf,InBufferCount); CLsCommDemoDlg* pDlg = (CLsCommDemoDlg*) pSender; CListBox*pList =(CListBox*)pDlg->GetDlgItem(IDC_LIST1); for(int i=0;i<(int)InBufferCount;i ) { ::sprintf(b,"%2.2X",a[i]); c =" "; c =b; } c="接收(Receive):" c; pList->AddString(c); }3.AutoReceiveBySignal 自动信号接收模式
#include "ComPort.h" LsComm::CComPort m_ComPort;//LsComm is namespace in c m_ComPort.Open(2,LsComm::CComPort::AutoReceiveBySignal ); m_ComPort.SetReceiveFunc((FOnReceiveData)OnReceiveData,this); m_ComPort.SetBreakHandleFunc(OnComBreak); //ReCeive Com Data:接收数据函数 in OnReceiveData(LPVOID pSender,void* pBuf,DWORD InBufferCount) //above //write data the same as the first mode;4.AutoReceiveByBreak 中断接收模式
#include "ComPort.h" LsComm::CComPort m_ComPort;//LsComm is namespace in c m_ComPort.Open(2,LsComm::CComPort::AutoReceiveByBreak ); m_ComPort.SetReceiveFunc((FOnReceiveData)OnReceiveData,this); //ReCeive Com Data:接收数据函数 in OnReceiveData(LPVOID pSender,void* pBuf,DWORD InBufferCount) //above //write data the same as the first mode;另外说明2点:
//定义中断事件接收函数 void OnComBreak(LPVOID pSender,DWORD dwMask,COMSTAT stat) { //deal with the break of com here } m_ComPort.SetBreakHandleFunc(OnComBreak); //设置中断事件(2)如何处理如,改变波特率,以及其它参数呢?
DCB dcb; this->m_ComPort.GetSerialPort()->GetState(dcb);二.类的设计与编程
LsComm::CComPort m_ComPort;//LsComm is namespace in c m_ComPort.Open(2,LsComm::CComPort::AutoReceiveBySignal ); m_ComPort.SetReceiveFunc(OnReceiveData,this); m_ComPort.SetBreakHandleFunc(OnComBreak);这些语句是怎么实现串口数据的发送和读取的呢?
void CComPort::Open(int nPort,ReceiveMode mode, DWORD dwBaud, Parity parity, BYTE DataBits, StopBits stopbits,FlowControl fc) { //1.新建串口 this->m_pPort = new CSerialPort(); //2.判断收发模式 if(mode==ReceiveMode::ManualReceiveByQuery) { this->m_IsOverlapped = false; } else { this->m_IsOverlapped = true; } this->m_RecvMode = mode; //3.转换参数,打开串口 int index; index=parity-CComPort::EvenParity; CSerialPort::Parity spParity=(CSerialPort::Parity)(CSerialPort::EvenParity index); …略去 this->m_pPort->Open(nPort,dwBaud,spParity,DataBits,spStopbits,spFC,m_IsOverlapped); this->m_pPort->Setup(4096,4096); this->m_pPort->Purge(PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); //it is important!! COMMTIMEOUTS timeouts; this->m_pPort->GetTimeouts(timeouts); timeouts.ReadIntervalTimeout=100; this->m_pPort->SetTimeouts(timeouts); this->m_CurPortNum = nPort; //创建关闭事件 this->m_hCloseEvent = CreateEvent(NULL,true,false,NULL); ASSERT(this->m_hCloseEvent); //4.创建线程类 this->m_pReadThread = new CReadComThread(); this->m_pReadThread->BandSerialPort(this); this->m_pReadThread->Create(); this->m_pReadThread->Resume(); if(this->IsOverlapped()) { this->m_hWriteEvent = ::CreateEvent(NULL,false,false,NULL); } }主要做的工作是:
void CReadComThread::BandSerialPort(CComPort* pPort) { ASSERT(pPort); this->m_pPort = pPort; //创建异步读取事件 if(this->m_pPort->IsOverlapped()) { this->m_ReadOverlapped.hEvent =::CreateEvent(NULL,false,false,NULL); ASSERT(this->m_ReadOverlapped.hEvent); this->m_BreakOverlapped.hEvent = ::CreateEvent(NULL,false,false,NULL); ASSERT(this->m_BreakOverlapped.hEvent); } }模式主要在线程执行的过程中发挥作用
DWORD CComPort::Output(void* pBuf,DWORD Count) { DWORD dwWriteBytes=0; if(this->IsOverlapped())//异步模式 { this->m_pPort->Write(pBuf,Count,this->m_WriteOverlapped); if(WaitForSingleObject(this->m_WriteOverlapped.hEvent,INFINITE)==WAIT_OBJECT_0) { this->m_pPort->GetOverlappedResult(this->m_WriteOverlapped,dwWriteBytes,false); } } else dwWriteBytes= this->m_pPort->Write(pBuf,Count); return dwWriteBytes; }再看this->m_pPort->Write(pBuf,Count);
DWORD CSerialPort::Write(const void* lpBuf, DWORD dwCount) { ASSERT(IsOpen()); ASSERT(!m_bOverlapped); DWORD dwBytesWritten = 0; if (!WriteFile(m_hComm, lpBuf, dwCount, &dwBytesWritten, NULL)) { TRACE(_T("Failed in call to WriteFile\n")); AfxThrowSerialException(); } return dwBytesWritten; }或者是BOOL CSerialPort::Write(const void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped, DWORD* pBytesWritten) 异步写串口的过程
DWORD CComPort::GetInput(void* pBuf,DWORD Count,DWORD dwMilliseconds) { //不能在自动模式下getinput if(this->GetReceiveMode()==CComPort::AutoReceiveByBreak|| this->GetReceiveMode()==CComPort::AutoReceiveBySignal) { ::AfxMessageBox("Can''t use GetInput methord in this mode!"); return 0; } if(this->IsOverlapped()) { ASSERT(this->m_pReadThread); DWORD dwBytes = this->m_pReadThread->ReadInput(pBuf,Count,dwMilliseconds); this->m_pPort->TerminateOutstandingReads(); return dwBytes; } else return this->m_pPort->Read(pBuf,Count); }主要是调用m_pPort->Read(pBuf,Count);然后调用API函数ReadFile
void CReadComThread::Execute() { if(this->m_pPort->GetReceiveMode()==CComPort::ManualReceiveByQuery) { this->ExecuteByManualQueryRecvMode(); } else if(this->m_pPort->GetReceiveMode()==CComPort::ManualReceiveByConst) { this->ExecuteByManualConstRecvMode(); } else if(this->m_pPort->GetReceiveMode()==CComPort::AutoReceiveBySignal) { this->ExecuteByAutoSignalRecvMode(); } else//中断模式 { this->ExecuteByAutoBreakRecvMode(); } }主要是选择模式然后执行: 下面看看this->ExecuteByAutoSignalRecvMode();
void CReadComThread::ExecuteByAutoSignalRecvMode() { DWORD dwMask=0; HANDLE WaitHandles[3]; //监听事件数组 DWORD dwSignaledHandle; WaitHandles[0] = this->m_pPort->GetCloseHandle(); WaitHandles[1] = this->m_ReadOverlapped.hEvent; WaitHandles[2] = this->m_BreakOverlapped.hEvent; this->m_pPort->GetSerialPort()->SetMask(EV_ERR | EV_RLSD | EV_RING ); if(!SetBreakEvent(dwMask)) goto EndThread; //设置读事件 if(!SetReadEvent(this->m_ReadOverlapped)) goto EndThread; //设置comEvent for(;;) { dwSignaledHandle=::WaitForMultipleObjects(3,WaitHandles,false,INFINITE); switch(dwSignaledHandle) { case WAIT_OBJECT_0: goto EndThread; break; case WAIT_OBJECT_0 1: if(!this->HandleReadEvent(this->m_ReadOverlapped)) goto EndThread; if(!this->SetReadEvent(this->m_ReadOverlapped)) goto EndThread; break; case WAIT_OBJECT_0 2: if(!this->HandleBreakEvent(dwMask)) goto EndThread; if(!this->SetBreakEvent(dwMask)) goto EndThread; break; default: //goto EndThread; break; } } EndThread: this->m_pPort->GetSerialPort()->Purge(PURGE_RXABORT | PURGE_RXCLEAR); ::CloseHandle(this->m_ReadOverlapped.hEvent); ::CloseHandle(this->m_BreakOverlapped.hEvent); return ; }主要是一个等待事件发送然后调用,响应的过程,如果读取事件发生则调用this->HandleReadEvent(this->m_ReadOverlapped);
bool CReadComThread::HandleReadEvent(OVERLAPPED& overlapped) { if(this->m_pPort->GetSerialPort()->GetOverlappedResult(overlapped,this->m_InBufferCount,false)) { return this->HandleData(); } DWORD dwError = ::GetLastError(); if(dwError==ERROR_INVALID_HANDLE) return false; else return true; }如果查询有数据,则this->HandleData();
bool CReadComThread::HandleData() //处理读取数据 { if(this->m_InBufferCount>0) { this->m_pBuffer = new byte[this->m_InBufferCount]; for(int i=0;i<(int)this->m_InBufferCount;i ) { this->m_pBuffer[i] = this->m_InputBuffer[i]; } this->m_pPort->ReceiveData(this->m_pBuffer,this->m_InBufferCount); delete[] this->m_pBuffer; } return true; }在这调用了this->m_pPort->ReceiveData(this->m_pBuffer,this->m_InBufferCount);即调用了你传入的函数.整个读取过程就这样了.如果还有不明白,请看我写的CComPort的类的代码.