串口.Qt532测试(同步)

环境:Win7x64、Qt5.3.2 MSVC OpenGL(x86)、vs2010(x86)

ZC:这里的例子是 同步的函数操作,貌似 如果子线程在等待 WaitCommEvent(...)或ReadFile(...) 返回的话(即 串口句柄正在被使用中),界面主线程执行 CloseHandle(...) 或 SetCommMask(...) 的话,就会卡在那里...    于是 考虑改用 异步方式

  ZC:想到 一个方式,使用同步方式的时候 可以使用 强制关闭线程的方式“TerminateThread(线程句柄, ExitCode);” 来关闭子线程,这样就不会再占用 串口句柄了,CloseHandle(..)也可以顺利执行。问题:TerminateThread(...) 会有一些 动态申请的资源释放,获取的锁释放 等的问题(具体看MSDN的说明,里面还提到了"heap lock",于是在 申请内存的过程中 如果强制结束线程的话 堆锁未释放 别的线程再申请内存的时候就卡在那里了...),还有 在 XP以及之前的Windws版本OS中 强制结束线程 OS不会释放 它的初始栈,造成内存泄漏(我看了一下 XP的资源管理器,确实是这样的现象)。

 

1、ZC:

 1.1、开始时,遇到的问题:在接收信息的线程中,ReadFile(...) 每次都是 立即返回的  但是获取的数据都是0字节的长度,现象就好像是 用了 异步的方式 没等到事件 就立即返回了...

  找到问题:(1)、查看了 CreateFile(...)的参数,发现使用的就是 同步的方式(没有指定 参数 FILE_FLAG_OVERLAPPED)

       (2)、后来 发现是 COMMTIMEOUTS 设置的不正确的缘故

  测试下来,有2种方式 设置COMMTIMEOUTS,来 解决上面的问题:

    ①、类似如下的参数设置:

         COMMTIMEOUTS TimeOuts;
         //设定读超时
         TimeOuts.ReadIntervalTimeout = 100;
         TimeOuts.ReadTotalTimeoutMultiplier = 5000;
         TimeOuts.ReadTotalTimeoutConstant = 5000;
         //设定写超时
         TimeOuts.WriteTotalTimeoutMultiplier = 500;
         TimeOuts.WriteTotalTimeoutConstant = 2000;

    ②、这样设置:

         COMMTIMEOUTS CommTimeOuts;
         GetCommTimeouts(hCom1, &CommTimeOuts);
         CommTimeOuts.ReadIntervalTimeout  = MAXDWORD;
         CommTimeOuts.ReadTotalTimeoutMultiplier  = 0;
         CommTimeOuts.ReadTotalTimeoutConstant  = 0;
         CommTimeOuts.WriteTotalTimeoutMultiplier  = 10;
         CommTimeOuts.WriteTotalTimeoutConstant  = 1000;

      ZC:光是这样设置的话,就会出现上面的问题,ReadFile(...) 立即返回 0字节数据。接收线程 就会 连续不断的去ReadFile(...) ...

      ZC:这样设置的话,需要 使用 “SetCommMask(hCom1, EV_RXCHAR);” 和 “DWORD dwMask = EV_RXFLAG; WaitCommEvent(g_hCom, &dwMask, NULL);”,使用 它们 就是 声明和等待EV_RXCHAR事件(该事件 意思是 只要输入缓冲区接收到数据就会触发)

      

2、测试代码:

  2,1、main.cpp

#include "MainWindow.h"
#include <QApplication>

extern MainWindow* g_pMainWindow;

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    g_pMainWindow = &w;
    a.installNativeEventFilter(&w);// 注意,不是“a.installEventFilter(w);”,少了 "Native"

    return a.exec();
}

  2.2、MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QAbstractNativeEventFilter>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow, public QAbstractNativeEventFilter
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

public:
    virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *);

private slots:
    void on_pbtnConn_clicked();
    void on_pbtnRecvMsgClear_clicked();
    void on_pbtnSendMsgClear_clicked();
    void on_pbrnSend_clicked();

public:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

  2.3、MainWindow.cpp

