串口操作类

来源:互联网 发布:mysql分区表 编辑:程序博客网 时间:2024/05/21 22:00

一:创建操作串口的串口类
    //定义串口接收数据函数类型
    typedef void (CALLBACK* ONSERIESREAD)(void * pOwner /*父对象指针*/
                                          ,BYTE* buf  /*接收到的缓冲区*/
                                          ,DWORD dwBufLen /*接收到的缓冲区长度*/);

    class CCESeries
    {
    public:
        CCESeries(void);
        ~CCESeries(void);
    public:
        //打开串口
        BOOL OpenPort(void* pOwner,/*指向父指针*/
            UINT portNo    = 1,        /*串口号*/
            UINT baud        = 9600,    /*波特率*/
            UINT parity    = NOPARITY, /*奇偶校验*/
            UINT databits    = 8,        /*数据位*/
            UINT stopbits    = 0        /*停止位*/
            );
        //关闭串口
        void ClosePort();
        //同步写入数据
        BOOL WriteSyncPort(const BYTE*buf , DWORD bufLen);
        //设置串口读取、写入超时
        BOOL SetSeriesTimeouts(COMMTIMEOUTS CommTimeOuts);
        //得到串口是否打开
        BOOL GetComOpened();
    private:
        //串口读线程函数
        static  DWORD WINAPI ReadThreadFunc(LPVOID lparam);
    private:
        //关闭读线程
        void CloseReadThread();
    private:
        //已打开的串口句柄
        HANDLE    m_hComm;
        //读线程句柄
        HANDLE m_hReadThread;
        //读线程ID标识
        DWORD m_dwReadThreadID;
        //读线程退出事件
        HANDLE m_hReadCloseEvent;
        BOOL m_bOpened; //串口是否打开
        void * m_pOwner; //指定父对象指针
    public:
        ONSERIESREAD m_OnSeriesRead; //串口读取回调函数
    };

    //构造函数
    CCESeries::CCESeries()
    {
        //初始化内部变量
        m_hComm = INVALID_HANDLE_VALUE;
        m_OnSeriesRead = NULL;
        m_bOpened = 0;
    }

    //析构函数
    CCESeries::~CCESeries()
    {
        if (m_bOpened)
        {
            //关闭串口
            ClosePort();
        }
    }

    //串口读线程函数
    DWORD CCESeries::ReadThreadFunc(LPVOID lparam)
    {
        CCESeries *ceSeries = (CCESeries*)lparam;

        DWORD    evtMask;
        BYTE * readBuf = NULL;//读取的字节
        DWORD actualReadLen=0;//实际读取的字节数
        DWORD willReadLen;

        DWORD dwReadErrors;
        COMSTAT    cmState;

        // 清空缓冲,并检查串口是否打开。
        ASSERT(ceSeries->m_hComm !=INVALID_HANDLE_VALUE);

        //清空串口
        PurgeComm(ceSeries->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR );

        SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );
        while (TRUE)
        {      
            if (WaitCommEvent(ceSeries->m_hComm,&evtMask,0))
            {           
                SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );
                //表示串口收到字符       
                if (evtMask & EV_RXCHAR)
                {
                    ClearCommError(ceSeries->m_hComm,&dwReadErrors,&cmState);
                    willReadLen = cmState.cbInQue ;
                    if (willReadLen <= 0)
                    {
                        continue;
                    }

                    //分配内存
                    readBuf = new BYTE[willReadLen];
                    ZeroMemory(readBuf,willReadLen);
                    //读取串口数据
                    ReadFile(ceSeries->m_hComm, readBuf, willReadLen, &actualReadLen,0);

                    //如果读取的数据大于,
                    if (actualReadLen>0)
                    {
                        //触发读取回调函数
                        if (ceSeries->m_OnSeriesRead)
                        {
                            ceSeries->m_OnSeriesRead(ceSeries->m_pOwner,readBuf,actualReadLen);
                        }
                    }

                    //释放内存
                    delete[] readBuf;
                    readBuf = NULL;
                }
            }
            //如果收到读线程退出信号,则退出线程
            if (WaitForSingleObject(ceSeries->m_hReadCloseEvent,500) == WAIT_OBJECT_0)
            {
                break;
            }
        }
        return 0;
    }

    //关闭读线程
    void CCESeries::CloseReadThread()
    {
        SetEvent(m_hReadCloseEvent);
        //设置所有事件无效无效
        SetCommMask(m_hComm, 0);
        //清空所有将要读的数据
        PurgeComm( m_hComm,  PURGE_RXCLEAR );
        //等待秒,如果读线程没有退出,则强制退出
        if (WaitForSingleObject(m_hReadThread,4000) == WAIT_TIMEOUT)
        {
            TerminateThread(m_hReadThread,0);
        }
        m_hReadThread = NULL;
    }

    /*
    *函数介绍:打开串口
    *入口参数:pPortOwner    :使用此串口类的窗体句柄
    portNo        :串口号
    baud            :波特率
    parity        :奇偶校验
    databits        :数据位
    stopbits        :停止位
    *出口参数:(无)
    *返回值:TRUE:成功打开串口;FALSE:打开串口失败
    */
    BOOL CCESeries::OpenPort(void * pOwner,
                             UINT portNo    ,            /*串口号*/
                             UINT baud        ,            /*波特率*/
                             UINT parity    ,            /*奇偶校验*/
                             UINT databits    ,            /*数据位*/
                             UINT stopbits               /*停止位*/
                             )
    {
        DCB commParam;
        TCHAR szPort[15];   

        ASSERT(pOwner!=NULL);
        m_pOwner = pOwner;

        // 已经打开的话,直接返回
        if (m_hComm != INVALID_HANDLE_VALUE)
        {
            return TRUE;
        }

        //设置串口名
        wsprintf(szPort, L"COM%d:", portNo);
        //打开串口
        m_hComm = CreateFile(
            szPort,
            GENERIC_READ | GENERIC_WRITE,    //允许读和写
            0,                                //独占方式(共享模式)
            NULL,
            OPEN_EXISTING,                    //打开而不是创建(创建方式)
            0,
            NULL
            );

        if (m_hComm == INVALID_HANDLE_VALUE)
        {
            // 无效句柄,返回。       
            TRACE(_T("CreateFile 返回无效句柄/n"));
            return FALSE;

        }

        // 得到打开串口的当前属性参数,修改后再重新设置串口。
        if (!GetCommState(m_hComm,&commParam))
        {       
            //关闭串口
            CloseHandle (m_hComm);
            m_hComm = INVALID_HANDLE_VALUE;
            return FALSE;
        }

        //设置串口参数
        commParam.BaudRate = baud;                    // 设置波特率
        commParam.fBinary = TRUE;                    // 设置二进制模式,此处必须设置TRUE
        commParam.fParity = TRUE;                    // 支持奇偶校验
        commParam.ByteSize = databits;                // 数据位,范围:4-8
        commParam.Parity = parity;                // 校验模式
        commParam.StopBits = stopbits;                // 停止位

        commParam.fOutxCtsFlow = FALSE;                // No CTS output flow control
        commParam.fOutxDsrFlow = FALSE;                // No DSR output flow control
        commParam.fDtrControl = DTR_CONTROL_ENABLE;
        // DTR flow control type
        commParam.fDsrSensitivity = FALSE;            // DSR sensitivity
        commParam.fTXContinueOnXoff = TRUE;            // XOFF continues Tx
        commParam.fOutX = FALSE;                    // No XON/XOFF out flow control
        commParam.fInX = FALSE;                        // No XON/XOFF in flow control
        commParam.fErrorChar = FALSE;                // Disable error replacement
        commParam.fNull = FALSE;                    // Disable null stripping
        commParam.fRtsControl = RTS_CONTROL_ENABLE;
        // RTS flow control
        commParam.fAbortOnError = FALSE;            // 当串口发生错误,并不终止串口读写

        //设置串口参数
        if (!SetCommState(m_hComm, &commParam))
        {
            TRACE(_T("SetCommState error"));   
            //关闭串口
            CloseHandle (m_hComm);
            m_hComm = INVALID_HANDLE_VALUE;       
            return FALSE;
        }

        //设置串口读写时间
        COMMTIMEOUTS CommTimeOuts;
        GetCommTimeouts (m_hComm, &CommTimeOuts);
        CommTimeOuts.ReadIntervalTimeout = MAXDWORD; 
        CommTimeOuts.ReadTotalTimeoutMultiplier = 0; 
        CommTimeOuts.ReadTotalTimeoutConstant = 0;   
        CommTimeOuts.WriteTotalTimeoutMultiplier = 10; 
        CommTimeOuts.WriteTotalTimeoutConstant = 1000; 
        if(!SetCommTimeouts( m_hComm, &CommTimeOuts ))
        {
            TRACE( _T("SetCommTimeouts 返回错误") );
            //关闭串口
            CloseHandle (m_hComm);

            m_hComm = INVALID_HANDLE_VALUE;
            return FALSE;
        }

        //指定端口监测的事件集
        SetCommMask (m_hComm, EV_RXCHAR);
        //分配串口设备缓冲区
        SetupComm(m_hComm,512,512);
        //初始化缓冲区中的信息
        PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);

        CString strEvent;
        strEvent.Format(L"Com_ReadCloseEvent%d",portNo);
        m_hReadCloseEvent = CreateEvent(NULL,TRUE,FALSE,strEvent);

        //创建串口读数据监听线程
        m_hReadThread = CreateThread(NULL,0,ReadThreadFunc,this,0,&m_dwReadThreadID);

        TRACE(_T("串口打开成功"));
        m_bOpened = TRUE;
        return TRUE;
    }

    /*
    *函数介绍:关闭串口
    *入口参数:(无)
    *出口参数:(无)
    *返回值:(无)
    */
    void CCESeries::ClosePort()
    {   
        //表示串口还没有打开
        if (m_hComm == INVALID_HANDLE_VALUE)
        {
            return ;
        }

        //关闭读线程
        CloseReadThread();

        //关闭串口
        CloseHandle (m_hComm);
        //关闭事件
        CloseHandle(m_hReadCloseEvent);

        m_hComm = INVALID_HANDLE_VALUE;
        m_bOpened = FALSE;
    }

    /*
    *函数介绍:往串口写入数据
    *入口参数:buf :待写入数据缓冲区
    bufLen : 待写入缓冲区长度
    *出口参数:(无)
    *返回值:TRUE:设置成功;FALSE:设置失败
    */
    BOOL CCESeries::WriteSyncPort(const BYTE*buf , DWORD bufLen)
    {
        DWORD dwNumBytesWritten;
        DWORD dwHaveNumWritten =0 ; //已经写入多少

        int iInc = 0; //如果次写入不成功,返回FALSE
        ASSERT(m_hComm != INVALID_HANDLE_VALUE);
        do
        {
            if (WriteFile (m_hComm,                    //串口句柄
                buf+dwHaveNumWritten,                //被写数据缓冲区
                bufLen - dwHaveNumWritten,          //被写数据缓冲区大小
                &dwNumBytesWritten,                    //函数执行成功后,返回实际向串口写的个数   
                NULL))                                //此处必须设置NULL
            {
                dwHaveNumWritten = dwHaveNumWritten + dwNumBytesWritten;
                //写入完成
                if (dwHaveNumWritten == bufLen)
                {
                    break;
                }
                iInc++;
                if (iInc >= 3)
                {
                    return FALSE;
                }
                Sleep(10);
            }
            else
            {
                return FALSE;
            }
        }while (TRUE);

        return TRUE;       
    }

    /*
    *函数介绍:设置串口读取、写入超时
    *入口参数:CommTimeOuts : 指向COMMTIMEOUTS结构
    *出口参数:(无)
    *返回值:TRUE:设置成功;FALSE:设置失败
    */
    BOOL CCESeries::SetSeriesTimeouts(COMMTIMEOUTS CommTimeOuts)
    {
        ASSERT(m_hComm != INVALID_HANDLE_VALUE);
        return SetCommTimeouts(m_hComm,&CommTimeOuts);
    }

    //得到串口是否打开
    BOOL CCESeries::GetComOpened()
    {
        return m_bOpened;
    }

