串口采集数据

本文档介绍的是如何获取串口原始数据并将原始数据解析成可处理或可展示的数据。

一、串口采集有很多方式:

1)、MFC有一个专门的控件,直接编程采集,一个控件只能采集一个串口,而且串口名字比如是COM20可能就打不开(这里我没有实践,师兄给这样说的),波特率太高读数会出错。

2)、利用Windows API通信函数(该工程里面就采用的这种方式)

3)、利用Visual C++的标准通信函数_inp、_inpw、_inpd、_outp等直接对串口进行操作。

4)、第三方编写的通信类。

二、实现过程

软件中参考《VC++编程实践宝典》中的第18章内容,主要参考最后示例代码,将对串口的操作封装在一个线程类中,当需要打开一个串口进行采集数据时,就实例化一个封装窗口操作的线程类。

下面介绍该线程类的实现(环境VS2010):

1、新建MFC类CThreadCom,继承自CWinThread。

2、构造函数初始化相关变量

CThreadCom::CThreadCom(HANDLE hCom)
{
//初始化串口句柄
//如果用到了其他变量请自行相应添加
m_bInit = false; //串口初始化状态为FALSE
m_sCom = _T(""); //清空串口名称
m_sError = _T("No Error!"); //初始化错误信息
m_hThread = NULL; //置空线程句柄
memset((unsigned char*)&m_overRead,,sizeof(OVERLAPPED));
//初始化读异步变量
memset((unsigned char*)&m_overWrite,,sizeof(OVERLAPPED));
//初始化写异步变量
m_overRead.hEvent = CreateEvent(NULL,true,false,NULL);//创建读事件
m_overWrite.hEvent = CreateEvent(NULL,true,false,NULL);//创建写事件
}

3、初始化函数:对相关变量初始化,跟构造函数的作用差不多,但是具体什么关系我还不太清楚

BOOL CThreadCom::InitInstance()
{
// TODO: perform and per-thread initialization here
m_bAutoDelete=FALSE;
m_bDone=FALSE;
return TRUE;
}

4、打开串口函数:对串口参数进行设置,并打开串口

//打开串口
BOOL CThreadCom::OpenCom(CString strCom,DWORD BaudRate,BYTE ByteSize,BYTE Parity,BYTE StopBits)
{
CloseCom(); CString strLog;
m_hCom = CreateFile(strCom,GENERIC_READ | GENERIC_WRITE,,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);
if(m_hCom == INVALID_HANDLE_VALUE)
{
strLog.Format(_T("Open %s Error"),strCom);
AfxMessageBox(strLog);
return false;
}
SetupComm(m_hCom,MAXCOMINBUF,MAXCOMOUTBUF);//设置输入输出最佳缓冲区
DCB dcb;
if(!GetCommState(m_hCom,&dcb))
{
AfxMessageBox(_T("获取串口状态失败"));
CloseHandle(m_hCom);
m_hCom = INVALID_HANDLE_VALUE;
return false;
}
dcb.BaudRate = BaudRate;
dcb.ByteSize = ByteSize;
dcb.Parity = Parity;
dcb.StopBits = StopBits;
if(!SetCommState(m_hCom,&dcb))
{
CloseHandle(m_hCom);
m_hCom = INVALID_HANDLE_VALUE;
strLog.Format(_T("Set %s CommState Error!"),strCom);
AfxMessageBox(strLog);
return false;
}
//初始化错误消息变量
m_sError = _T("No Error");
//定义串口时间标记
DWORD CommMask;
CommMask =
//| EV_BREAK //检测到中断信号
//| EV_CTS //检测到CTS信号发生中断
//| EV_DSR //检测到DSR信号发生变化
//| EV_ERR //发生行状态错误
//| EV_EVENT1 //第一个程序指定的事件
//| EV_EVENT2 //第二个程序指定的事件
//| EV_PERR //发生打印错误
//| EV_RING //检测到振铃
//| EV_RLSD //检测到PLSD信号发生变化
//| EV_RX80FULL //接收缓冲区数据到达80%
| EV_RXCHAR; //接收到字符,并放入到输入输出缓冲区
//| EV_RXFLAG //接收到事件字符,并放入到输入输出缓冲区
//| EV_TXEMPTY; //输出缓冲区的最后一个字符发送出去
SetCommMask(m_hCom,CommMask);//注册要处理的事件
GetCommTimeouts(m_hCom,&m_Commtimeout);
//将ReadIntervalTimeout设为MAXDWORD;ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant设为0 ,表示读操作将立即返回放在缓冲区的数据
m_Commtimeout.ReadIntervalTimeout = MAXDWORD; // 两个字符到达之间的最大时间A,毫秒。0表示不用间隔限时
m_Commtimeout.ReadTotalTimeoutMultiplier = ; // 以毫秒为单位指定一个乘数B,用来计算读操作的总限时:
m_Commtimeout.ReadTotalTimeoutConstant = ; // 以毫秒为单位指定一个常数C,用于计算读操作的总限时:0表示读操作不使用限时时间
//上面三行代码对时间的限制达到的效果是只要缓冲区里有数据就进行读取
m_bInit = true;//初始化串口状态为true
return true;
}