#include "MainWindow.h"
#include "ui_MainWindow.h"



MainWindow* g_pMainWindow = NULL;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // https://blog.csdn.net/horizons_kong/article/details/54412339
    // https://blog.csdn.net/zmdsjtu/article/details/78539681
}

MainWindow::~MainWindow()
{
    delete ui;
}

#include <QDebug>
#include <windows.h>
#include <stdio.h>
#include <process.h>

#include "thread_z.h"
#include "PassInfo_z.h"
#include "SeriesPort_z.h"

QString TimeNowZ()
{
    SYSTEMTIME sys;
    GetLocalTime( &sys );

    char buf[128] = {0};
    sprintf_s( buf, sizeof(buf), "%02d:%02d:%02d.%03d ", sys.wHour, sys.wMinute, sys.wSecond, sys.wMilliseconds );

    return QString::fromLocal8Bit(buf);
}

void ShowMsgZ(const QString& _str)
{
    if (g_pMainWindow != NULL)
        g_pMainWindow->ui->teRecv->append(_str);
}

bool MainWindow::nativeEventFilter(const QByteArray &eventType, void *message, long *)
{
    MSG* pMsg = reinterpret_cast<MSG*>(message);
    if(pMsg->message == WM_PASSINFO)
    {
        int iLen = pMsg->wParam;
        QString strInfo = Info_Recv(ShowMsgZ, iLen, (char*)pMsg->lParam);
        if (! strInfo.isNull())
            //ui->teRecv->append(TimeNowZ() + strInfo);
            ui->teRecv->append(strInfo);
        else
            ui->teRecv->append("\r\n");

        return true;
    }

    return false;
}

void MainWindow::on_pbtnConn_clicked()
{
    QString strChuanKouHao = ui->cbChuanKouHao->currentText();
    QString strBoTeLv = ui->cbBoTeLv->currentText();
    BYTE Parity = (BYTE)ui->cbJiaoYanWei->currentIndex();
    BYTE ShuJuWei = (BYTE)(ui->cbShuJuWei->currentIndex() + 4);
    BYTE TingZhiWei = ((BYTE)ui->cbTingZhiWei->currentIndex() + 2) * 0.5;
    bool bRtn = SPort_Init(ShowMsgZ, strChuanKouHao.toLocal8Bit().data(),
                           strBoTeLv.toULong(), Parity, ShuJuWei, TingZhiWei);
    if (! bRtn)
    {
        ui->pbtnConn->setChecked(false);
        return;
    }

    ShowMsgZ("After SPort_Init(...)");
    _beginthread(Thread_RECV, 0, (void*)this->winId());
    ShowMsgZ("After _beginthread(...)");
}


void MainWindow::on_pbtnRecvMsgClear_clicked()
{
    ui->teRecv->clear();
    //_beginthread(Thread_RECV, 0, (void*)this->winId());
}


void MainWindow::on_pbtnSendMsgClear_clicked()
{
    ui->teSend->clear();
}

void MainWindow::on_pbrnSend_clicked()
{
//    char pc[] = {128,129};
//    if (IsDBCSLeadByte(pc[0]))
//        qDebug() << "T";
//    else
//        qDebug() << "F";

    ui->teRecv->append("\r\n");
}

  2.4、PassInfo_z.h

#ifndef PASSINFO_Z_H
#define PASSINFO_Z_H

#include <QString>

#include <Windows.h>
#define WM_PASSINFO     WM_USER+0x1000

typedef void (__cdecl *TshowMsg)(const QString& _str);

void Info_Send(HWND _hWnd, int _iLen, char* _pc);
void Info_Send_pc(HWND _hWnd, char* _pc);
QString Info_Recv(TshowMsg _funcShowMsg, int _iLen, char* _pc);

#endif // PASSINFO_Z_H

  2.5、PassInfo_z.cpp

#include "PassInfo_z.h"