二:操作串口类

    public:
        CCESeries *m_pSerial;  //串口对象指针
        //定义串口接收数据函数类型
        static void CALLBACK OnSerialRead(void * pOwner,BYTE* buf,DWORD bufLen);

1:打开串口
    //打开串口
    void CSerialSampleDlg::OnBnClickedBtnOpen()
    {
        //判断串口是否已经打开
        if (m_pSerial != NULL)
        {
            m_pSerial->ClosePort();

            delete m_pSerial;
            m_pSerial = NULL;
        }

        //串口参数输入对话框
        CDlgParams dlgParams;

        if (dlgParams.DoModal() == IDOK)
        {
            //新建串口通讯对象
            m_pSerial = new CCESeries();
            m_pSerial->m_OnSeriesRead = OnSerialRead; //

            //打开串口
            if (m_pSerial->OpenPort(this,
                dlgParams.m_portNo,
                dlgParams.m_baud,
                dlgParams.m_parity,
                dlgParams.m_databits,
                dlgParams.m_stopbits))
            {
                AfxMessageBox(L"串口打开成功");
            }
            else
            {
                AfxMessageBox(L"串口打开失败");
            }
        }
    }

2:关闭串口
    //关闭串口
    void CSerialSampleDlg::OnBnClickedBtnClose()
    {
        //
        if (m_pSerial != NULL)
        {
            //关闭串口
            m_pSerial->ClosePort();

            //释放串口对象
            delete m_pSerial;
            m_pSerial = NULL;
        }
    }

