串口.Qt532測試(同步)

環境:Win7x6四、Qt5.3.2 MSVC OpenGL(x86)、vs2010(x86)windows

ZC:這裏的例子是 同步的函數操做,貌似 若是子線程在等待 WaitCommEvent(...)或ReadFile(...) 返回的話(即 串口句柄正在被使用中),界面主線程執行 CloseHandle(...) 或 SetCommMask(...) 的話,就會卡在那裏...    因而 考慮改用 異步方式緩存

  ZC:想到 一個方式,使用同步方式的時候 可使用 強制關閉線程的方式「TerminateThread(線程句柄, ExitCode);」 來關閉子線程,這樣就不會再佔用 串口句柄了,CloseHandle(..)也能夠順利執行。問題:TerminateThread(...) 會有一些 動態申請的資源釋放,獲取的鎖釋放 等的問題(具體看MSDN的說明,裏面還提到了"heap lock",因而在 申請內存的過程當中 若是強制結束線程的話 堆鎖未釋放 別的線程再申請內存的時候就卡在那裏了...),還有 在 XP以及以前的Windws版本OS中 強制結束線程 OS不會釋放 它的初始棧,形成內存泄漏(我看了一下 XP的資源管理器,確實是這樣的現象)。app

 

一、ZC:異步

 1.一、開始時,遇到的問題:在接收信息的線程中,ReadFile(...) 每次都是 當即返回的  可是獲取的數據都是0字節的長度,現象就好像是 用了 異步的方式 沒等到事件 就當即返回了...ide

  找到問題:(1)、查看了 CreateFile(...)的參數,發現使用的就是 同步的方式(沒有指定 參數 FILE_FLAG_OVERLAPPED)函數

       (2)、後來 發現是 COMMTIMEOUTS 設置的不正確的緣故測試

  測試下來,有2種方式 設置COMMTIMEOUTS,來 解決上面的問題:ui

    ①、相似以下的參數設置:this

         COMMTIMEOUTS TimeOuts;
         //設定讀超時
         TimeOuts.ReadIntervalTimeout = 100;
         TimeOuts.ReadTotalTimeoutMultiplier = 5000;
         TimeOuts.ReadTotalTimeoutConstant = 5000;
         //設定寫超時
         TimeOuts.WriteTotalTimeoutMultiplier = 500;
         TimeOuts.WriteTotalTimeoutConstant = 2000;

    ②、這樣設置:spa

         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,一、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.二、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.三、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.四、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.五、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.六、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.七、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.八、thread_z.h

#ifndef THREAD_Z_H
#define THREAD_Z_H

void Thread_RECV(void *_ArgList);

#endif // THREAD_Z_H

  2.九、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;
}

 

三、

四、

五、

相關文章
相關標籤/搜索