QT開發之旅三串口設備調試工具


這里首先說明一下,這個為什么叫串口設備調試工具而不是串口調試工具,是因為這個工具比網絡上的串口調試工具多出了一些真實需要的用來調試設備的功能,首先一點就是大部分的串口調試工具收到數據都是立即返回,這樣的數據都是連着的,頭一條數據和后一條數據頭尾相連,對於調試着來說要看數據非常麻煩,而且在不斷有數據過來時,要停下來看數據除非關閉串口,而不能在打開串口的時候看數據,因為不斷有數據過來沖掉前面的數據顯示,甚至有些還不能最大化等等情況,這個工具是根據將近四年的與硬件通信這塊開發工具的切身實際需求量身定做的,還能模擬設備立即回復數據。之前很多QT開發的版本源碼分成兩套,一套在windows下編譯,一套在linux下編譯,甚至麻煩,雖然QT5.1之后的版本自帶了串口通信類,本人測試過,貌似還有小問題,在快速收發數據時容易卡住,這次帶來的源碼不用做任何改動即可在多個平台編譯。

項目名稱:串口設備調試工具

開發環境:WIN7+QT4.7+QT CREATOR2.8+MINGW

已編譯通過測試平台:XPWin7ubuntutiny210

技術實現:通過第三方串口通信類,解析協議並作出處理

基本功能:

1:支持16進制數據發送與接收。

2:支持windowsCOM9以上的串口通信。

3:自動加載對應操作系統串口號。

4:實時顯示收發數據字節大小以及串口狀態。

高級功能:

1:可自由管理需要發送的數據,每次只要從下拉框中選擇數據即可,無需重新輸入數據。

2:可模擬設備回復數據,需要在主界面開啟模擬設備回復數據。當接收到設置好的指令時,立即回復設置的回復指令。例如指定收到0x16 0x00 0xFF 0x01需要回復0x16 0x00 0xFE 0x01,則只需要在SendData.txt中添加一條數據16 00 FF 01:16 00 FE 01即可。

3:可定時發送數據和保存數據到文本文件:,默認間隔5秒鍾,可更改間隔時間。

4:在不斷接收到大量數據時,可以暫停顯示數據來查看具體數據,后台依然接收數據但不處理,無需關閉串口來查看已接收到的數據。

5:每次收到的數據都是完整的一條數據,而不是脫節的,做了延時處理。

6:一套源碼隨處編譯,無需更改串口通信類,已在XP/WIN7/UBUNTU/ARMLINUX系統下成功編譯並運行。

如果有更好的建議或者意見,請Q(517216493),謝謝!

運行截圖:

粗略步驟:

第一步:布局好界面,控件命名好,建議用pascal命名法。

第二步:准備unixwindows串口通信第三方類qextserialport.hqextserialport.cppqextserialport_global.hqextserialport_p.hqextserialport_unix.cppqextserialport_win.cpp

導入到工程,在pro文件中這樣寫:

QT       += core gui

 

TARGET = mySerialPortTools

TEMPLATE = app

 

 

SOURCES += main.cpp\

        qextserialport.cpp\

        frmmain.cpp

 

HEADERS += frmmain.h \

        qextserialport_global.h \

        qextserialport.h \

        myhelper.h

 

win32 { SOURCES += qextserialport_win.cpp }

unix { SOURCES += qextserialport_unix.cpp }

 

FORMS += frmmain.ui

 

RESOURCES += \

    main.qrc

 

MOC_DIR=temp/moc

RCC_DIR=temp/rcc

UI_DIR=temp/ui

OBJECTS_DIR=temp/obj

DESTDIR=bin

 

win32:RC_FILE=main.rc

 

CONFIG += warn_off      #關閉警告

 

這樣的話在不同平台下會自動加載對應平台的cpp實現文件來編譯。

第三步:初始化主界面,自動加載對應串口號波特率等信息。