3:发送数据
    //发送数据
    void CSerialSampleDlg::OnBnClickedBtnSend()
    {
        char * buf  =NULL;  //定义发送缓冲区
        DWORD dwBufLen = 0;   //定义发送缓冲区长度
        CString strSend = L"";

        //得到发送输入框
        CEdit *pEdtSendMsg = (CEdit*)GetDlgItem(IDC_EDT_SEND);
        ASSERT(pEdtSendMsg != NULL);

        //串口如果没有打开,直接返回
        if (m_pSerial == NULL)
        {
            AfxMessageBox(L"串口未打开");
            return;
        }

        //得到待发送的字符串
        pEdtSendMsg->GetWindowTextW(strSend);

        //将待发送的字符串转换成单字节,进行发送
        buf = new char[strSend.GetLength()*2+1];
        ZeroMemory(buf,strSend.GetLength()*2+1);
        //转换成单字节进行发送   
        WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,strSend.GetBuffer(strSend.GetLength())
            ,strSend.GetLength(),buf,strSend.GetLength()*2,NULL,NULL);

        dwBufLen = strlen(buf) + 1;
        //发送字符串
        m_pSerial->WriteSyncPort((BYTE*)buf,dwBufLen);

        //释放内存
        delete[] buf;
        buf = NULL;
    }
    //定义串口接收数据函数类型
    void CALLBACK CSerialSampleDlg::OnSerialRead(void * pOwner,BYTE* buf,DWORD bufLen)
    {
        BYTE *pRecvBuf = NULL; //接收缓冲区
        //得到父对象指针
        CSerialSampleDlg* pThis = (CSerialSampleDlg*)pOwner;
        //将接收的缓冲区拷贝到pRecvBuf种
        pRecvBuf = new BYTE[bufLen];
        CopyMemory(pRecvBuf,buf,bufLen);
        //发送异步消息,表示收到串口数据,消息处理完,应释放内存
        pThis->PostMessage(WM_RECV_SERIAL_DATA,WPARAM(pRecvBuf),bufLen);
    }

