Qt practice record: serial debugging assistant

Since the project requires the use of serial debugging and testing, in order to practice hand, use Qt to write a serial debugging assistant. This article describes a simple press development process, and also relates to a module used in the code section. Detailed code reference source repository.

Tool Properties

Specific features

  • It includes a serial transceiver function.
  • Serial port parameters. Default 115200, N, 8,1
  • Empty reception area, reception area hexadecimal display, reception area timestamp.
  • Clear transmitting region, transmission hexadecimal, is automatically added \r\n, the timing of transmission.
  • And a transceiver count display is cleared.
  • Automatic detection of serial devices. Ready to run before the serial port is automatically turned on. Serial insert operation does not automatically open. During operation the device will automatically shut down the serial removed.

Known Bug

Receiving display area timestamp imperfection.
When the serial port to send a large number of garbled, the program will crash. Garbled gibberish may be true, it could be an error to set the baud rate.

Qt knowledge

  • MainWindow design.
  • Qt serial class.
  • Common controls: buttons, check boxes, text edit box controls for the map. Applications logo.
  • Qt hot plug detection device (Windows).

Run 1 shows the results:
Here Insert Picture Description
Figure 1

development process

Project-related

Qt is used in the serial class QSerialPort, we need to add the corresponding library in the project file, as follows:

QT       += core gui serialport

logo icon, note the ico format:

RC_ICONS = images/logo.ico

Picture resource file:

RESOURCES += \
    images.qrc

USB device detection relies on libraries:

win32: LIBS += -lSetupAPI -luser32

Signals and slots

Add in Qt Creator of controls, right-click on the control and select "Go trough ...", select the appropriate slot and click "OK", can automatically add the function declaration tank and automatically jump to the slot function implementation code. The system automatically added to the form on_<控件对象名>_<控件事件名>, such as a button click event to open the serial channel function on_btnOpen_clicked. There are similar on_cbPortName_currentTextChanged(to change the serial devices), on_ckRecvHex_stateChanged(receiving hex box change), and so on.
This mechanism and operation, may be analogous to the design and MFC interface response message. In fact, I like the slot function called the response function.

Serial correlation

Serial class declaration:

#include <QSerialPort>
#include <QSerialPortInfo>

QSerialPort serial;

Native serial device enumeration:

QSerialPortInfo::availablePorts()

Serial port parameters:

serial.setPortName("com4"); // 串口名称
serial.setBaudRate(115200); // 串口波特率
serial.setDataBits(QSerialPort::Data8); // 数据位
serial.setStopBits(QSerialPort::OneStop); // 停止位
serial.setParity(QSerialPort::NoParity); // 校验位
serial.setFlowControl(QSerialPort::NoFlowControl); // 流控

Note: Qt seems that only no flow control, software flow control, hardware flow control these three, can not distinguish between RTS, DTR.

Serial opening, closing:

serial.open(QIODevice::ReadWrite);
serial.close();

Serial data transmission:

 QByteArray sendData;
 serial.write(sendData);

Serial data reception:

// 串口数据到来时,会触发QSerialPort::readyRead事件,添加相应的响应函数
QObject::connect(&serial, &QSerialPort::readyRead, this, &MainWindow::readyRead);

void MainWindow::readyRead()
{
    QByteArray buffer = serial.readAll();
}

Note that the serial data type QByteArray.

Automatic detection USB

Given the current most applications using a USB serial cable, so add detection of USB devices. When this USB device is detected, then using the QSerialPortInfo::availablePorts()detected serial devices.