void Info_Send(HWND _hWnd, int _iLen, char* _pc)
{
    if (_hWnd == 0)
        return;

    if (_iLen > 0)
    {
        char* pc = new char[_iLen];
        memcpy(&pc[0], _pc, _iLen);

        PostMessage( _hWnd, WM_PASSINFO, WPARAM(_iLen), LPARAM(pc) );
    }
    else
        PostMessage( _hWnd, WM_PASSINFO, WPARAM(_iLen), 0 );
}

void Info_Send_pc(HWND _hWnd, char* _pc)
{
    Info_Send(_hWnd, strlen(_pc), _pc);
}

QString Info_Recv(TshowMsg _funcShowMsg, int _iLen, char* _pc)
{
    // ZC: 进入到这个函数,前提是 "_iLen > 0"
    if (_iLen <= 0)
        return QString::null;
    return QString::fromLocal8Bit(_pc, _iLen);
}


// ZC: IsDBCSLeadByte( char ):BOOL; // ZC: 判断是否是 中文/韩文等字符的第1个字节
//char g_infoRecv[1024 * 4] = {0};
//int g_iInfoRecvCnt = 0;

//QString Info_Recv(TshowMsg _funcShowMsg, int _iLen, char* _pc)
//{
//    // ZC: 进入到这个函数,前提是 "_iLen > 0"

//    _funcShowMsg(QString::number(g_iInfoRecvCnt));

//    if (g_iInfoRecvCnt > 0)
//    {
//        memcpy(&g_infoRecv[g_iInfoRecvCnt], _pc, _iLen);
//        g_iInfoRecvCnt += _iLen;

//        if (! IsDBCSLeadByte(_pc[_iLen - 1]) )// ZC: 判断是否是 中文/韩文等字符的第1个字节
//        {
//            QString str = QString::fromLocal8Bit(g_infoRecv, g_iInfoRecvCnt);
//            g_iInfoRecvCnt = 0;
//            return str;
//        }
//    }
//    else
//    {
//        if ( IsDBCSLeadByte(_pc[_iLen - 1]) )
//        {
//            memcpy(&g_infoRecv[g_iInfoRecvCnt], _pc, _iLen);
//            g_iInfoRecvCnt += _iLen;
//        }
//        else
//            return QString::fromLocal8Bit(_pc, _iLen);
//    }
//    return QString::null;
//}

  2.6、SeriesPort_z.h

#ifndef SERIESPORT_Z_H
#define SERIESPORT_Z_H

#include <Windows.h>
#include <QString>

extern HANDLE g_hCom;

#define SERIES_PORT__IN_QUEUE_SIZE  1024 * 16
#define SERIES_PORT__OUT_QUEUE_SIZE 1024 * 16

typedef void (__cdecl *TshowMsg)(const QString& _str);

bool SPort_Init(TshowMsg _funcShowMsg,
                char* _pcSeriesPortName, DWORD _BaudRate, BYTE _Parity, BYTE _ByteSize, BYTE _StopBits);

#endif // SERIESPORT_Z_H

  2.7、SeriesPort_z.cpp

#include "SeriesPort_z.h"

#include <QDebug>
#include <QTextCodec>

#include "PassInfo_z.h"

//HWND g_hWnd = 0;
HANDLE g_hCom = 0;

