使用版本为Qt 5.9.8(Qt5.1以上6以下的版本适用)
目录
一、效果演示
这里用的虚拟串口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_OBJECT
public:
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());
}
}
如图:当发现没有可用串口时可点击检测窗口按钮刷新可用串口