Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2096295
  • 博文数量: 909
  • 博客积分: 4000
  • 博客等级: 上校
  • 技术积分: 12260
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-06 20:50
文章分类

全部博文(909)

文章存档

2008年(909)

我的朋友

分类:

2008-05-06 21:43:32

一起学习
多线程,多接收模式串口类LsComm
作者:Liu_sir

下载示例源代码

描述:一个串口通讯类
应用平台: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;

写串口数据的过程同上

注意:第3,4种模式为自动接收模式,在用以前你必须先定义回调函数,然后,设置类的接收函数
/* 回调函数定义 */

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点:
(1)如果你需要处理中断事件,你可以在每种模式中设置中断接收事件:如下
//定义中断事件接收函数  

void OnComBreak(LPVOID pSender,DWORD dwMask,COMSTAT stat)

{

     //deal with the break of com here

}

m_ComPort.SetBreakHandleFunc(OnComBreak); //设置中断事件
(2)如何处理如,改变波特率,以及其它参数呢?
m_ComPort.GetSerialPort()可以获得一个CSerialPort类的指针,如何就可以操作各种com属性了.
DCB dcb;

this->m_ComPort.GetSerialPort()->GetState(dcb);
二.类的设计与编程

1. 类结构

为了说明一个大概的类构成,我用Rose画了一下类图:如下
CComPort内部聚合了一个CSerialPort的串口类,并与一个CReadComThread线程关联,让其去读取串口数据.
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);
这些语句是怎么实现串口数据的发送和读取的呢?

2. 打开过程CComPort::Open()
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);

	}

}
主要做的工作是:
新建串口 this->m_pPort = new CSerialPort();
打开串口 this->m_pPort->Open
创建读取线程 this->m_pReadThread = new CReadComThread();
设立线程类与CComPort的关联关系this->m_pReadThread->BandSerialPort(this);
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); 

	}

	

}
模式主要在线程执行的过程中发挥作用

3.串口的发送数据过程
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) 异步写串口的过程

4.串口的读取过程

分两种:查询读取和线程自动读取
(1) 查询读取
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

(2) 线程等待处理
主要过程:在线程CreadComThread的Execute中
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的类的代码.

当然,由于串行通讯各种情况综合在一起比较复杂,另外本人水平有限,所以一时很难考虑全面,这个版本暂时定为1.0,希望大家如果在使用过程中发现什么问题,请及时的告诉偶(E-Mail:Milo2002@sohu.com),有时间我做个升级什么的,当然,希望大家多多提出批评意见. 下载本文示例代码


多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm多线程,多接收模式串口类LsComm
阅读(493) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~