5、在线程类的Run函数里面进行读取操作,一直读取,知道线程退出

int CThreadCom::Run()
{
// TODO: 在此添加专用代码和/或调用基类
DWORD dwError,dwReadNum,dwByteRead,dwEvent;
COMSTAT ComState;//窗口状态
BYTE rBuf[MAXCOMINBUF];//输入数据缓冲区
while(!m_bDone)
{ while(m_hCom != INVALID_HANDLE_VALUE)
{
if(WaitCommEvent(m_hCom,&dwEvent,NULL))//等待注册的串口事件发生
{
dwReadNum = ;//初始化读取数字的个数为30000
if(dwEvent & EV_RXCHAR)
{
ClearCommError(m_hCom,&dwError,&ComState);//清空串口事件
if(ComState.cbInQue != )
{
//如果缓冲区内容少于30000个,则全部取出即可
if(ComState.cbInQue < dwReadNum)
{
dwReadNum = ComState.cbInQue;
}
memset(rBuf,,sizeof(rBuf));
dwByteRead = ;
ReadFile(m_hCom,rBuf,dwReadNum,&dwByteRead,&m_overRead);//读取数据
//到这里已经将本次读取操作取到的数据放在了rBuf数组里面了,接下来就是按照下位机对数据帧的定义进行解析,然后再进行数据存储或展示之类的操作。
}
}
}
}
}
}
} return CWinThread::Run();
}

6、在使用该线程类的类中在不再使用该线程后比如:关闭窗口,一定要有线程退出的操作,不然会造成内存泄露问题。

CPortSet类是我定义CThreadCom类变量并使用的类。在采集结束的地方调用该函数。释放线程内存。

DWORD CPortSet::StopWinThread(/*CWinThread *pThread,*/ DWORD dwTimeout)
{
CWinThread *pThread = pThreadCom;
if (pThread==NULL) return NULL;
pThread->PostThreadMessage(WM_QUIT,,);
::WaitForSingleObject(pThread->m_hThread,dwTimeout);
DWORD nExitCode=;
BOOL bFlag=::GetExitCodeThread(pThread->m_hThread,&nExitCode);
if (bFlag)
{
delete pThread;
} return nExitCode;
}

MFC--串口编程---WIN API的方式将串扣操作封装在线程类中的更多相关文章

  1. MFC框架各部分指针获取方式

    MFC框架各部分指针获取方式 前人在CSDN总结的,曾经帮助过我,整理总结一下,希望也能帮助一下别人. 获得CWinApp 获得CMainFrame 获得CChildFrame 获得CDocument ...

  2. MFC【6】文件I/O和串行化

    文件输入和输出(I/O)服务是所有操作系统的主要工作.Microsoft Windows提供了各种API函数用来读.写和操作磁盘文件.MFC将这些桉树和CFile类融合在面对对象的模型里.其中CFil ...

  3. Delphi中线程类TThread实现多线程编程1---构造、析构……

    参考:http://www.cnblogs.com/rogee/archive/2010/09/20/1832053.html Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大 ...

  4. MFC类中获得其它类指针

    当用VC++的Application Wizard生成除了CDialog Basiced以外的应用程序时,将自动产生视图类.文档类.主帧窗口类.应用程序类等等.一般来说,程序的核心数据及操作在文档类中 ...

  5. 转发 Delphi中线程类TThread 实现多线程编程

    Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchr ...

  6. Delphi 实现多线程编程的线程类 TThread

    http://blog.csdn.net/henreash/article/details/3183119 Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉 ...

  7. 多线程,线程类三种方式,线程调度,线程同步,死锁,线程间的通信,阻塞队列,wait和sleep区别?

    重难点梳理 知识点梳理 学习目标 1.能够知道什么是进程什么是线程(进程和线程的概述,多进程和多线程的意义) 2.能够掌握线程常见API的使用 3.能够理解什么是线程安全问题 4.能够知道什么是锁 5 ...

  8. Appium(八):Appium API(二) 元素等待、元素操作

    1. 元素等待 我们在使用脚本的时候,可能会由于网络.服务器处理.电脑等原因,我们想要找的元素没有加载出来,这个时候如果直接定位就可能会报错. 这个时候我们就可以设置元素等待了. 什么叫元素等待呢? ...

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

    老有人觉得MSComm通讯控件很土,更有人大声疾呼:忘了它吧.确实当我们对串口编程有了一定的了解后,应该用API函数写一个属于自己的串口程序,由于编程者对程序了解,对程序修改自如.但我一直没有停止过用 ...

随机推荐

  1. unix_timestamp 和 from_unixtime 时间戳函数 区别

    1.unix_timestamp 将时间转化为时间戳.(date 类型数据转换成 timestamp 形式整数) 没传时间参数则取当前时间的时间戳 mysql> select unix_time ...

  2. json/pickle/shelve/xml/configparser/hashlib/subprocess - 总结

    序列化:序列化指把内存里的数据类型转成字符串,以使其能存储到硬盘或通过网络传输到远程,因为硬盘或网络传输时只能接受bytes为什么要序列化:可以直接把内存数据(eg:10个列表,3个嵌套字典)存到硬盘 ...

  3. PHP数组遍历详解

    一.PHP数组简介 1.PHP数组的分类 按照下标的不同分为关联数组和索引数组①索引数组:下标从0开始依次增长②关联数组:下标为字符串格式,每个下标字符串与数组的值一一对应,(有点像对象的键值对) 下 ...

  4. Linux学习笔记(6)磁盘分区(LVM)

    1.逻辑管理技术LVM的概念 1.1 LVM ,逻辑卷管理,以便扩展管理盘符. PV:物理卷 VG:卷组 LV:逻辑卷 PE(physical Extend):物理扩展(默认4M),就是我们逻辑卷管理 ...

  5. CoreThink开发(十)把官方首页轮播替换成HTML5-3D轮播

    效果: 资源已经上传到我的下载里边. http://download.csdn.net/detail/u012995856/9587206 1.复制资源文件到CoreThink项目中 corethin ...

  6. 简明python教程七----面向对象的编程

    根据操作数据的函数或语句块来设计程序的,被称为面向过程的编程. 把数据和功能结合起来,用称为对象的东西包裹起来的组织程序的方法,称为面向对象的编程理念. 类和对象是面向对象编程的两个主要方面.类创建一 ...

  7. mysql中解决主键自增长断号问题

    情况一:如果表中本来已经存在数据,并且有断号的现象.那先得删除主键再添加,重新设置自增长. 1.ALTER TABLE student DROP id; 2.ALTER TABLE student A ...

  8. mysql忽略一些错误代码

    模拟的故障,在从库中新建一个库,然后主库新建一个与从库相同名字的库,然后进入下面的show Mysql从库复制故障解决 当show slave status:报错 slave_io_running:y ...

  9. 美国评出2016最值得去的旅游胜地+纯电动车郊游记+DIY一个小电动车

    美国评出2016最值得去的旅游胜地(10) http://bbs.miercn.com/bd/201510/thread_569397_1_10.html 自带发电机! 北汽E150 EV纯电动车郊游 ...

  10. Java技术学习路线

    转载 作者:David 链接:https://www.zhihu.com/question/25255189/answer/86898400来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商 ...