分享
 
 
 

多线程,多接收模式串口类LsComm

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

多线程,多接收模式串口类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(InBufferCount0)

{

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_InBufferCount0)

{

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),有时间我做个升级什么的,当然,希望大家多多提出批评意见.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有