中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > Visual C++ > 网络与通信
多线程,多接收模式串口类LsComm
作者:未知 时间:2005-07-20 14:16 出处:VC知识库 责编:chinaitpower
              摘要:多线程,多接收模式串口类LsComm

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

下载示例源代码

描述:一个串口通讯类
应用平台:Windows
版本: v1.0
主要功能:设计了一个简洁易用的多线程串行通讯接口,可以切换查询和自动接收模式,进行对串口数据收发的类

接触VC,很不习惯mscomm等Active控件老让人去注册的方式,所以参照Delphi中的SpComm设计了一个类CComPort,对PJ Naughter 的CSerialPort()进行了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),有时间我做个升级什么的,当然,希望大家多多提出批评意见.
关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有