bool SPort_Init(TshowMsg _funcShowMsg,
                char* _pcSeriesPortName,
                DWORD _BaudRate, BYTE _Parity, BYTE _ByteSize, BYTE _StopBits)
{
    g_hCom = 0;

    WCHAR wszSeriesPortName[8] = {0};
    MultiByteToWideChar(CP_ACP, 0, _pcSeriesPortName, strlen(_pcSeriesPortName) + 1,
                        wszSeriesPortName, sizeof(wszSeriesPortName) / sizeof(wszSeriesPortName[0]));
    HANDLE hCom1 = CreateFile(wszSeriesPortName,//COM1口
                              GENERIC_READ | GENERIC_WRITE, //允许读和写
                              0, //独占方式
                              NULL,
                              OPEN_EXISTING, //打开而不是创建
                              0, //同步方式
                              NULL);
    if (hCom1 == INVALID_HANDLE_VALUE)
    {
        QTextCodec *pCodec = QTextCodec::codecForName("GBK");
        QString strPrint = pCodec->toUnicode("打开COM失败 !");
        _funcShowMsg(strPrint);
        return false;
    }
    else
    {
        QTextCodec *pCodec = QTextCodec::codecForName("GBK");
        QString strPrint = pCodec->toUnicode("COM打开成功 !");
        _funcShowMsg(strPrint);
    }

                        //    SetupComm(hCom1, SERIES_PORT__IN_QUEUE_SIZE, SERIES_PORT__OUT_QUEUE_SIZE); //输入缓冲区和输出缓冲区的大小都是1024

                        //    COMMTIMEOUTS TimeOuts;
                        //    //设定读超时
                        //    TimeOuts.ReadIntervalTimeout = 100;
                        //    TimeOuts.ReadTotalTimeoutMultiplier = 5000;
                        //    TimeOuts.ReadTotalTimeoutConstant = 5000;
                        //    //设定写超时
                        //    TimeOuts.WriteTotalTimeoutMultiplier = 500;
                        //    TimeOuts.WriteTotalTimeoutConstant = 2000;
                        //    if (! SetCommTimeouts(hCom1, &TimeOuts)) //设置超时
                        //    {
                        //        int iErr = ::GetLastError();
                        //        QTextCodec *pCodec = QTextCodec::codecForName("GBK");
                        //        QString strPrint = pCodec->toUnicode("设置串口读写超时时间失败");
                        //        strPrint += ", last error code is "+QString::number(iErr);
                        //        _funcShowMsg(strPrint);
                        //        return false;
                        //    }

                        //    DCB dcb;
                        //    GetCommState(hCom1, &dcb);
                        //    dcb.BaudRate = _BaudRate;//9600; //波特率为9600
                        //    dcb.ByteSize = _ByteSize;//8; //每个字节有8位
                        //    dcb.Parity = _Parity;//NOPARITY; //无奇偶校验位
                        //    dcb.StopBits = _StopBits;//ONESTOPBIT; //1个停止位
                        //    if (! SetCommState(hCom1, &dcb))
                        //    {
                        //        int iErr = ::GetLastError();
                        //        QTextCodec *pCodec = QTextCodec::codecForName("GBK");
                        //        QString strPrint = pCodec->toUnicode("设置串口参数失败");
                        //        strPrint += ", last error code is "+QString::number(iErr);
                        //        _funcShowMsg(strPrint);
                        //        return false;
                        //    }

    _funcShowMsg( QString::number(_BaudRate)+","+QString::number(_ByteSize)
                  +","+QString::number(_Parity)+","+QString::number(_StopBits) );

    DCB dcb;
    if (! GetCommState(hCom1, &dcb))
    {
        int iErr = ::GetLastError();
        QTextCodec *pCodec = QTextCodec::codecForName("GBK");
        QString strPrint = pCodec->toUnicode("获取串口当前属性参数失败");
        strPrint += ", last error code is "+QString::number(iErr);
        _funcShowMsg(strPrint);
        return false;
    }
    //配置串口参数
    dcb.BaudRate  = _BaudRate;    //波特率
    dcb.fBinary  = TRUE;    //二进制模式。必须为TRUE
    dcb.ByteSize  = _ByteSize;    //数据位。范围4-8
    dcb.StopBits  = _StopBits;    //停止位
    if (_Parity  == NOPARITY)
    {
        dcb.fParity  = FALSE;    //奇偶校验。无奇偶校验
        dcb.Parity  = _Parity;    //校验模式。无奇偶校验
    }
    else
    {
        dcb.fParity  = TRUE;        //奇偶校验。
        dcb.Parity  = _Parity;    //校验模式。无奇偶校验
    }
    dcb.fOutxCtsFlow  = FALSE;    //CTS线上的硬件握手
    dcb.fOutxDsrFlow  = FALSE;    //DST线上的硬件握手
    dcb.fDtrControl  = DTR_CONTROL_ENABLE;//DTR控制
    dcb.fDsrSensitivity  = FALSE;
    dcb.fTXContinueOnXoff  = FALSE;//
    dcb.fOutX  = FALSE;            //是否使用XON/XOFF协议
    dcb.fInX  = FALSE;            //是否使用XON/XOFF协议
    dcb.fErrorChar  = FALSE;        //是否使用发送错误协议
    dcb.fNull  = FALSE;            //停用null stripping
    dcb.fRtsControl  = RTS_CONTROL_ENABLE;//
    dcb.fAbortOnError  = FALSE;    //串口发送错误,并不终止串口读写
    //设置串口参数
    if (! SetCommState(hCom1, &dcb))
    {
        int iErr = ::GetLastError();
        QTextCodec *pCodec = QTextCodec::codecForName("GBK");
        QString strPrint = pCodec->toUnicode("设置串口参数失败");
        strPrint += ", last error code is "+QString::number(iErr);
        _funcShowMsg(strPrint);
        return false;
    }

    //设置串口事件
    SetCommMask(hCom1, EV_RXCHAR);//在缓存中有字符时产生事件

    SetupComm(hCom1, SERIES_PORT__IN_QUEUE_SIZE, SERIES_PORT__OUT_QUEUE_SIZE);

    //设置串口读写时间
    COMMTIMEOUTS CommTimeOuts;
    GetCommTimeouts(hCom1, &CommTimeOuts);
    CommTimeOuts.ReadIntervalTimeout  = MAXDWORD;
    CommTimeOuts.ReadTotalTimeoutMultiplier  = 0;
    CommTimeOuts.ReadTotalTimeoutConstant  = 0;
    CommTimeOuts.WriteTotalTimeoutMultiplier  = 10;
    CommTimeOuts.WriteTotalTimeoutConstant  = 1000;
    if (!SetCommTimeouts(hCom1, &CommTimeOuts))
    {
        int iErr = ::GetLastError();
        QTextCodec *pCodec = QTextCodec::codecForName("GBK");
        QString strPrint = pCodec->toUnicode("设置串口读写超时时间失败");
        strPrint += ", last error code is "+QString::number(iErr);
        _funcShowMsg(strPrint);
        return false;
    }

    g_hCom = hCom1;
    return true;
}

