Qt编写串口通信、调试的小工具

前言

项目中在中标麒麟系统(Linux)上,通过cpci插槽,连接GPS板卡,板卡型号为BDM683,minicom的命令行对不熟悉Linux的人来说不够方便直观,所以仿照Windows上的一个串口调试助手界面,自己用Qt编写了一个小工具,方便不熟悉Linux系统的同事进行GPS板卡通信、调试工作。

思路

因为是根据Windows上的串口调试助手来编写的,所以思路会往这个软件上面靠。
Windows上的串口调试助手分了两个大功能,一个是串口的数据收发,一个是网口的数据收发,并且都支持以文本或者十六进制方式显示读取到的数据,也可以以文本或者十六进制方式存储读取到的数据到指定的文件中。
界面如下图所示:
Qt编写串口通信、调试的小工具_第1张图片
Linux系统中的串口以文件的形式在/dev目录下,文件名为tty*,这里,我不用以读文件的方式读写串口,用QT5提供的QSerialPort类读写串口,并且用connect函数把QSerialPort的信号和我们定义的槽函数连接起来。
勾选“接收转向文件”,弹出打开文件的对话框,直接在对话框中输入文件名,或者选择一个文件,即可把数据写入到文件中。
Qt编写串口通信、调试的小工具_第2张图片
勾选“16进制显示”,则通过QByteArray的toHex()函数,把数据的十六进制的值放到另一个缓存变量中,再转为QString,显示到文本框中。
网口接收数据方面,使用QUdpSocket,connect函数把QUdpSocket的信号和我们定义的槽函数连接起来。数据的处理、显示和串口一样。

代码

因为功能简单,代码都写在了主窗口类里了。
完整项目工程[https://github.com/yangyingle/SerialPorttAssistant]。
主窗口类的头文件中,声明了串口对象、缓存变量、udpSocket对象以及相关的槽函数。
mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

    QString endLine = "\r\n";

    //串口对象
    QSerialPort  *serialPort = NULL;
    QString serialPortName;
    //波特率
    int baudRate = 0;

    //缓存区
    QByteArray buffer_serialPort;
    //接收转向的文件
    QString serialPortRevDesFile = "";

    //UDP
    QUdpSocket *udpSocket;
	//远端IP和端口
    QString strLocalIP;
    qint16 localPort = 8080;

	//远端IP和端口
    QString strRemoteIP = "127.0.0.1";
    qint16 remotePort = 8080;

    //缓存区
    QByteArray buffer_netPort;
    //接收转向的文件
    QString netPortRevDesFile = "";

private slots:

    //串口接收数据槽函数
    void serialPort_rev();
    //串口发送按钮
    void on_pushButton_clicked();
    //打开串口按钮,并设置参数
    void on_pushButton_SerialPortOpen_clicked();
    //清除串口接收区
    void on_pushButton_serialPortRevClear_clicked();
    //网口开始监听按钮
    void on_pushButton_netStartListen_clicked();
    //串口计数复位
    void on_pushButton_serialPortResetCount_clicked();
    //串口接收转向文件
    void on_checkBox_serialPortRevToFile_clicked();
    //网口接收函数
    void netPort_Rev();
    //清除网口接受区
    void on_pushButton_netRevClear_clicked();
    //网口接收转向文件
    void on_checkBox_netPortRevToFile_clicked();
    //网口计数复位
    void on_pushButton_netPortResetCount_clicked();
    //串口发送按钮
    void on_pushButton_netPortSend_clicked();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

接下来是主窗口类的源文件,槽函数的实现。
mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    //初始化
    ui->comboBox_baudRate->setCurrentIndex(7);

    QRegExp ipRegexp = QRegExp("((2[0-4]]\\d|25[0-5]|[01]?\\d\\d?)\\.){4}");
    QRegExpValidator *ipRegExpValidator = new QRegExpValidator(ipRegexp,this);
    ui->lineEdit_localIP->setValidator(ipRegExpValidator);
    ui->lineEdit_localIP->setInputMask("000.000.000.000");
    ui->lineEdit_localIP->setText("127.0.0.1");

    QIntValidator *portValidator = new QIntValidator(0,65535,this);
    ui->lineEdit_localPort->setValidator(portValidator);
    ui->lineEdit_localPort->setText(QString::number(localPort));

    ui->lineEdit_remoteIP->setValidator(ipRegExpValidator);
    ui->lineEdit_remoteIP->setInputMask("000.000.000.000");
    ui->lineEdit_remoteIP->setText("127.0.0.1");

    ui->lineEdit_remotePort->setValidator(portValidator);
    ui->lineEdit_remotePort->setText(QString::number(remotePort));
}

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

