QT 篇 QT上位机串口编程
最近因为项目需要,需要用到上位机,通过串口与上位机进行通讯,来上传和下发一些数据以及控制指令,所以用QT写了一个上位机,并记录下来,免得到时候要用又到处翻资料。
QT版本:QT Creater 4.80
硬件:stm32 + 串口转ttl模块
默认安装好QT了
1.新建工程
选择Qt Widgets
然后下一步
自己填一个项目名字和项目路径
我只安装了这一个,我就选了这个
这里主要是一些基类的选择
这里我选择QMainWindow
类名,文件名什么的,你们看看要不要改名
项目管理这里版本控制选择无
然后就完成了创建了
2.添加类和库文件
QT5自带有串口的封装库 QSerialPort ,我们要用的相关的函数,所以要在.pro文件里面添加一样代码,在QT += core gui的基础上添加serialport
QT += core gui serialport
在mainwindo.h里添加串口的一些库
#include <QSerialPort> //访问串口的库
#include <QSerialPortInfo> //查询串口信息的库
#include <QDebug> //用于调试打印输出的库
#include <QTimer> //定时器的库
#include <QTime> //时间的库
#include <QDate> //日期的库
#include <QMessageBox> //一个小的弹窗库
其实上面这些这需要添加最顶上的了两个就够了,其余的是我有其他的用处就加了进去
3.设计上位机页面
这是我自己设计好的上位机
布局上参考了一下正点原子的XCOM串口软件
上位机页面
先设置好窗口的大小固定位800x480
窗口最大和最小都是800x480 这样一来就没办法拉伸了,就不会影响美观
然后可以按照我的设计页面来摆放一些控件
当然你们也可以不用这么多控件,因为实际上来说,需要修改的串口参数其实就只有名称和波特率而已,其余的是可以固定不做改变的
看需求
对象的名称最好改一下,不然到时候编写代码的时候你会不记得哪个代表哪一个部件,我的部件的名称
对象名称
摆放好这些部件后
分别添加波特率
停止位
数据位
奇偶校验等等
然后设置Combox的初始值我把串口的波特率设置为115200是初始值
其余的你们看着来
CurrentIndex的索引和数组一样,从0开始
4.添加函数 扫描串口和初始化串口设置
在mainwindow.h里的private:
//定时器
QTimer *timer;
// 串口对象
QSerialPort *serialport;
//扫描串口
void scanSerialPort();
//初始化
void serialPortInit();
public
int btn_on_off = 0;
int btn_state = 0;
mainwindow.cpp文件
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//对象实例化
serialport = new QSerialPort(this);
timer = new QTimer(this);
scanSerialPort();
}
void MainWindow::scanSerialPort()
{
//查询可用的串口信息
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
{
//添加串口到下拉菜单
ui->cbx_setPortName->addItem(info.portName());
}
qDebug()<<"已发现串口:"<<ui->cbx_setPortName->currentText();
}
}
void MainWindow::serialPortInit()
{
//设置串口名称
serialport->setPortName(ui->cbx_setPortName->currentText());
qDebug()<<"串口名称:"<<serialport->portName();
if (!serialport->open(QIODevice::ReadWrite))
{
qDebug()<<"错误,串口无法打开,可能被占用!";
QMessageBox::about(this,"错误","串口无法打开,可能被占用!");
serialport->close();
return ;
}
//波特率
serialport->setBaudRate(ui->cbx_setBaudRate->currentText().toInt());
qDebug()<<"波特率:"<<ui->cbx_setBaudRate->currentText().toInt();
//停止位
switch (ui->cbx_setStopBits->currentText().toInt())
{
case 1 : serialport->setStopBits(QSerialPort::OneStop); break;
case 2 : serialport->setStopBits(QSerialPort::TwoStop); break;
default: break;
}
qDebug()<<"停止位:"<<serialport->stopBits();
//数据位
switch (ui->cbx_setDataBits->currentText().toInt())
{
case 5 : serialport->setDataBits(QSerialPort::Data5); break;
case 6 : serialport->setDataBits(QSerialPort::Data6); break;
case 7 : serialport->setDataBits(QSerialPort::Data7); break;
case 8 : serialport->setDataBits(QSerialPort::Data8); break;
default: break;
}
//奇偶位
switch (ui->cbx_setParity->currentIndex())
{
case 0 : serialport->setParity(QSerialPort::NoParity); break;
case 1 : serialport->setParity(QSerialPort::OddParity); break;
case 2 : serialport->setParity(QSerialPort::EvenParity); break;
default: break;
}
qDebug()<<"奇偶位:"<<serialport->parity();
serialport->setFlowControl(QSerialPort::NoFlowControl) ;
}
然后打开ui文件
找到打开串口这个PushButton
转到槽函数
就会自动帮你生成信号函数和槽函数
打开串口的槽函数
void MainWindow::on_btn_open_close_clicked()
{
if (btn_on_off == 0)
{
ui->cbx_setPortName->setEnabled(false);
ui->cbx_setBaudRate->setEnabled(false);
ui->cbx_setDataBits->setEnabled(false);
ui->cbx_setStopBits->setEnabled(false);
ui->cbx_setParity->setEnabled(false);
ui->btn_open_close->setText("关闭串口");
qDebug()<<"打开串口:";
serialPortInit();
}
if (btn_on_off == 1)
{
serialport->close();
ui->cbx_setPortName->setEnabled(true);
ui->cbx_setBaudRate->setEnabled(true);
ui->cbx_setDataBits->setEnabled(true);
ui->cbx_setStopBits->setEnabled(true);
ui->cbx_setParity->setEnabled(true);
ui->btn_open_close->setText("打开串口");
}
btn_on_off = !btn_on_off;
}
到这里基本上就可以成功打开串口了
5. 读串口函数 serialport->readAll();
在mainwindow.h 的private slots: 添加
void serialPortReadyRead();
void serialPortWrite();
对应的槽函数
void MainWindow::serialPortReadyRead()
{
QByteArray temp = serialport->readAll();
QString str = ui->textEdit_rx->toPlainText();
str = QString::fromLocal8Bit(temp);//显示中文
ui->textEdit_rx->append(str);
}
此时要手动链接信号函数和槽函数
在mainwindow.cpp的MainWindow::MainWindow(QWidget *parent) :添加
connect(serialport,SIGNAL(readyRead()),this,
SLOT(serialPortReadyRead()));
//SIGNAL 是信号函数,QT的串口自带了
//SLOT是自己定义的槽函数
按着ctrl点击readyRead(),就可以跳转到这里:
6.写串口函数 serialport->write(buff);
void MainWindow::serialPortWrite()
{
QByteArray buff;
//判断是否非空
if(!ui->textEdit_tx->toPlainText().isEmpty())
{
buff = ui->textEdit_tx->toPlainText().toLocal8Bit();//可以写中文
serialport->write(buff);
}
}
把serialPortWrite()关联到发送的槽函数下
顺便把清空接收和清空发送也关联起来
void MainWindow::on_btn_clear_rx_clicked()
{
ui->textEdit_rx->clear();
}
void MainWindow::on_btn_clear_tx_clicked()
{
ui->textEdit_tx->clear();
}
void MainWindow::on_btn_sent_clicked()
{
serialPortWrite();
}
接下来就是测试结果
第一个是stm32打印的上来的串口信息
第二个是自发自收(usb转串口的tx和rx短接)
分割线:=======================================================================================
2022-11-04新增修订
评论区有小伙伴提出没有自刷新串口的功能,以及str += QString::fromLocal8Bit(temp);
现在去掉“+”号,变成str = QString::fromLocal8Bit(temp);
在原有的基础上
增加一个函数
在mainwindows.cpp
里
void MainWindow::reflash_com()
{
ui->cbx_setPortName->clear();//清除选择串口下拉栏添加的所有项目
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
{
ui->cbx_setPortName->addItem(info.portName());
}
}
ui->cbx_setPortName->setCurrentIndex(choose_index);
qDebug()<<"choose_index:"<<choose_index;
}
此外增加一个变量choose_index
用于记录上次选择串口的项目index
函数和变量记得在对应的.h文件增加声明
同时把reflash_com()
放到500ms定时器里面
void MainWindow::timer_500ms()
{
QTime time = QTime::currentTime();
QDate date = QDate::currentDate();
ui->label_Date_Time->setText(date.toString("yyyy-MM-dd") +" "+ time.toString("hh:mm:ss"));
ui->textEdit_rx->setFontPointSize(ui->cbx_setFontPointSize->currentText().toInt());
if (btn_on_off == 0)//判断串口处于未打开状态
{
choose_index = ui->cbx_setPortName->currentIndex();
reflash_com();
}
}
效果: