多线程串口编程工具CserialPort类(附VC基于MFC单文档协议通讯源程序及详细编程步骤)...

 老有人觉得MSComm通讯控件很土,更有人大声疾呼:忘了它吧。确实当我们对串口编程有了一定的了解后,应该用API函数写一个属于自己的串口程序,由于编程者对程序了解,对程序修改自如。但我一直没有停止过用MSComm通讯控件,那么简单的东西,对付简单的任务完全可以,但当我们需要在程序中用多个串口,而且还要做很多复杂的处理,那么最好不用MSComm通讯控件,如果这时你还不愿意自己编写底层,就用这个类:CserialPort类。

这是Remon Spekreijse写的一个串口类, 地址在: 

http://codeguru.earthweb.com/network/serialport.shtml

类作者Remon Spekreijse已作了一个基于对话框的同时检测4个串口示例的程序,在上面的网址和我主页的串口源码下载页也可以找到。我在这儿主要介绍如何将这个类应用到VC中基于文档的程序中。为了加深对串口数据处理的了解,我们利用这个类解决如下问题:


问题:


串口2(COM2)每隔1秒向串口1(COM1)发送的NEMA格式的报文:串头为$,串尾为*,中间为一个xxxx的整数( 比如2345,不足4位则前面以0代替代),最后是hh校验,规定hh为xxxx四个数的半BYTE校验和,最后加上回车<CR>与换行<LF>。整个数据包为$xxxx*hh<CR><LF>。
串口1收到上述报文后,校验正确后,将发来的数据显示在视窗中,并记下发来的正确帧数和错误帧数,若正确,还向串口2发送Y,串口2收到Y后将收到的Y的计数显示在视窗中。
测试方法:
将三线制串口线联接上同一台计算机的两个串口,编好程序后就可测试。如果没有两个串口的微机,自己改改程序。

好了,你可以先下载源程序: scporttest.zip(大小:49KB,VC6,WIN9X/2000,SerialPort.h SerialPort.cpp是两个类文件)

编程步骤:


◆1. 建立程序:
建立一个基于单文档的MFC应用程序SCPortTest,所有步骤保持缺省状态。


◆2. 添加类文件:
将SerialPort.h SerialPort.cpp两个类文件复制到工程文件夹中,用Project-Add to Project-Files命令将上述两个文件加入工程。并在SCPortTestView.h中将头文件SerialPort.h说明:#include "SerialPort.h"。


◆3. 人工增加串口消息响应函数:OnCommunication(WPARAM ch, LPARAM port)
首先在SCPortTestView.h中添加串口字符接收消息WM_COMM_RXCHAR(串口接收缓冲区内有一个字符)的响应函数声明:
//{{AFX_MSG(CSCPortTestView)
afx_msg LONG OnCommunication(WPARAM ch, LPARAM port);
//}}AFX_MSG
然后在SCPortTestView.cpp文件中进行WM_COMM_RXCHAR消息映射:
BEGIN_MESSAGE_MAP(CSCPortTestView, CView)
//{{AFX_MSG_MAP(CSCPortTestView)
ON_MESSAGE(WM_COMM_RXCHAR, OnCommunication)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
接着在SCPortTestView.cpp中加入函数的实现:
LONG CSCPortTestView::OnCommunication(WPARAM ch, LPARAM port)
{ ….. }
注意:由于这个串口类加入工程后,没有自动的消息映射机制,因此上述步骤均需要手工添加。


◆4 初始化串口
在视创建时初始化串口,首先利用ClassWizardr按下图生成OnInitialUpdate()函数。


接着在SerialPort.h文件中说明我们在程序中要用到的全局变量:
保存两个串口接收数据:
char m_chChecksum; //用于COM1的校验和计算
CString m_strRXhhCOM1; //用于存放COM1接收的半BYTE校验字节hh
CString m_strRXDataCOM1; //COM1接收数据
CString m_strRXDataCOM2; //COM2接收数据
UINT m_nRXErrorCOM1; //COM1接收数据错误帧数
UINT m_nRXErrorCOM2; //COM2接收数据错误帧数
UINT m_nRXCounterCOM1; //COM1接收数据错误帧数
UINT m_nRXCounterCOM2; //COM2接收数据错误帧数CString

再在SerialPort.h文件中说明串口类对象:CSerailPort m_ComPort[2]; (public)。
因为要初始化2个串口,所以这里用了数组。
下面是初始化串口1和串口2:
void CSCPortTestView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
m_chChecksum=0; //校验和置0
m_nRXErrorCOM1=0; //COM1接收数据错误帧数置0
m_nRXErrorCOM2=0; //COM2接收数据错误帧数置0
m_nRXCounterCOM1=0; //COM1接收数据错误帧数置0
m_nRXCounterCOM2=0; //COM2接收数据错误帧数置0
m_strRXhhCOM1.Empty(); //清空半BYTE校验hh存储变量
for(int i=0;i<2;i++)
{
if (m_ComPort[i].InitPort(this,i+1,9600,'N',8,1,EV_RXFLAG | EV_RXCHAR,512))
//portnr=1(2),baud=960,parity='N',databits=8,stopsbits=1,
//dwCommEvents=EV_RXCHAR|EV_RXFLAG,nBufferSize=512
{
m_ComPort[i].StartMonitoring(); //启动串口监视线程
if(i==1) SetTimer(1,1000,NULL); //设置定时器,1秒后发送数据
}
else
{
CString str;
str.Format("COM%d 没有发现,或被其它设备占用",i+1);
AfxMessageBox(str);
}
}
}

◆5 利用ClassWizard按下图生成CSCPortTestView 的时间消息WM_TIMER响应函数:


void CSCPortTestView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
int randdata=rand()%9000; //产生9000以内的随机数
CString strSendData;
strSendData.Format("%04d",randdata);
SendString(strSendData, 2); //串口2发送数据;
CView::OnTimer(nIDEvent);
}

上面用到的SendString()需按如下方式生成:
在ClassView中单击鼠标右键,在环境菜单中选择Add Member Function:



void CSCPortTestView::SendString(CString &str, int Port)
{
char checksum=0,cr=CR,lf=LF;
char c1,c2;
for(int i=0;i<str.GetLength();i++)
checksum = checksum^str[i];
c2=checksum & 0x0f; c1=((checksum >> 4) & 0x0f);
if (c1 < 10) c1+= '0'; else c1 += 'A' - 10;
if (c2 < 10) c2+= '0'; else c2 += 'A' - 10;
CString str1;
str1='$'+str+"*"+c1+c2+cr+lf;
m_ComPort[Port-1].WriteToPort((LPCTSTR)str1);
}
请注意上面函数中是如何生成校验码的,要切记的是发送的校验码生成方式和对方接收的校验检测方式要一致。



◆6 在OnCommunication(WPARAM ch, LPARAM port)函数中进行数据处理
说明:WPARAM、 LPARAM 类型是多态数据类型(polymorphic data type),在WIN32中为32位,支持多种数据类型,根据需要自动适应,这样程序有很强的适应性。在此我们可以分别理解为char和 integer 类型数据。
每当串口接收缓冲区内有一个字符时,就会产生一个WM_COMM_RXCHAR消息,触发OnCommunication函数,这时我们就可以在函数中进行数据处理,所以这个消息就是整个程序的"发动机"。
下面是根据本文最初提出的问题写出的处理函数:
LONG CSCPortTestView::OnCommunication(WPARAM ch, LPARAM port)
{
static int count1=0,count2=0,count3=0;
static char c1,c2;
static int flag;
CString strCheck="";

if(port==2) //COM2接收到数据
{
CString strtemp=(char)ch;
if(strtemp=="Y")
{
m_nRXCounterCOM2++;
CString strtemp;
strtemp.Format("COM2: NO.%06d", m_nRXCounterCOM2);
CDC* pDC=GetDC(); //准备数据显示
pDC->TextOut(10,50,strtemp);//显示接收到的数据
ReleaseDC(pDC);
}
}

if(port==1) //COM1接收到数据
{
m_strRXDataCOM1 += (char)ch;
switch(ch)
{
case '$':
m_chChecksum=0; //开始计算CheckSum
flag=0;
break;
case '*':
flag=2;
c2=m_chChecksum & 0x0f; c1=((m_chChecksum >> 4) & 0x0f);
if (c1 < 10) c1+= '0'; else c1 += 'A' - 10;
if (c2 < 10) c2+= '0'; else c2 += 'A' - 10;
break;
case CR:
break;
case LF:
m_strRXDataCOM1.Empty();
break;
default:
if(flag>0)
{
m_strRXhhCOM1 += ch; //得到收到的校验值hh
if(flag==1)
{
strCheck = strCheck+c1+c2; //计算得到的校验值hh
if(strCheck!=m_strRXhhCOM1) //如果校验有错
{
m_strRXDataCOM1.Empty();
m_nRXErrorCOM1++; //串口1错误帧数加1
}
else
{
m_nRXCounterCOM1++;
if(m_strRXDataCOM1.Left(1)=="$") //接收数据的第一个字符是$吗?
{
char tbuf[6];
char *temp=(char*)((LPCTSTR)m_strRXDataCOM1);
tbuf[0]=temp[1]; tbuf[1]=temp[2];
tbuf[2]=temp[3]; tbuf[3]=temp[4];
tbuf[4]=0; //0表示字符串的结束,必要
int data=atoi(tbuf);
CString strDisplay1,strDisplay2;
strDisplay1.Format("NO. %06d: The reseived data is %04d",m_nRXCounterCOM1,data);
strDisplay2.Format("Error Counter=%04d.",m_nRXErrorCOM1);
CDC* pDC=GetDC(); //准备数据显示
//int nColor=pDC->SetTextColor(RGB(255,255,0));
pDC->TextOut(10,10,strDisplay1);//显示接收到的数据
pDC->TextOut(30,30,strDisplay2);//显示错误帧数
//pDC->SetTextColor(nColor);
ReleaseDC(pDC);
}
CString str1="Y";
m_ComPort[0].WriteToPort((LPCTSTR)str1);//发送应答信号Y
}
m_strRXhhCOM1.Empty();
}
flag--;
}
else
m_chChecksum ^= ch;
break;
}

}
return 0;
}

 