//打开串口按钮,并设置参数
void MainWindow::on_pushButton_SerialPortOpen_clicked()
{
    //按键的打开串口、设置串口功能
    if(ui->pushButton_SerialPortOpen->text() == "打开")
    {
        //未打开串口时,打开串口,设置串口参数
        serialPort = new QSerialPort();
        serialPortName = ui->lineEdit_PortName->text();
        QString strBaudRate = ui->comboBox_baudRate->currentText();
        baudRate = strBaudRate.toInt();

        if(serialPort->isOpen())
        {
            serialPort->close();
        }

        serialPort->setPortName(serialPortName);
        bool bOK = serialPort->open(QIODevice::ReadWrite);
        if(!bOK)
        {
            QMessageBox::critical(this,"提示","无法打开串口,请检查是否被占用。",QMessageBox::Yes,QMessageBox::Yes);
            return;
        }
        //设置波特率
        serialPort->setBaudRate(baudRate);

        int index_parity = 0;
        index_parity = ui->comboBox_parity->currentIndex();
        int index_dataBit = 0;
        index_dataBit = ui->comboBox_dataBit->currentIndex();
        int index_stopBit = 0;
        index_stopBit = ui->comboBox_stopBit->currentIndex();

        //设置校验位
        switch(index_parity)
        {
            case 0:
                serialPort->setParity(QSerialPort::NoParity);
                break;
            case 1:
                serialPort->setParity(QSerialPort::OddParity);
                break;
            case 2:
                serialPort->setParity(QSerialPort::EvenParity);
                break;
            case 3:
                serialPort->setParity(QSerialPort::MarkParity);
                break;
            case 4:
                serialPort->setParity(QSerialPort::SpaceParity);
                break;
            default:
                serialPort->setParity(QSerialPort::NoParity);
                break;
        }

        //设置数据位
        switch(index_dataBit)
        {
            case 0:
                serialPort->setDataBits(QSerialPort::Data8);
                break;
            case 1:
                serialPort->setDataBits(QSerialPort::Data7);
                break;
            case 2:
                serialPort->setDataBits(QSerialPort::Data6);
                break;
            case 3:
                serialPort->setDataBits(QSerialPort::Data5);
                break;
            default:
                serialPort->setDataBits(QSerialPort::Data8);
                break;
        }

        //设置停止位
        switch(index_stopBit)
        {
            case 0:
                serialPort->setStopBits(QSerialPort::OneStop);
                break;
            case 1:
                serialPort->setStopBits(QSerialPort::OneAndHalfStop);
                break;
            case 2:
                serialPort->setStopBits(QSerialPort::TwoStop);
                break;
            default:
                serialPort->setStopBits(QSerialPort::OneStop);
                break;
        }

        serialPort->setFlowControl(QSerialPort::NoFlowControl);

        //连接信号和槽函数,串口有数据可读时,调用serialPort_rev()函数读取数据并处理。
        connect(serialPort,SIGNAL(readyRead()),this,SLOT(serialPort_rev()));
        //改变按键的文本
        ui->pushButton_SerialPortOpen->setText("关闭");
    }
    //按键的关闭串口功能
    else
    {
        //串口打开,则关闭串口
        if(serialPort->isOpen())
        {
            serialPort->close();
        }
        //释放串口对象
        delete serialPort;
        serialPort = NULL;
        //改变按键的文本
        ui->pushButton_SerialPortOpen->setText("打开");
    }

}