三:关注回调函数的使用

1:定义串口接收数据函数类型
    typedef void (CALLBACK* ONSERIESREAD)(void * pOwner /*父对象指针*/
        ,BYTE* buf  /*接收到的缓冲区*/
        ,DWORD dwBufLen /*接收到的缓冲区长度*/);
        ONSERIESREAD m_OnSeriesRead; //串口读取回调函数

2:当线程收到数据时,触发回调函数
    if (ceSeries->m_OnSeriesRead)
    {
        ceSeries->m_OnSeriesRead(ceSeries->m_pOwner,readBuf,actualReadLen);
    }

3:在操作类中添加函数
    CCESeries *m_pSerial;  //串口对象指针
    m_pSerial = new CCESeries();
    m_pSerial->m_OnSeriesRead = OnSerialRead; //
    //定义串口接收数据函数类型
    static void CALLBACK OnSerialRead(void * pOwner,BYTE* buf,DWORD bufLen);
    //定义串口接收数据函数类型
    void CALLBACK CSerialSampleDlg::OnSerialRead(void * pOwner,BYTE* buf,DWORD bufLen)
    {
        BYTE *pRecvBuf = NULL; //接收缓冲区
        //得到父对象指针
        CSerialSampleDlg* pThis = (CSerialSampleDlg*)pOwner;
        //将接收的缓冲区拷贝到pRecvBuf种
        pRecvBuf = new BYTE[bufLen];
        CopyMemory(pRecvBuf,buf,bufLen);

        //发送异步消息,表示收到串口数据,消息处理完,应释放内存
        pThis->PostMessage(WM_RECV_SERIAL_DATA,WPARAM(pRecvBuf),bufLen);
    }

4:消息触发收到函数
    //定义串口数据接收消息常量
    #define WM_RECV_SERIAL_DATA WM_USER + 101
    afx_msg LONG OnRecvSerialData(WPARAM wParam,LPARAM lParam);
    ON_MESSAGE(WM_RECV_SERIAL_DATA,OnRecvSerialData)
    // 串口接收数据处理函数
    LONG CSerialSampleDlg::OnRecvSerialData(WPARAM wParam,LPARAM lParam)
    {
        return 0;
    }

原创粉丝点击