感谢原作者。原文没有作者签名。

转载于:https://www.cnblogs.com/Davis812/p/3902607.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MFC(Microsoft Foundation Classes)是微软公司为Windows操作系统开发的一套面向对象的C++库。它能够使开发者更轻松地创建Windows图形用户界面(GUI)应用程序。 在MFC中,实现多串口通信可以通过使用CSerialPort来实现。CSerialPort封装了串口的操作,可以通过调用其方法来打开、关闭、读取和写入串口数据。 而要实现多线程,则可以利用MFC提供的CWinThread。通过创建多个CWinThread对象,每个对象分别处理一个串口的通信任务。每个CWinThread对象使用独立的线程来执行任务,以避免阻塞主线程。 具体实现步骤如下: 1. 创建一个主线程,用于处理用户界面和整体程序的控制逻辑。 2. 创建多个CWinThread对象,每个对象分别处理一个串口通信任务。 3. 在每个CWinThread对象中,使用CSerialPort的实例进行串口的打开、配置、读取和写入等操作。 4. 确保每个CWinThread对象都在独立的线程中执行任务,以防止串口通信阻塞主线程。 5. 如果需要在主线程中对串口通信的数据进行处理,可以使用MFC提供的同步机制来保证数据的正确访问。 总结起来,使用MFC实现多串口多线程步骤主要包括创建主线程和多个CWinThread对象、配置串口参数、在每个CWinThread对象中进行串口通信操作,并进行数据的同步处理。这样可以使程序具备同时处理多个串口通信任务的能力。 ### 回答2: MFC是Microsoft Foundation Classes的简称,它是一个用于创建Windows应用程序的C++库。多串口是指在一个应用程序中同时使用多个串口进行通信的功能。多线程是指在一个应用程序中同时运行多个线程的能力。 在MFC中实现多串口功能可以通过使用串口CSerialPort)和线程(CWinThread)来实现。首先,我们可以创建多个CSerialPort对象,每个对象代表一个串口。然后,可以使用CWinThread创建多个线程,每个线程负责处理一个串口的通信操作。 在每个线程中,可以使用CSerialPort对象进行串口通信。每个线程通过调用CSerialPort的成员函数来打开、关闭、发送和接收数据。可以使用线程同步机制(如事件、临界区或互斥量)来避免多个线程同时访问同一个串口对象。 在多线程环境下使用MFC串口功能需要注意以下几点: 1. 使用线程同步机制来保证多个线程对同一个串口对象的访问安全; 2. 避免多个线程同时发送数据到同一个串口,以避免数据冲突; 3. 合理分配串口和线程资源,避免资源竞争和性能瓶颈; 4. 注意处理串口通信错误和异常情况,保证程序的稳定性和可靠性。 总之,借助MFC库,我们可以很方便地实现多串口多线程功能,提高应用程序的通信效率和并发处理能力。但在并发操作时,需要注意线程安全和资源管理等问题,以确保应用程序的稳定性和可靠性。 ### 回答3: MFC是由Microsoft开发的用于Windows操作系统的应用程序框架,它提供了许多用于开发图形用户界面的和功能。多串口是指在一个应用程序中同时控制多个串口设备的能力。而多线程是指在一个应用程序中同时运行多个线程的能力。 在MFC中实现多串口控制涉及到串口通信的操作,可以使用MFC提供的CSerialPort来进行串口的打开、读取和写入操作。对于多个串口设备,可以创建多个CSerialPort对象来分别控制每个串口。 而要在MFC中实现多线程,可以使用MFC提供的CWinThread来创建和管理线程。可以通过创建多个CWinThread对象来实现多个并发运行的线程。每个线程可以执行不同的任务,通过线程间的通信和同步机制来实现数据的共享和协调。 在实现MFC串口多线程时,可以将每个串口设备的控制逻辑分别放在不同的线程中运行,使得每个串口都可以独立地进行读取和写入操作。同时,还可以利用多线程的特性,实现同步和异步的串口通信,提高程序的效率和响应性。 总之,MFC串口多线程的实现可以通过使用MFC提供的相关和功能来实现。通过合理地组织和管理多个串口设备和线程,可以实现并发运行的串口通信任务,提高应用程序的灵活性和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值