//串口接收数据槽函数
void MainWindow::serialPort_rev()
{
    //串口可读去数据的长度
    int byteLen = serialPort->bytesAvailable();

    if(byteLen < 0)
    {
        return;
    }
    //读取串口的数据到缓存变量中
    buffer_serialPort += serialPort->readAll();

    //接收是否转向文件
    if(ui->checkBox_serialPortRevToFile->isChecked())
    {
        //接收转向文件
        //16进制
        if(ui->checkBox_serialPortRevHex->isChecked())
        {
            //数据转换为16进制
            QByteArray bufferHex = buffer_serialPort.toHex();
            QString str_buffer = QString(bufferHex.toUpper());
            //使字符串的形式变为“AB 23 43 55”
            for(int i = str_buffer.count(); i > 0; i = i-2)
            {
                str_buffer.insert(i," ");
            }
            //把数据存到文件
            QFile fs(serialPortRevDesFile);
            if(fs.open(QFile::Append))
            {
                QTextStream ts(&fs);

                ts<label_serialPortRevCount->text().toInt();
        allCount = allCount + buffer_serialPort.count();
        ui->label_serialPortRevCount->setText(QString::number(allCount));
        buffer_serialPort.clear();
        return;
    }

    //接收不转向文件时,显示在界面上
    //16进制
    if(ui->checkBox_serialPortRevHex->isChecked())
    {
        //数据转换为16进制
        QByteArray bufferHex = buffer_serialPort.toHex();
        QString str_buffer = QString(bufferHex.toUpper());
        //使字符串的形式变为“AB 23 43 55”
        for(int i = str_buffer.count(); i > 0; i = i-2)
        {
            str_buffer.insert(i," ");
        }
        //光标移动到结尾
        ui->textEdit_SerialPortRev->moveCursor(QTextCursor::End);
        //显示数据
        ui->textEdit_SerialPortRev->insertPlainText(str_buffer);
        //光标移动到结尾
        ui->textEdit_SerialPortRev->moveCursor(QTextCursor::End);

    }
    //普通处理,即不转换为16进制
    else
    {
        //直接把数据转为字符串
        QString str_buffer = QString(buffer_serialPort);
        //光标移动到结尾
        ui->textEdit_SerialPortRev->moveCursor(QTextCursor::End);
        //显示数据
        ui->textEdit_SerialPortRev->insertPlainText(str_buffer);
        //光标移动到结尾
        ui->textEdit_SerialPortRev->moveCursor(QTextCursor::End);


    }
    //统计接收到的数据的长度,显示到界面上
    int allCount = ui->label_serialPortRevCount->text().toInt();
    allCount = allCount + buffer_serialPort.count();
    ui->label_serialPortRevCount->setText(QString::number(allCount));
    //清空缓存变量
    buffer_serialPort.clear();

}

//串口发送按钮
void MainWindow::on_pushButton_clicked()
{
    if(ui->pushButton_SerialPortOpen->text() == "打开")
    {
        QMessageBox::critical(this,"提示","串口未打开。",QMessageBox::Yes,QMessageBox::Yes);
        return;
    }
    //获取发送的命令,并在结尾加上换行,GPS板卡的命令结尾必须有换行
    QString command = ui->lineEdit_serialPortSend->text();
    if(ui->checkBox_endLine->isChecked())
    {
        command += endLine;
    }
    //将命令转换为16进制发送
    if(ui->checkBox_serialPortSendHex->isChecked())
    {
        QStringList commadList = command.split(' ');
        QByteArray byteArray;
        byteArray.resize(commadList.count());
        bool ok = false;
        for(int i = 0; i < commadList.count(); i++)
        {
            byteArray[i] = commadList.at(i).toInt(&ok,16);
        }

        serialPort->write(byteArray);
        QThread::msleep(30);
    }
    //直接发送
    else
    {
        serialPort->write(command.toUtf8().data());
        QThread::msleep(30);
    }
    //统计发送的长度
    int allCount = ui->label_serialPortSendCount->text().toInt();
    allCount = allCount + command.toUtf8().count();
    ui->label_serialPortSendCount->setText(QString::number(allCount));
}

//串口接收转向文件
void MainWindow::on_checkBox_serialPortRevToFile_clicked()
{
    //勾选“接收转向文件”时,弹出文件选择对话框,
    if(ui->checkBox_serialPortRevToFile->isChecked())
    {
        QFileDialog *fileDialog = new QFileDialog(this);
        fileDialog->setWindowTitle("选择一个文件");
        fileDialog->setAcceptMode(QFileDialog::AcceptOpen);
        fileDialog->setFileMode(QFileDialog::AnyFile);
        fileDialog->setViewMode(QFileDialog::Detail);
        fileDialog->setDirectory("/home/");

        QString desFile = "";
        if(fileDialog->exec() == QDialog::Accepted)
        {
            desFile = fileDialog->selectedFiles()[0];

        }
        //未选择文件或者直接关闭对话框,取消勾选,直接退出函数
        if(desFile == "")
        {
            ui->checkBox_serialPortRevToFile->setChecked(false);
            return;
        }
        serialPortRevDesFile = desFile;
        //界面的文本框显示接收转向文件的信息
        ui->textEdit_SerialPortRev->append("接收转向文件");
        ui->textEdit_SerialPortRev->append(desFile);
        ui->textEdit_SerialPortRev->setEnabled(false);
    }
    else
    {
        ui->textEdit_SerialPortRev->clear();
        ui->textEdit_SerialPortRev->setEnabled(true);
        serialPortRevDesFile.clear();
    }
}

//清除串口接收区
void MainWindow::on_pushButton_serialPortRevClear_clicked()
{
    ui->textEdit_SerialPortRev->clear();
}

//串口计数复位
void MainWindow::on_pushButton_serialPortResetCount_clicked()
{
    ui->label_serialPortRevCount->setText("0");
    ui->label_serialPortSendCount->setText("0");
}

//网口开始监听按钮
void MainWindow::on_pushButton_netStartListen_clicked()
{
    if(ui->pushButton_netStartListen->text() == "开始监听")
    {
        //获取本地IP和端口
        strLocalIP = ui->lineEdit_localIP->text();
        localPort = ui->lineEdit_localPort->text().toInt();

        QHostAddress ipAddress;
        ipAddress.setAddress(strLocalIP);
        //绑定IP和端口
        udpSocket = new QUdpSocket(this);
        bool b = udpSocket->bind(ipAddress,localPort);
        //连接信号和槽函数
        connect(udpSocket,SIGNAL(readyRead()),this,SLOT(netPort_Rev()));
        //改变按键文本
        ui->pushButton_netStartListen->setText("停止监听");
    }
    else
    {
        //释放套接字对象
        if(udpSocket!=NULL)
        {
            delete udpSocket;
            udpSocket = NULL;
        }
        //改变按键文本
        ui->pushButton_netStartListen->setText("开始监听");
    }
}

//网口接收函数
void MainWindow::netPort_Rev()
{
    while(udpSocket->hasPendingDatagrams())
    {
        buffer_netPort.resize(udpSocket->pendingDatagramSize());
        udpSocket->readDatagram(buffer_netPort.data(), buffer_netPort.size());

        //接收是否转向文件
        if(ui->checkBox_netPortRevToFile->isChecked())
        {
            //16进制
            if(ui->checkBox_netPortRevHex->isChecked())
            {
                //数据转换为16进制
                QByteArray bufferHex = buffer_netPort.toHex();
                QString str_buffer = QString(bufferHex.toUpper());
                //使字符串的形式变为“AB 23 43 55”
                for(int i = str_buffer.count(); i > 0; i = i-2)
                {
                    str_buffer.insert(i," ");
                }
                //把数据存到文件
                QFile fs(netPortRevDesFile);
                if(fs.open(QFile::Append))
                {
                    QTextStream ts(&fs);

                    ts<checkBox_netPortRevHex->isChecked())
            {
                //数据转换为16进制
                QByteArray bufferHex = buffer_netPort.toHex();
                QString str_buffer = QString(bufferHex.toUpper());
                //使字符串的形式变为“AB 23 43 55”
                for(int i = str_buffer.count(); i > 0; i = i-2)
                {
                    str_buffer.insert(i," ");
                }
                //光标移动到结尾
                ui->textEdit_netRev->moveCursor(QTextCursor::End);
                //显示数据
                ui->textEdit_netRev->insertPlainText(str_buffer);
                //光标移动到结尾
                ui->textEdit_netRev->moveCursor(QTextCursor::End);
            }
            //普通处理
            else
            {
                QString str_buffer = QString(buffer_netPort);
                //光标移动到结尾
                ui->textEdit_netRev->moveCursor(QTextCursor::End);
                //显示数据
                ui->textEdit_netRev->insertPlainText(str_buffer);
                //光标移动到结尾
                ui->textEdit_netRev->moveCursor(QTextCursor::End);
            }
        }
        //统计接收数据长度
        int allCount = ui->label_netPortRevCount->text().toInt();
        allCount = allCount + buffer_netPort.count();
        ui->label_netPortRevCount->setText(QString::number(allCount));
        buffer_netPort.clear();
    }

}

//清除网口接收区
void MainWindow::on_pushButton_netRevClear_clicked()
{
    ui->textEdit_netRev->clear();
}

//网口接收转向文件
void MainWindow::on_checkBox_netPortRevToFile_clicked()
{
    //勾选“接收转向文件”时,弹出文件选择对话框,
    if(ui->checkBox_netPortRevToFile->isChecked())
    {
        QFileDialog *fileDialog = new QFileDialog(this);
        fileDialog->setWindowTitle("选择一个文件");
        fileDialog->setAcceptMode(QFileDialog::AcceptOpen);
        fileDialog->setFileMode(QFileDialog::AnyFile);
        fileDialog->setViewMode(QFileDialog::Detail);
        fileDialog->setDirectory("/home/");

        QString desFile = "";
        if(fileDialog->exec() == QDialog::Accepted)
        {
            desFile = fileDialog->selectedFiles()[0];

        }
        //未选择文件或者直接关闭对话框,取消勾选,直接退出函数
        if(desFile == "")
        {
            ui->checkBox_netPortRevToFile->setChecked(false);
            return;
        }
        netPortRevDesFile = desFile;
        //界面的文本框显示接收转向文件的信息
        ui->textEdit_netRev->append("接收转向文件");
        ui->textEdit_netRev->append(desFile);
        ui->textEdit_netRev->setEnabled(false);
    }
    else
    {
        ui->textEdit_netRev->clear();
        ui->textEdit_netRev->setEnabled(true);
        netPortRevDesFile.clear();
    }
}

//网口计数复位
void MainWindow::on_pushButton_netPortResetCount_clicked()
{
    ui->label_netPortRevCount->setText("0");
    ui->label_netPortSendCount->setText("0");
}

//网口发送按钮
void MainWindow::on_pushButton_netPortSend_clicked()
{

    if(ui->pushButton_netStartListen->text() == "开始监听")
    {
        QMessageBox::critical(this,"提示","网络未连接。",QMessageBox::Yes,QMessageBox::Yes);
        return;
    }

    QString command = ui->lineEdit_netPortSend->text();
    if(ui->checkBox_netEndLine->isChecked())
    {
        command += endLine;
    }
    //获取界面的远端的IP和端口
    strRemoteIP = ui->lineEdit_remoteIP->text();
    remotePort = ui->lineEdit_remotePort->text().toInt();

    int ilen = 0;

    //以16进制发送
    if(ui->checkBox_netPortSendHex->isChecked())
    {
        QStringList commadList = command.split(' ');
        QByteArray byteArray;
        byteArray.resize(commadList.count());
        bool ok = false;
        for(int i = 0; i < commadList.count(); i++)
        {
            byteArray[i] = commadList.at(i).toInt(&ok,16);
        }

        ilen = udpSocket->writeDatagram(byteArray,QHostAddress(strRemoteIP),remotePort);
        QThread::msleep(30);
    }
    //直接发送文本
    else
    {
        ilen = udpSocket->writeDatagram(command.toUtf8(),QHostAddress(strRemoteIP),remotePort);
        QThread::msleep(30);
    }
    //统计发送的长度
    int allCount = ui->label_netPortSendCount->text().toInt();
    allCount = allCount + ilen;
    ui->label_netPortSendCount->setText(QString::number(allCount));
}

最终效果:
Qt编写串口通信、调试的小工具_第3张图片
Qt编写串口通信、调试的小工具_第4张图片

声明

本文中的代码均为本人所写,本文亦是本人原创,转载请注明出处。

你可能感兴趣的:(QT学习)