使用版本为Qt 5.9.8(Qt5.1以上6以下的版本适用)
目录
一、效果演示
二、ui设计
三、代码部分
四、完整代码
五*、功能优化
(1)清空接收区
(2)16进制显示
(3)16进制发送
(4)时间戳
(5)检测串口
一、效果演示
这里用的虚拟串口COM1、COM2模拟串口通信。左边为QT设计的串口工具为COM2串口,右边串口助手为COM1,可以看到设计的串口工具可以完成简单的收发数据功能,此外可以对串口波特率等等参数进行设置。
二、ui设计
控件布局、控件名称如图所示:
波特率下拉栏添加了两个item:第一个115200,第二个9600。
数据位下拉栏添加了两个item:第一个8位,第二个6位。
校验位下拉栏添加了三个item:第一个无,第二个偶校验,第三个奇检验。
停止位下拉栏添加了三个item:第一个1位,第二个1.5位,第三个2位。
另外右下角红色指示灯实际上是一个Label(宽高:5*5),预设样式表代码为:
border-radius:5px; //设置圆角半径background-color: rgb(255, 0, 0); //设置背景颜色
三、代码部分
serial.pro 工程文件开头加入:
QT += serialport
头文件mainwindow.h 开头加入:
#include <QSerialPort> //提供访问串口的功能#include <QSerialPortInfo> //提供系统中存在的串口信息#include <QDebug> //debug用
头文件mainwindow.h 内,在 private:中加入:用于定义一个串口对象
QSerialPort *serial; //定义串口对象
mainwindow.cpp窗口构造函数中加入以下代码:(三部分功能见注释)
//获取可用串口名到下拉栏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);
关键部分是写点击“打开串口”按钮的槽函数:打开还是关闭串口是通过判断按钮文本进行的。整体设计框架为:(具体代码见部分四:完整代码)
如果按钮文本为“打开串口”: 依次设置:串口名、波特率、数据位、校验位、停止位、流控制。 再打开串口。 如果成功打开:指示灯变绿、禁用下拉栏、改变按钮文本为“关闭窗口”。 如果打开失败:QDebug报错如果按钮文本为“关闭串口”: 关闭串口、指示灯变红、恢复下拉栏使能、改变按钮文本为“打开串口”。*****************************************************************************************void MainWindow::on_Button_openserial_clicked()//点击打开串口按钮{ if(ui->Button_openserial->text() == "打开串口") { //设置串口为下拉栏所选串口名 //设置波特率 //设置数据位数 //设置校验位 //设置停止位 //设置流控制 //打开串口 同时判断是否成功 bool info = serial->open(QIODevice::ReadWrite); if(info == true) { qDebug()<<"success"; //改变label颜色(指示灯) //关闭下拉栏设置使能 //改变按钮文本为"关闭串口" } else { qDebug()<<"fail"; } else { //关闭串口 //改变label颜色(指示灯) //恢复下拉栏设置使能 //改变按钮文本为"打开串口" }}
接着写点击接收数据的函数(读数据):
void MainWindow::ReadData()//读取接收到的信息{ QByteArray buf = serial->readAll(); ui->textBrowser->append(buf);}
最后写点击“发送”按钮的槽函数:
void MainWindow::on_Button_send_clicked()//点击发送按钮{ //检查串口是否打开 if (serial->isOpen()) { //获取要发送的数据:lineEdit_send内容 QString sendData = ui->lineEdit_send->text()+"\r\n"; //将字符串转为QByteArray QByteArray data = sendData.toUtf8(); //发送数据 serial->write(data); } else { qDebug()<<"serial is not open"; }}
四、完整代码
头文件mainwindow.h
#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>#include <QSerialPort> //提供访问串口的功能#include <QSerialPortInfo> //提供系统中存在的串口信息#include <QDebug> //debug用namespace Ui {class MainWindow;}class MainWindow : public QMainWindow{ Q_OBJECTpublic: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow();private slots: void on_Button_openserial_clicked();//点击打开串口按钮 void ReadData();//串口读数据(接收数据) void on_Button_send_clicked();//点击发送按钮private: Ui::MainWindow *ui; QSerialPort *serial;//定义串口对象};#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); //获取可用串口名到下拉栏 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);}MainWindow::~MainWindow(){ delete ui;}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;//115200 case 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 { qDebug()<<"fail"; } } 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::ReadData()//接收数据函数(并显示){ QByteArray buf = serial->readAll(); ui->textBrowser->append(buf);}void MainWindow::on_Button_send_clicked()//点击发送按钮{ //检查串口是否打开 if (serial->isOpen()) { //获取要发送的数据:lineEdit_send内容 QString sendData = ui->lineEdit_send->text(); //将字符串转为QByteArray QByteArray data = sendData.toUtf8(); //发送数据 serial->write(data); } else { qDebug()<<"serial is not open"; }}
五*、功能优化
以上代码仅仅实现了简单的收发功能,但还可以做一些使用功能上的优化:
(1)清空接收区
接收显示的数据过多需要清空,可以设计点击按钮实现清空的功能,因此只需要添加一个按钮控件,编写点击按钮的槽函数即可:
void MainWindow::on_pushButton_2_clicked()//点击清空接收区按钮{ ui->textBrowser->clear();}
(2)16进制显示
要接收数据以16进制显示,且每个字节用空格隔开便于查看。方法为添加一个checkBox控件,在接收数据函数内判断checkBox勾选情况,执行不同的操作,接收数据函数修改如下:
void MainWindow::ReadData()//读取接收到的信息{ QByteArray buf = serial->readAll(); if(ui->checkBox->isChecked()==true)//已经被勾选 { QString hexData= buf.toHex().toUpper();//将数据转换为16进制字符串并大写 QString spacedData;//用于存储每个字节隔开一个空格的字符串 for(int i=0;i<hexData.length();i+=2)//在每个字节后添加一个空格 { spacedData += hexData.mid(i,2); spacedData += " "; } ui->textBrowser->append(spacedData);//将转换后的数据显示 } else { ui->textBrowser->append(buf);//未勾选则直接显示 }}
(3)16进制发送
前面已经实现了输入内容点击按钮发送,但是发送的是字符串,如果想要直接发送16进制数(如输入1a2b,发送出去16进制数1a2b),可用设计一个16进制发送功能,将符合16进制格式的字符串转换为QByteArray形式进行发送。方法为添加一个checkBox控件,在点击发送按钮槽函数内判断checkBox勾选情况,执行不同的操作,具体如下:
1.mainwindow.h文件public:中 定义static函数(实现符合16进制格式的QString数据转QByteArray数据)***************************************************************************static 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;//返回转换结果 }
2.修改点击发送按钮槽函数如下:****************************************************************************void MainWindow::on_Button_send_clicked()//点击发送按钮{ //检查串口是否打开 if (serial->isOpen()) { if(ui->checkBox_2->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(); //将字符串转为QByteArray QByteArray data = sendData.toUtf8(); //发送数据 serial->write(data); } } else { qDebug()<<"serial is not open"; }}
如图:勾选16进制发送后,输入16进制数发送内容与即为接收方接收结果,所键即所得。
(4)时间戳
时间戳功能就是显示接收数据时,一并显示接收时间,需要添加一个头文件,并且在接收数据函数内加入第二段代码(添加到 “ui->textBrowser->append(xxx)” 上方):
#include <QDateTime> //获取当前时间
QString currentTime = QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss]");//定义字符串 = 当前时间并转换为字符串ui->textBrowser->append(currentTime);//设置标签内容
(5)检测串口
当打开程序后,可用串口会自动添加到下拉栏,但如果可用串口增加或减少时,可用串口选项并不会发生变化,因此可设置一个按钮进行手动刷新(另外也可以用定时器实现定时刷新),点击按钮的槽函数与构造函数内获取可用串口是一样的:
void MainWindow::on_Button_check_clicked()//点击检测串口按钮{ //获取可用串口名到下拉栏 QList<QSerialPortInfo> list = QSerialPortInfo::availablePorts(); for(int i=0; i<list.size(); i++) { ui->comboBox_port->addItem(list.at(i).portName()); }}
如图:当发现没有可用串口时可点击检测窗口按钮刷新可用串口