void frmMain::InitForm()
{    
    ReceiveCount=0;
    SendCount=0;
    IsShow=true;
    IsAutoClear=false;
    IsHexSend=true;
    IsHexReceive=true;
    IsDebug=false;

    QStringList comList;//串口號
    QStringList baudList;//波特率
    QStringList parityList;//校驗位
    QStringList dataBitsList;//數據位
    QStringList stopBitsList;//停止位

#ifdef Q_OS_WIN//如果是windows系統
    comList<<"COM1"<<"COM2"<<"COM3"<<"COM4"<<"COM5"<<"COM6"
          <<"COM7"<<"COM8"<<"COM9"<<"COM10"<<"COM11"<<"COM12"
         <<"COM13"<<"COM14"<<"COM15";
#else//如果是unix或者其他系統
    comList<<"ttyUSB0"<<"ttyUSB1"<<"ttyUSB2"<<"ttyUSB3"<<"ttyUSB4"<<"ttyUSB5"
          <<"ttyS0"<<"ttyS1"<<"ttyS2"<<"ttyS3"<<"ttyS4"<<"ttyS5"<<"ttyS6"
         <<"ttyS7"<<"ttyS8"<<"ttyS9";
#endif

    ui->cboxPortName->addItems(comList);
    ui->cboxPortName->setCurrentIndex(0);

    baudList<<"50"<<"75"<<"100"<<"134"<<"150"<<"200"<<"300"
           <<"600"<<"1200"<<"1800"<<"2400"<<"4800"<<"9600"
          <<"14400"<<"19200"<<"38400"<<"56000"<<"57600"
         <<"76800"<<"115200"<<"128000"<<"256000";

    ui->cboxBaudRate->addItems(baudList);
    ui->cboxBaudRate->setCurrentIndex(12);

    parityList<<"無"<<"奇"<<"偶";

#ifdef Q_OS_WIN//如果是windows系統
    parityList<<"標志";
#endif

    parityList<<"空格";

    ui->cboxParity->addItems(parityList);
    ui->cboxParity->setCurrentIndex(0);

    dataBitsList<<"5"<<"6"<<"7"<<"8";
    ui->cboxDataBit->addItems(dataBitsList);
    ui->cboxDataBit->setCurrentIndex(3);

    stopBitsList<<"1";

#ifdef Q_OS_WIN//如果是windows系統
    stopBitsList<<"1.5";
#endif

    stopBitsList<<"2";

    ui->cboxStopBit->addItems(stopBitsList);
    ui->cboxStopBit->setCurrentIndex(0);

    //讀取數據(采用定時器讀取數據,不采用事件,方便移植到linux)
    myReadTimer=new QTimer(this);
    myReadTimer->setInterval(300);
    connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));

    //發送數據
    mySendTimer=new QTimer(this);
    mySendTimer->setInterval(5000);
    connect(mySendTimer,SIGNAL(timeout()),this,SLOT(WriteMyCom()));

    //保存數據
    mySaveTimer=new QTimer(this);
    mySaveTimer->setInterval(5000);
    connect(mySaveTimer,SIGNAL(timeout()),this,SLOT(SaveMyCom()));

    //顯示日期時間
    myTimer=new QTimer(this);
    myTimer->start(1000);
    connect(myTimer,SIGNAL(timeout()),this,SLOT(SetTime()));

    QDate dateNow=QDate::currentDate();
    ui->labDate->setText(QString("日期:%1").arg(dateNow.toString("yyyy年MM月dd日 dddd")));

    for (int i=1;i<=60;i++)
    {
        ui->cboxSend->addItem(QString::number(i)+"秒");
        ui->cboxSave->addItem(QString::number(i)+"秒");
    }

    ui->cboxSave->setCurrentIndex(4);
    ui->cboxSend->setCurrentIndex(4);

    ui->cboxSend->setEnabled(false);
    ui->cboxSave->setEnabled(false);

    this->ChangeEnable(false);
    this->ReadConfigData();//讀取發送數據加載到下拉框
    this->ReadSendData();//讀取數據轉發文件

    ui->txtSend->installEventFilter(this);//安裝監聽器監聽發送數據框回車響應
}