#include <windows.h>
#include <WinUser.h>
#include <Dbt.h>
#include <devguid.h>
#include <SetupAPI.h>
#include <InitGuid.h>

    static const GUID GUID_DEVINTERFACE_LIST[] =
    {
    // GUID_DEVINTERFACE_USB_DEVICE
    { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } },
    // GUID_DEVINTERFACE_DISK
    { 0x53f56307, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } },
    // GUID_DEVINTERFACE_HID,
    { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } },
    // GUID_NDIS_LAN_CLASS
    { 0xad498944, 0x762f, 0x11d0, { 0x8d, 0xcb, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c } }
    //// GUID_DEVINTERFACE_COMPORT
    //{ 0x86e0d1e0, 0x8089, 0x11d0, { 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73 } },
    //// GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR
    //{ 0x4D36E978, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } },
    //// GUID_DEVINTERFACE_PARALLEL
    //{ 0x97F76EF0, 0xF883, 0x11D0, { 0xAF, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x84, 0x5C } },
    //// GUID_DEVINTERFACE_PARCLASS
    //{ 0x811FC6A5, 0xF728, 0x11D0, { 0xA5, 0x37, 0x00, 0x00, 0xF8, 0x75, 0x3E, 0xD1 } }
    };

    //注册插拔事件
    HDEVNOTIFY hDevNotify;
    DEV_BROADCAST_DEVICEINTERFACE NotifacationFiler;
    ZeroMemory(&NotifacationFiler,sizeof(DEV_BROADCAST_DEVICEINTERFACE));
    NotifacationFiler.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    NotifacationFiler.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;

    for(int i=0;i<sizeof(GUID_DEVINTERFACE_LIST)/sizeof(GUID);i++)
    {
        NotifacationFiler.dbcc_classguid = GUID_DEVINTERFACE_LIST[i];//GetCurrentUSBGUID();//m_usb->GetDriverGUID();

        hDevNotify = RegisterDeviceNotification((HANDLE)this->winId(),&NotifacationFiler,DEVICE_NOTIFY_WINDOW_HANDLE);
        if(!hDevNotify)
        {
            DWORD Err = GetLastError();
        }
    }

In response to nativeEventthe event:

bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
    MSG* msg = reinterpret_cast<MSG*>(message);
    int msgType = msg->message;
    if(msgType==WM_DEVICECHANGE) // 设备插入事件
    {
        PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam;
        switch (msg->wParam) {
        case DBT_DEVICEARRIVAL:
            if(lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
            {
                //PDEV_BROADCAST_DEVICEINTERFACE pDevInf  = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
                //QString strname = QString::fromWCharArray(pDevInf->dbcc_name,pDevInf->dbcc_size);
                //qDebug() << "arrive" + strname;
                printDebugInfo("USB device arrive");
                emit sig_deviceChanged(1);
            }
            break;
        case DBT_DEVICEREMOVECOMPLETE:  // 设备移除事件
            if(lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
            {
                //PDEV_BROADCAST_DEVICEINTERFACE pDevInf  = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
                //QString strname = QString::fromWCharArray(pDevInf->dbcc_name,pDevInf->dbcc_size);
                printDebugInfo("USB device removed");
                emit sig_deviceChanged(0);
            }
            break;
        }
    }
    return false;
}

As used herein, the custom signals sig_deviceChangedconnected to the function on_deviceChanged:

QObject::connect(this, &MainWindow::sig_deviceChanged, this, &MainWindow::on_deviceChanged);

void MainWindow::on_deviceChanged(int flag)
{
    if (flag == 1)
    {
        foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
        {
            if (-1 == ui->cbPortName->findText(info.portName()))
                ui->cbPortName->addItem(info.portName());
        }
    }
    else
    {
        serial.close();
        ui->btnOpen->setText(tr("打开串口"));
        ui->btnOpen->setIcon(QIcon(":images/notopened.ico"));
    }
}

Interface logic

interface design

Interface uses designers to design, shown in Figure 2.
Here Insert Picture Description
figure 2

Interface Basic Settings

In initMainWindowto the basic settings window function, such as title, window size, minimize maximize button, and the like.

// 对主窗口的初始化
void MainWindow::initMainWindow()
{
    setWindowTitle(tr("QtSerialPort"));
    setMinimumSize(480, 320);

    Qt::WindowFlags winFlags  = Qt::Dialog;
    winFlags = winFlags | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint;

    setWindowFlags(winFlags);
}

Status Bar

The status bar is mainly used for debugging information, prompt display of information, the transceiver additionally display the count (and cleared).
The status bar correlation function and variable declarations are as follows:

    void initStatusBar();

    int m_rxCnt;
    int m_txCnt;

    // 状态栏相关
    QLabel* m_stsPinned;
    QLabel* m_stsDebugInfo;
    QLabel* m_stsRx;
    QLabel* m_stsTx;
    QLabel* m_stsResetCnt;
    QLabel* m_stsCopyright;

    QLabel* m_stsExit;

Initialization function as follows:

void MainWindow::initStatusBar()
{
    // 状态栏分别为:
    // 提示信息(可多个)
    // RX、TX
    // 版本信息(或版权声明)
    // 退出图标
    ui->statusbar->setMinimumHeight(22);
    //ui->statusbar->setStyleSheet(QString("QStatusBar::item{border: 0px}")); // 不显示边框
    ui->statusbar->setSizeGripEnabled(false);//去掉状态栏右下角的三角

    m_stsDebugInfo = new QLabel();
    m_stsRx = new QLabel();
    m_stsTx = new QLabel();
    m_stsResetCnt = new QLabel();
    m_stsCopyright = new QLabel();
    m_stsExit = new QLabel();

    m_stsDebugInfo->setMinimumWidth(this->width()/2);
    ui->statusbar->addWidget(m_stsDebugInfo);

    m_stsRx->setMinimumWidth(64);
    ui->statusbar->addWidget(m_stsRx);
    m_stsRx->setText("RX: 0");

    m_stsTx->setMinimumWidth(64);
    ui->statusbar->addWidget(m_stsTx);
    m_stsTx->setText("TX: 0");

    m_stsResetCnt->installEventFilter(this);
    m_stsResetCnt->setFrameStyle(QFrame::Plain);
    m_stsResetCnt->setText("复位计数");
    m_stsResetCnt->setMinimumWidth(32);
    ui->statusbar->addWidget(m_stsResetCnt);

    printDebugInfo("欢迎使用");
    // 版权信息
    m_stsCopyright->setFrameStyle(QFrame::NoFrame);
    m_stsCopyright->setText(tr("  <a href=\"https://www.latelee.org\">技术主页</a>  "));
    m_stsCopyright->setOpenExternalLinks(true);
    ui->statusbar->addPermanentWidget(m_stsCopyright);

    // 退出图标
    m_stsExit->installEventFilter(this); // 安装事件过滤,以便获取其单击事件
    m_stsExit->setToolTip("Exit App");
    m_stsExit->setMinimumWidth(32);
    // 贴图
    QPixmap exitIcon(":/images/exit.png");
    m_stsExit->setPixmap(exitIcon);
    ui->statusbar->addPermanentWidget(m_stsExit);

    connect(this, &MainWindow::sig_exit, qApp, &QApplication::quit); // 直接关联到全局的退出槽
}

In response to the status bar control events need to override and implement the function eventFilter:

bool eventFilter(QObject *watched, QEvent *event);


bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
    if (watched == m_stsExit) // 程序退出
    {
        //判断事件
        if(event->type() == QEvent::MouseButtonPress)
        {
            // TODO:直接退出还是发信号?
            emit sig_exit();
            return true; // 事件处理完毕
        }
        else
        {
            return false;
        }
    }
    else if (watched == m_stsResetCnt)
    {
        if(event->type() == QEvent::MouseButtonPress)
        {
            m_stsRx->setText("RX: 0");
            m_stsTx->setText("TX: 0");
            m_rxCnt = m_txCnt = 0;
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return QWidget::eventFilter(watched, event);
    }
}

There are currently handling the event, click the Exit icon, and click cleared count QLabel.

Controls for the map

New Resource file images.qrc, as follows, and then add the file in Qt Creator:

<RCC>
    <qresource prefix="/">
        <file>images/logo.jpg</file>
        <file>images/notopened.ico</file>
        <file>images/opened.ico</file>
        <file>images/unpinned.bmp</file>
        <file>images/pinned.bmp</file>
        <file>images/exit.png</file>
    </qresource>
</RCC>

You can also create a new resource file in Qt Creator, the right to add a picture. The same effect.
Button to open the serial port, for example, set the text and icons code is as follows:

    ui->btnOpen->setText(tr("打开串口"));
    ui->btnOpen->setIconSize(ui->btnOpen->rect().size());
    ui->btnOpen->setIcon(QIcon(":images/notopened.ico"));

Note: The resource file qrc the prefix /, images directory under the project, using the parameters of the form QIcon :images/xxx.

QComboBox

Serial QComboBox control parameters, add data to facilitate the use QStringList class reuse addItems add items. Initialized as follows:

    QStringList list;
    list.clear();
    list << "2400" << "4800" << "9600" << "14400" << \
         "19200" << "38400" << "43000" << "57600" << "76800" << \
         "115200" << "230400" << "256000" << "460800" << "921600";
    ui->cbBaudrate->addItems(list);
    ui->cbBaudrate->setCurrentText(tr("115200"));

    list.clear();
    list << "5" << "6" << "7" << "8";
    ui->cbDatabit->addItems(list);
    ui->cbDatabit->setCurrentText(tr("8"));

    list.clear();
    list << "1" << "1.5" << "2";
    ui->cbStopbit->addItems(list);
    ui->cbStopbit->setCurrentText(tr("1"));

    list.clear();
    list << "none" << "odd" << "even";
    ui->cbParity->addItems(list);
    ui->cbParity->setCurrentText(tr("none"));

    list.clear();
    list << "off" << "hardware" << "software";
    ui->cbFlow->addItems(list);
    ui->cbFlow->setCurrentText(tr("off"));

Serial port parameters are automatically updated

When the serial port is open, you can change the parameters in real time, with the exception of serial devices. QComboBox of response currentTextChangedor currentIndexChangedevent. as follows:

// 串口设备直接用文本文字形式即可
void MainWindow::on_cbPortName_currentTextChanged(const QString &arg1)
{
    serial.setPortName(arg1);
}

// 如停止位等,需要用索引转换
void MainWindow::on_cbStopbit_currentIndexChanged(int index)
{
    //qDebug()<< index;
    //设置停止位
    switch(index)
    {
    case 0: serial.setStopBits(QSerialPort::OneStop); break;
    case 1: serial.setStopBits(QSerialPort::OneAndHalfStop); break;
    case 2: serial.setStopBits(QSerialPort::TwoStop); break;
    default: break;
    }
}

QCheckBox

Box for transmitting, the characteristics of the display settings. The hexadecimal transmission, display, transmission timings, and the like. Using the logo design variable transmission is performed, it is determined to be displayed. The main response QCheckBox of stateChangedfunctions.

void MainWindow::on_ckRecvHex_stateChanged(int arg1)
{
    if (arg1 == Qt::Checked)
    {
        m_recvHex = 1;
    }
    else if (arg1 == Qt::Unchecked)
    {
        m_recvHex = 0;
    }
}

Timer

Overloaded timerEventfunction:

#include <QTimer>

void timerEvent(QTimerEvent *event);

Function to achieve:

void MainWindow::timerEvent(QTimerEvent *event)
{
    //qDebug() << "Timer ID:" << event->timerId();
    sendData();
}

Open, stop the timer:

m_sendTimerId = startTimer(ui->txtInterval->text().toInt());

killTimer(m_sendTimerId);

Since this procedure uses only one timer, it is not determined event->timerId().

Hex

To facilitate debugging tools supports character, hexadecimal data transmission and display. "Hex string" string like transfer function set as follows:


int hexStringToString(QString& hexStr, QString& str)
{
    int ret = 0;
    bool ok;
    QByteArray retByte;
    hexStr = hexStr.trimmed();
    hexStr = hexStr.simplified();
    QStringList sl = hexStr.split(" ");

    foreach (QString s, sl)
    {
        if(!s.isEmpty())
        {
            char c = (s.toInt(&ok,16))&0xFF;
            if (ok)
            {
                retByte.append(c);
            }
            else
            {
                ret = -1;
            }
        }
    }

    str = retByte;

    return ret;
}

int hexStringToHexArray(QString& hexStr, QByteArray& arr)
{
    int ret = 0;
    bool ok;
    hexStr = hexStr.trimmed();
    hexStr = hexStr.simplified();
    QStringList sl = hexStr.split(" ");

    foreach (QString s, sl)
    {
        if(!s.isEmpty())
        {
            char c = (s.toInt(&ok,16))&0xFF;
            if (ok)
            {
                arr.append(c);
            }
            else
            {
                ret = -1;
            }
        }
    }

    return ret;
}

int hexArrayToString(QByteArray& hexArr, QString& str)
{
    int ret = 0;
    str = hexArr.toHex(' ').toLower();
    return ret;
}

other

In fact, the program is not difficult, especially in the operation of the serial class, because QSerialPortprovides a very friendly and convenient interface serial port settings, send and receive. If there's difficulty may lie in the design of the interface logic. The timing to transmit a single transmission, and controls the text icon display prompts, hexadecimal string between the conversion, and the like.
The author of this tool basis to achieve operation ESP8266 comprising indicating LED lamp, relay, and restore the factory operating mode function test verification operation. Since this article is not associated with, not to be reopened.

Code repository

Warehouse code to prevail, this does not necessarily include all. This works all the source code can be freely used autonomously, including, but not limited to, add, delete, modify, commercial, personal use. The resulting outcomes / consequences almost nothing to do with the author. Limited to the level of ability, this program without any quality assurance, the author of the program no obligation to provide the service.

Warehouse address here .

Published 481 original articles · won praise 244 · Views 1.1 million +

Guess you like

Origin blog.csdn.net/subfate/article/details/104107132