老有人觉得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;

}

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

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

  1. VC++ MFC单文档应用程序SDI下调用glGenBuffersARB(1, &pbo)方法编译通过但执行时出错原因分析及解决办法:glewInit()初始化的错误

    1.问题症状 在VC++环境下,利用MFC单文档应用程序SDI下开发OpenGL程序,当调用glGenBuffersARB(1, &pbo)方法编译通过但执行时出错,出错代码如下: OpenG ...

  2. 【2016.3.30项目技术记录】]VS2010自动生成MFC单文档框架程序的修改:去除属性框,在CViewTree类中添加鼠标单击响应

    转自http://blog.csdn.net/yanfeiouc2009/archive/2010/06/07/5653360.aspx 手头上有个东西要用到单文档,由于想省事,直接用VS2010做了 ...

  3. VC基于单文档OpenGL框架

    本文是在VC6.0的环境下,运用MFC实现的OpenGL最基本框架,需要简单了解MFC编程(会在VC6.0里创建MFC单文档应用程序就行),甚至不必了解OpenGL的知识.以下是具体的步骤. 1.创建 ...

  4. C++MFC编程笔记day05 文档类-单文档和多文档应用程序

    文档类 1 相关类    CDocument类-父类是CCmdTarget类,所以,文档类也能够处理菜单等               命令消息. 作用保存和管理数据.    注意事项:怎样解决断言错 ...

  5. 【VC编程技巧】窗口☞3.5对单文档或者多文档程序制作启动画面

    (一)概要: 文章描写叙述了如何通过Visual C++ 2012或者Visual C++ .NET,为单文档或者多文档程序制作启动画面.在Microsoft Visual Studio 6.0中对于 ...

  6. VS2010/MFC编程入门之二(利用MFC向导生成单文档应用程序框架)

    VS2010/MFC编程入门之二(利用MFC向导生成单文档应用程序框架)-软件开发-鸡啄米 http://www.jizhuomi.com/software/141.html   上一讲中讲了VS20 ...

  7. 用VC++MFC做文本编辑器(单文档模式)

    用VC++MFC做文本编辑器(单文档模式) 原来做过一个用对话框实现的文本编辑器,其实用MFC模板里面的单文档模板也可以做,甚至更加方便,适合入门级的爱好者试试,现介绍方法如下: < xmlna ...

  8. 第15.37节 PyQt(Python+Qt)入门学习:containers容器类部件QMdiArea多文档界面部件详解及编程开发案例

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.引言 老猿在前期学习PyQt相关知识时,对每个组件的属性及方法都研 ...

  9. 【C#附源码】数据库文档生成工具支持(Excel+Html)

    [2015] 很多时候,我们在生成数据库文档时,使用某些工具,可效果总不理想,不是内容不详细,就是表现效果一般般.很多还是word.html的.看着真是别扭.本人习惯用Excel,所以闲暇时,就简单的 ...

随机推荐

  1. 解决无法安装Microsoft .Net Framework 3.5

    如果解决不了,试试我的方法吧,我也在网上找了好久,最终在本地解决了 所需工具:dism,和Net Framework 3.5,已经打包 链接:https://pan.baidu.com/s/1nKok ...

  2. python记录_day09 初识函数

    一.认识函数 函数:对动作或者功能的封装 格式: 函数声明     def  函数名(): 函数体 函数调用     函数名() #定义函数 def xiao(): print("你的笑像一 ...

  3. 媒体查询漫谈——@media Queries

    通过不同的媒体类型和条件定义样式表规则.媒体查询让CSS可以更精确作用于不同的媒体类型和同一媒体的不同条件.媒体查询的大部分媒体特性都接受min和max用于表达”大于或等于”和”小与或等于”.如:wi ...

  4. mybatis批量插入的方式

    批量插入数据经常是把一个集合的数据一次性插入数据库,只需要执行一次sql语句,但是批量插入通常会报框架版本号的错误,本人就遇到 com.alipay.zdal.parser.exceptions.a: ...

  5. css/html/Javascript/getUrlCode/各种前端小点汇总集合

    js与原生进行数据交互,简单来说就是原生拦截js传到的数据 var u = navigator.userAgent; var isAndroid = u.indexOf('Android') > ...

  6. Spring JdbcTemplate 查询结果集Map反向生成Java实体(转)

    原文地址:Spring JdbcTemplate 查询结果集Map反向生成Java实体 以前写过一篇文章吐槽过Spring JdbcTemplate的queryForList方法(参见:http:// ...

  7. burpsuite拦截https数据包(Firefox)

    1.配置浏览器对http/https都使用burpsuite代理 http和https是分开的,对http使用了代理并不代表对https也使用了代理,要配置浏览器让其对https也使用同样的代理. 当 ...

  8. ACCESS数据库基本使用

    ACCESS是Office自带的数据库,使用起来非常方便. 相比,其它数据库来说,使用率较低,但是同MYSQL一样,免费.正因为如此,所以很多建站的程序员,还是会选择使用它. 部分代码: <sc ...

  9. linux下正则表达式学习

    下表包含了元字符的完整列表以及它们在正则表达式上下文中的行为: 字符 描述 \ 将下一个字符标记为一个特殊字符.或一个原义字符.或一个 向后引用.或一个八进制转义符.例如,'n' 匹配字符 " ...

  10. html <iframe>介绍

    iframe 元素会创建包含另外一个文档的内联框架 属性 值 描述 align left.right.top.middle.bottom 不赞成使用.请使用样式代替.规定如何根据周围的元素来对齐此框架 ...