第四步:采用定時器讀取串口數據,其實也可以采用事件機制,在幾大平台也測試通過事件來收數據也可以,但是在windows下在快速收發大量數據時候居然會卡住,同樣的代碼在linux表現很好,不明原因,后面采用定時器讀取機制,問題就沒有了。

void frmMain::ReadMyCom()
{
    //這個判斷尤為重要,否則的話直接延時再接收數據,空閑時和會出現高內存占用
    if (myCom->bytesAvailable()<=0){return;}

    myHelper::Sleep(100);//延時100毫秒保證接收到的是一條完整的數據,而不是脫節的
    QByteArray buffer=myCom->readAll();

    if (IsShow)
    {
        if (IsHexReceive)
        {
            QString tempDataHex=myHelper::ByteArrayToHexStr(buffer);
            ui->txtDataHex->append(QString("接收:%1 時間:%2")
                                   .arg(tempDataHex)
                                   .arg(QTime::currentTime().toString("HH:mm:ss")));

            if (IsDebug)//2013-8-6增加接收數據后轉發數據,模擬設備
            {
                foreach(QString tempData,SendDataList)
                {
                    QStringList temp=tempData.split(';');
                    if (tempDataHex==temp[0])
                    {
                        //這里沒有跳出循環,有可能一條數據會對應多條數據需要轉發
                        myCom->write(myHelper::HexStrToByteArray(temp[1]));
                    }
                }
            }
        }
        else
        {
            QString tempDataNormal=QString(buffer);
            ui->txtDataHex->append(QString("接收:%1 時間:%2")
                                   .arg(tempDataNormal)
                                   .arg(QTime::currentTime().toString("HH:mm:ss")));

            if (IsDebug)//2013-8-6增加接收數據后轉發數據,模擬設備
            {
                foreach(QString tempData,SendDataList)
                {
                    QStringList temp=tempData.split(';');
                    if (tempDataNormal==temp[0])
                    {
                        //這里沒有跳出循環,有可能一條數據會對應多條數據需要轉發
                        myCom->write(temp[1].toAscii());
                    }
                }
            }
        }

        ReceiveCount=ReceiveCount+buffer.size();
        ui->labReceive->setText(QString("接收:%1 字節").arg(ReceiveCount));
    }
}
void frmMain::WriteMyCom()
{
    QString str=ui->txtSend->currentText();
    if (str==""){ui->txtSend->setFocus();return;}//發送數據為空
    if (!myCom->isOpen()) { return; }//串口沒有打開

    QByteArray outData=str.toAscii();
    int size=outData.size();

    if (IsHexSend)//轉化為16進制發送
    {
        outData=myHelper::HexStrToByteArray(str);
        size=outData.size();
        myCom->write(outData);
    }
    else
    {
        size=outData.size();
        myCom->write(outData);
    }

    ui->txtDataHex->append(QString("發送:%1 時間:%2")
                           .arg(str)
                           .arg(QTime::currentTime().toString("HH:mm:ss")));

    SendCount=SendCount+size;
    ui->labSend->setText(QString("發送:%1 字節").arg(SendCount));

    if (IsAutoClear)
    {
        ui->txtSend->setCurrentIndex(-1);
        ui->txtSend->setFocus();
    }
}

void frmMain::SaveMyCom()
{
    QString tempData=ui->txtDataHex->toPlainText();
    if (tempData==""){return;}//如果沒有內容則不保存

    QDateTime now=QDateTime::currentDateTime();
    QString name=now.toString("yyyyMMddHHmmss");
    QString fileName=name+".txt";

    QFile file(fileName);
    file.open(QFile::WriteOnly | QIODevice::Text);
    QTextStream out(&file);
    out<<tempData;
    file.close();
}

這里有個細節說下,就是在接收數據函數里面,增加了一個判斷if (myCom->bytesAvailable()<=0){return;}如果不這樣的話,每次定時讀取都會延時,內存占用很高,當然,如果采用事件機制的話,這里不需要任何延時或者判斷。

可執行文件下載地址:http://download.csdn.net/detail/feiyangqingyun/6745003

源碼猛點這里:http://download.csdn.net/detail/feiyangqingyun/6745011 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM