MFC--串口编程---WIN API的方式将串扣操作封装在线程类中

串口采集数据

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

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

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,0,sizeof(OVERLAPPED));
                                                  //初始化读异步变量
    memset((unsigned char*)&m_overWrite,0,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,0,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 = 0
        //|    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 = 0;    // 以毫秒为单位指定一个乘数B,用来计算读操作的总限时:
    m_Commtimeout.ReadTotalTimeoutConstant = 0;      // 以毫秒为单位指定一个常数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;//初始化读取数字的个数为30000
                if(dwEvent & EV_RXCHAR)
                {
                    ClearCommError(m_hCom,&dwError,&ComState);//清空串口事件
                    if(ComState.cbInQue != 0)
                    {
                        //如果缓冲区内容少于30000个,则全部取出即可
                        if(ComState.cbInQue < dwReadNum)
                        {
                            dwReadNum = ComState.cbInQue;
                        }
                        memset(rBuf,0,sizeof(rBuf));
                        dwByteRead = 0;
                        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,0,0);
    ::WaitForSingleObject(pThread->m_hThread,dwTimeout);
    DWORD nExitCode=0;
    BOOL bFlag=::GetExitCodeThread(pThread->m_hThread,&nExitCode);
    if (bFlag)
    {
        delete pThread;
    }    

    return nExitCode;
}
时间: 2024-10-15 00:10:34

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

基于java的socket编程及API解析

一.socket通讯过程 1.socket与socket编程简介: socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式.通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据. 我们所说的socket 编程,是站在传输层的基础上,所以可以使用 TCP/UDP 协议,但是不能进行访问网页,因为访问网页所需要的 http 协议位于应用层.作为一个应用程序是能实现该层以下的内容,而不能实现在该层之上的内容. 2.socket通讯过程: (

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

http://blog.csdn.net/henreash/article/details/3183119 Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到, 但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchronize的用法就完了. 然而这并不是多线程编程的全部,我写此文的目的在于对此作一个补充. 线程本质上是进程中一段并发运行的代码. 一个进程至少有一个线程,即所谓的主线程. 同时还可以有多个

Delphi中线程类TThread 实现多线程编程

作者:Rogee出处:Http://Rogee.cnblogs.com/心得:BLOG是什么,它是一个记录学习过程的东西 Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchronize的用法就完了.然而这并不是多线程编程的全部,我写此文的目的在于对此作一个补充. 线程本质上是进程中一段并发运行的代码.一个进程至少有一个线程,即所谓的主线程.同时还可以

【转】VS2010下MFC的串口编程

串口通信简介 一般来说,计算机都有一个或多个串行端口,这些串口提供了外部设备与PC进行数据传输和通信的通道,在CPU和外设之间充当解释器的角色.当字符数据从CPU发送给外设时,这些字符数据将被转换成串行比特流数据:当接收数据时,比特流数据被转换为字符数据传递给CPU,再进一步说,在操作系统方面,Windows用通信驱动程序(COMM.DRV)调用API函数发送和接收数据:当用通信控件或声明调用API函数时,它们由COMM.DRV解释并传递给设备驱动程序.作为一个程序员,要编写通信程序,只需知道通

VS2010下MFC的串口编程

串口通信简介 一般来说,计算机都有一个或多个串行端口,这些串口提供了外部设备与PC进行数据传输和通信的通道,在CPU和外设之间充当解释器的角色.当字符数据从CPU发送给外设时,这些字符数据将被转换成串行比特流数据:当接收数据时,比特流数据被转换为字符数据传递给CPU,再进一步说,在操作系统方面,Windows用通信驱动程序(COMM.DRV)调用API函数发送和接收数据:当用通信控件或声明调用API函数时,它们由COMM.DRV解释并传递给设备驱动程序.作为一个程序员,要编写通信程序,只需知道通

MFC串口的编程 mscomm控件与SerialPort类

MFC制作上位机,首先需要了解的是串口的编程,一般有两种方法,一个是使用ActiveX控件,例如mscomm串口控件,还有一个是用SerialPort类或者一些其他的串口类,这两个的区别是使用SerialPort类不需要注册控件,在其他没有安装控件的电脑上也能够用. 一·使用mscomm串口控件 使用mscomm串口控件的方法网上一大堆,大致说一些方法和一些需要注意的地方.如果是使用VC6.0在WIN7上来编写就会有个问题会通常说添加控件的方法为选中项目à“工程”à“添加到工程”à“Compon

Window API串口编程

虽然使用诸如 CSerialPort VC串口类,MSComm VC 串口控件等非常方便,但有时这些控件并不适合自己的特殊需求,所以有必要了解一下基于Windows API的串口编程方法,下面介绍一下API串口编程的一般步骤及相关串口API函数. 串口操作一般有四步,分别是: 1) 打开串口 2) 配置串口 3) 读写串口 4) 关闭串口 1. 打开串口 在<VC 打开串口>一文中我们已经单独介绍过如果利用API打开串口的方法,打开串口是用API函数CreateFile来打开或创建的.该函数的

使用Windows API进行串口编程

串口通信一般分为四大步:打开串口->配置串口->读写串口->关闭串口,还可以在串口上监听读写等事件. 1.打开和关闭串口 Windows中串口是作为文件来处理的,调用CreateFile()函数可以打开串口,函数执行成功返回串口句柄,出错返回INVALID_HANDLE_VALUE. HANDLE WINAPI CreateFile( _In_ LPCTSTR lpFileName,//要打开或创建的文件名 _In_ DWORD dwDesiredAccess,//访问类型 _In_ D

Java串口编程

最近由于项目的需要,需要用到java串口和windows端java程序的通讯,笔者也是刚刚接触串口这一模块,在网上搜索了很多的串口编程实例之类的,几乎前篇一律吧,但是串口通讯之前的配置是非常重要的,如果配置没有成功,编程也显得没有意义.串口编程主要有两种接口,第一种是利用sun提供的comm.jar包,这种方式比较古老了,这个包也没有更新.第二种就是RXTX模式,这种模式其实和comm.jar包的模式几乎是一样的.下面就记下我学习和使用此模块的记录.RXTX资源包,网上有很多,但要注意的是看你的