bool SendData(TshowMsg _funcShowMsg, HANDLE _hComm, char* data, int len)
{
    if (_hComm == INVALID_HANDLE_VALUE)
    {
        QTextCodec *pCodec = QTextCodec::codecForName("GBK");
        QString strPrint = pCodec->toUnicode("串口未打开");
        _funcShowMsg(strPrint);
        return false;
    }
    //清空串口
    PurgeComm(_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR);
    //写串口
    DWORD dwWrite  = 0;
    DWORD dwRet  = WriteFile(_hComm, data, len, &dwWrite, NULL);
    int iErr = 0;
    if (! dwRet) { iErr = ::GetLastError(); }
    //清空串口
    PurgeComm(_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR);
    if (! dwRet)
    {
        QTextCodec *pCodec = QTextCodec::codecForName("GBK");
        QString strPrint = pCodec->toUnicode("发送数据失败");
        strPrint += ", last error code is "+QString::number(iErr);
        _funcShowMsg(strPrint);
        return false;
    }
    return true;
}

  2.8、thread_z.h

#ifndef THREAD_Z_H
#define THREAD_Z_H

void Thread_RECV(void *_ArgList);

#endif // THREAD_Z_H

  2.9、thread_z.cpp

#include "thread_z.h"

#include <QDebug>

#include <Windows.h>
#include <process.h>

#include "PassInfo_z.h"
#include "SeriesPort_z.h"

int g_iIdx = 0;
bool MsgHandler01(HWND _hWnd, DWORD _dwRecv, char* _pc);

void Thread_RECV(void *_ArgList)
{
//    ::MessageBoxA(0, "Thread_RECV(...) in ", "", 0);

    HWND hWnd = (HWND)_ArgList;
    if (hWnd == 0)
    {
        qDebug() << "Thread_RECV(...) - hWnd is NULL .";
        _endthread();
        return;
    }
    if (g_hCom == 0)
    {
        Info_Send_pc(hWnd, "Thread_RECV(...) - g_hCom is 0 .");
        _endthread();
        return;
    }

    Info_Send_pc(hWnd, "Thread_RECV(...) in");
    qDebug() << "Thread_RECV(...) in";

    //清空串口
    PurgeComm(g_hCom, PURGE_RXCLEAR | PURGE_TXCLEAR);

    char buf[SERIES_PORT__OUT_QUEUE_SIZE] = {0};
    char bufErr[256] = {0};
    while (true)
    {
//        g_iIdx ++;
//        if (g_iIdx > 10)
//            break;

        BOOL bRtn = false;

        DWORD dwMask = EV_RXFLAG;//EV_RXCHAR;
        bRtn = WaitCommEvent(g_hCom, &dwMask, NULL);
        if (! bRtn)
        {
            int iErr = ::GetLastError();
            sprintf_s(bufErr, sizeof(bufErr), "WaitCommEvent(...) return false, last error code is %d .", iErr);
            Info_Send_pc(hWnd, bufErr);
            break;
        }

        DWORD dwRead;
        bRtn = ReadFile(g_hCom, &buf[0], SERIES_PORT__OUT_QUEUE_SIZE, &dwRead, NULL);
//                {
//                    char msg[128] = {0};
//                    sprintf_s(msg, sizeof(msg), "%d - %d", g_iIdx, dwRead);
//                    Info_Send_pc(hWnd, msg);
//                }
        if (! bRtn)
        {
            int iErr = ::GetLastError();
            sprintf_s(bufErr, sizeof(bufErr), "ReadFile(...) return false, last error code is %d .", iErr);
            Info_Send_pc(hWnd, bufErr);
            break;
        }

        if (dwRead > 0)
            if (! MsgHandler01(hWnd, dwRead, buf))
                break;
    }

    Info_Send_pc(hWnd, "Thread_RECV(...) out");
    qDebug() << "Thread_RECV(...) out";

    /* _endthread given to terminate */
    _endthread();
}

// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

char g_infoRecv[1024 * 4] = {0};
DWORD g_dwInfoRecvCnt = 0;

bool MsgHandler01(HWND _hWnd, DWORD _dwRecv, char* _pc)
{
    //char buf1[64] = {0};


    memcpy(&g_infoRecv[g_dwInfoRecvCnt], _pc, _dwRecv);
    g_dwInfoRecvCnt += _dwRecv;

    //Info_Send_pc(_hWnd, "11");
    void* p = memchr(&g_infoRecv[0], '\n', g_dwInfoRecvCnt);
    if (p != NULL)
    {
        //Info_Send_pc(_hWnd, "12");
        DWORD dwBegin = (DWORD)( &g_infoRecv[0] );
        DWORD dwPos = (DWORD)p;
        int iLen = dwPos - dwBegin - 1;// \r\n 为占2个字节位置,需要将它们占的位置去掉
        //Info_Send_pc(_hWnd, "13");
        if (iLen < 0)
        {
            char bufErr[64] = {0};
            sprintf_s(bufErr, sizeof(bufErr), "MsgHandler01(...) - (dwLen < 0) : %d", iLen);
            Info_Send_pc(_hWnd, bufErr);
            return false;
        }

        //sprintf_s(buf1, sizeof(buf1), "14 : %d, %d, %d, %d", iLen, g_dwInfoRecvCnt, dwBegin, dwPos);
        //Info_Send_pc(_hWnd, buf1);
        Info_Send(_hWnd, iLen, &g_infoRecv[0]);
        //Info_Send_pc(_hWnd, "15");
        memcpy( &g_infoRecv[0], &g_infoRecv[iLen + 2], g_dwInfoRecvCnt - (iLen+2) );
        //Info_Send_pc(_hWnd, "16");
        g_dwInfoRecvCnt -= (iLen+2);
        //Info_Send_pc(_hWnd, "17");
    }
    return true;
}

 

3、

4、

5、

 

posted @ 2018-09-29 14:56  CppSkill  阅读(386)  评论(0编辑  收藏  举报