QT:串口通信、串口发送与接收(2)
使用版本为 Qt 5.9.8(Qt 5.1以上 6 以下的版本都适用)
目录
一、效果演示
二、UI界面
三、完整代码
(1).pro
(2)mainwindow.h
(3)mainwindow.cpp
(4)main.cpp
最近用到Qt做串口通信上位机,翻看了之前写过一篇文章,写的有点乱(传送门),于是打算重新整理一个完整的串口通信上位机的模板工程,功能和之前的一样,代码部分也不再详细介绍,都有详细注释。附完整代码和工程文件资源。
一、效果演示
用的虚拟串口COM1、COM2模拟串口通信。右边为QT设计的串口工具,左边为XCOM串口助手。可以看到设计的串口工具可以完成串口收发数据,此外可以设置显示和发送的格式。
二、UI界面
波特率下拉栏添加了两个item:第一个115200,第二个9600。
数据位下拉栏添加了两个item:第一个8位,第二个6位。
校验位下拉栏添加了三个item:第一个无,第二个偶校验,第三个奇检验。
停止位下拉栏添加了三个item:第一个1位,第二个1.5位,第三个2位。
另外右下角红色指示灯实际上是一个Label,预设样式表代码为:
border-radius:5px;
background-color: rgb(255, 0, 0);
三、完整代码
(1).pro
在 ".pro" 工程文件开头加入代码:引入Qt串口通信模块,用于实现串口数据的发送和接收。
QT += serialport
(2)mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>//================================================添加头文件
#include <QSerialPort> //串口
#include <QSerialPortInfo> //串口
#include <QTimer> //定时器
#include <QDateTime> //获取当前时间
#include <QMessageBox> //信息弹窗
#include <QDebug> //用于调试
#include <QTextCodec> //编码namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();//函数功能:将QString(16进制格式)转换为ByteArraystatic QByteArray hexStringToByteArray(const QString &hexString){QByteArray byteArray;QString sanitizedHex = hexString;//确保字符串长度是偶数if (sanitizedHex.length() % 2 != 0){sanitizedHex.prepend('0');//如果长度是奇数,前面补零}for (int i=0; i<sanitizedHex.length(); i+=2){QString byteString = sanitizedHex.mid(i,2);//取两位bool ok;byteArray.append(static_cast<char>(byteString.toInt(&ok,16)));//转为字节if (!ok){qDebug() << "Invalid hex byte:" << byteString;//字符串非16进制格式,转换报错}}return byteArray;//返回转换结果}private slots:void showTime(); //获取时间void on_Button_check_clicked(); //点击检测串口按钮void on_Button_openserial_clicked(); //点击打开串口按钮void on_Button_clean_clicked(); //点击清空接收按钮void on_Button_send_clicked(); //点击发送按钮private:void initSerial(); //串口初始化void initTime(); //时间初始化void ReadData(); //串口读数据(接收数据)private:Ui::MainWindow *ui;QSerialPort *serial; //串口对象QTimer *timer; //定时器QByteArray buf; //接收数据缓存
};#endif // MAINWINDOW_H
(3)mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);initTime(); // ============= 时间初始化initSerial(); // ============= 串口初始化
}MainWindow::~MainWindow()
{delete ui;
}//==========================================================//
// 时间相关函数
//==========================================================//
void MainWindow::initTime() //时间初始化
{timer = new QTimer;// 信号连接、每一秒执行一次槽函数connect(timer,&QTimer::timeout,this,&MainWindow::showTime);timer->start(1000);
}
void MainWindow::showTime()//获取当前时间并显示
{//定义字符串currentTime = 获取当前时间并转换为字符串QString currentTime = QDateTime::currentDateTime().toString("时间: yyyy-MM-dd hh:mm:ss");ui->label_time->setText(currentTime);//设置标签内容
}//==========================================================//
// 串口相关函数
//==========================================================//void MainWindow::initSerial() //串口初始化
{//获取可用串口名到下拉栏QList<QSerialPortInfo> list = QSerialPortInfo::availablePorts();for(int i=0; i<list.size(); i++){ ui->comboBox_port->addItem(list.at(i).portName()); }//创建串口对象serial = new QSerialPort;//连接信号与槽(接收数据)QObject::connect(serial,&QSerialPort::readyRead,this,&MainWindow::ReadData);
}
void MainWindow::on_Button_openserial_clicked() //点击打开串口按钮
{if(ui->Button_openserial->text() == "打开串口") {//设置串口为下拉栏所选串口名serial->setPortName(ui->comboBox_port->currentText());//设置波特率switch (ui->comboBox_baud->currentIndex()) {case 0: serial->setBaudRate(QSerialPort::Baud115200); break;//115200case 1: serial->setBaudRate(QSerialPort::Baud9600); break;default: break;}//设置数据位数switch (ui->comboBox_databit->currentIndex()) {case 0:serial->setDataBits(QSerialPort::Data8); break;//1位case 1:serial->setDataBits(QSerialPort::Data6); break;default: break;}//设置校验位switch (ui->comboBox_parity->currentIndex()) {case 0:serial->setParity(QSerialPort::NoParity); break;//无校验位case 1:serial->setParity(QSerialPort::EvenParity); break;case 2:serial->setParity(QSerialPort::OddParity); break;default: break;}//设置停止位switch (ui->comboBox_stopbit->currentIndex()) {case 0:serial->setStopBits(QSerialPort::OneStop); break;//1位case 1:serial->setStopBits(QSerialPort::OneAndHalfStop); break;case 2:serial->setStopBits(QSerialPort::TwoStop); break;default: break;}//设置流控制serial->setFlowControl(QSerialPort::NoFlowControl);//无流控制//打开串口 同时判断是否成功bool info = serial->open(QIODevice::ReadWrite);if(info == true) {qDebug()<<"success";//改变label颜色(指示灯)ui->label_light->setStyleSheet("background-color:rgb(0,255,0);border-radius:5px;");//关闭下拉栏设置使能ui->comboBox_port->setEnabled(false);ui->comboBox_baud->setEnabled(false);ui->comboBox_databit->setEnabled(false);ui->comboBox_parity->setEnabled(false);ui->comboBox_stopbit->setEnabled(false);//改变按钮文本为“关闭串口”ui->Button_openserial->setText(tr("关闭串口"));}else{ QMessageBox::critical(this, "Error", "串口打开失败:未检测到可用串口"); }}else { //关闭串口serial->clear();serial->close();//改变label颜色(指示灯)ui->label_light->setStyleSheet("background-color:rgb(255,0,0);border-radius:5px;");//恢复下拉栏设置使能ui->comboBox_port->setEnabled(true);ui->comboBox_baud->setEnabled(true);ui->comboBox_databit->setEnabled(true);ui->comboBox_parity->setEnabled(true);ui->comboBox_stopbit->setEnabled(true);//改变按钮文本为“打开串口”ui->Button_openserial->setText(tr("打开串口"));}
}void MainWindow::on_Button_check_clicked() //点击检测串口按钮
{//获取可用串口名到下拉栏QList<QSerialPortInfo> list = QSerialPortInfo::availablePorts();ui->comboBox_port->clear(); // 清除之前检测到的串口for(int i=0; i<list.size(); i++){ ui->comboBox_port->addItem(list.at(i).portName()); }
}void MainWindow::on_Button_clean_clicked() //点击清空接收按钮
{ui->textBrowser->clear();
}void MainWindow::on_Button_send_clicked() //点击发送按钮
{//检查串口是否打开if (serial->isOpen()){if(ui->checkBox_16send->isChecked()==true) { //16进制发送被选中//获取要发送的数据:lineEdit_send内容QString sendData = ui->lineEdit_send->text();//将字符串转换为QByteArray形式(调用了hexStringToByteArray函数)QByteArray data = hexStringToByteArray(sendData);//发送数据serial->write(data);}else {//获取要发送的数据:lineEdit_send内容QString sendData = ui->lineEdit_send->text();//将字符串转为QByteArrayQByteArray data = sendData.toUtf8();//发送数据serial->write(data);}}else {qDebug()<<"serial is not open";QMessageBox::critical(this, "Error", "发送失败:请打开串口");}
}void MainWindow::ReadData()//==================================== 读取接收到的信息
{// 1. 读取数据并追加到缓存 (.h头文件已声明 QByteArray buf)buf.append(serial->readAll()); //或QByteArray buf = serial->readAll();// 2. 判断数据显示格式QString displayData; //定义最后显示出来的字符串if (ui->checkBox_16rec->isChecked()==true) { //16进制显示被选中QString hexData= buf.toHex().toUpper(); //将数据转换为16进制字符串并大写QString spacedData; //用于存储每个字节隔开一个空格的字符串for(int i=0;i<hexData.length();i+=2) { //在每个字节后添加一个空格spacedData += hexData.mid(i,2);spacedData += " ";}displayData = spacedData; //显示转换后的数据}else { //非十六进制显示QTextCodec *gbkCodec = QTextCodec::codecForName("GBK");//使用GBK编码解析(解决中文乱码)displayData = gbkCodec->toUnicode(buf);}// 3. 添加时间戳,显示接收数据QString currentTime = QDateTime::currentDateTime().toString("Rx: [hh:mm:ss]");//获取当前时间currentTime = QString("<span style='color: red;'>%1</span>").arg(currentTime);//设置时间戳为红色ui->textBrowser->append(currentTime); //显示时间戳ui->textBrowser->append(displayData); //显示接收数据buf.clear(); //清空接收缓存
}
(4)main.cpp
mian.cpp里面有两行被注释掉的的代码,主要是用于适应屏幕放缩用的,如果ui运行出来布局和设计的摆放布局不一致或者是错位了,可以尝试通过这两行代码解决。
#include "mainwindow.h"
#include <QApplication>int main(int argc, char *argv[])
{//自适应放缩(用于屏幕分辨率改变会使窗口布局大小变化)//if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling,true);QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}