美文网首页
Qt编写串口通信、调试的小工具

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

作者: yangyingle | 来源:发表于2018-12-25 19:51 被阅读0次

    前言

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

    思路

    因为是根据Windows上的串口调试助手来编写的,所以思路会往这个软件上面靠。
    Windows上的串口调试助手分了两个大功能,一个是串口的数据收发,一个是网口的数据收发,并且都支持以文本或者十六进制方式显示读取到的数据,也可以以文本或者十六进制方式存储读取到的数据到指定的文件中。
    界面如下图所示:


    主界面

    Linux系统中的串口以文件的形式在/dev目录下,文件名为tty*,这里,我不用以读文件的方式读写串口,用QT5提供的QSerialPort类读写串口,并且用connect函数把QSerialPort的信号和我们定义的槽函数连接起来。
    勾选“接收转向文件”,弹出打开文件的对话框,直接在对话框中输入文件名,或者选择一个文件,即可把数据写入到文件中。


    选择文件对话框
    勾选“16进制显示”,则通过QByteArray的toHex()函数,把数据的十六进制的值放到另一个缓存变量中,再转为QString,显示到文本框中。
    网口接收数据方面,使用QUdpSocket,connect函数把QUdpSocket的信号和我们定义的槽函数连接起来。数据的处理、显示和串口一样。

    代码

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

      #ifndef MAINWINDOW_H
      #define MAINWINDOW_H​
      #include <QMainWindow>
      #include <QtSerialPort/QSerialPort>
      #include <QMessageBox>
      #include <QIcon>
      #include <QThread>
      #include <QUdpSocket>
      #include <QFileDialog>
      ​
      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<<str_buffer;
      ​
                      ts.flush();
                  }
                  fs.close();
      ​
              }
              //普通处理,即不转换为16进制
              else
              {
                  //直接把数据转为字符串,直接存到文件中
                  QString str_buffer = QString(buffer_serialPort);
                  QFile fs(serialPortRevDesFile);
                  if(fs.open(QFile::Append))
                  {
                      QTextStream ts(&fs);
      ​
                      ts<<str_buffer;
      ​
                      ts.flush();
                  }
                  fs.close();
              }
      ​
              //写完文件后直接返回
              int allCount = ui->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<<str_buffer;
      ​
                          ts.flush();
                      }
                      fs.close();
                  }
                  //普通处理,即不转换为16进制
                  else
                  {
                      //直接把数据转为字符串,直接存到文件中
                      QString str_buffer = QString(buffer_netPort);
                      //把数据存到文件
                      QFile fs(netPortRevDesFile);
                      if(fs.open(QFile::Append))
                      {
                          QTextStream ts(&fs);
      ​
                          ts<<str_buffer;
      ​
                          ts.flush();
                      }
                      fs.close();
                  }
              }
              //接收不转向文件时,显示在界面上
              else
              {
                  //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," ");
                      }
                      //光标移动到结尾
                      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));
      }
    

    最终效果:


    串口通信测试结果-1
    串口通信测试结果-2

    声明

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

    相关文章

      网友评论

          本文标题:Qt编写串口通信、调试的小工具

          本文链接:https://www.haomeiwen.com/subject/axrqlqtx.html