这章来学习串口数据流操作。仿照已有成品制作一个用Qt开发的串口调试助手。
介于篇幅有限,本篇只介绍一个最简单的能收发的串口调试助手的制作。后续篇幅会陆续对剩余代码进行讲解,并添加各种功能,对显示、操作、功能的各方面继续优化,达成与市面上的成品一样的功能。目标精简、可靠、高效、便于当作框架二次开发,欢迎各位使用和作为框架进行二次开发,并提出改进意见。
发布效果展示:Qt串口调试助手演示效果
GitHub源码仓库:Qt串口调试助手源码下载
更新:后期在原有串口功能上,使用了QCustomPlot绘图库,制作了串口波形绘图上位机,非常适合单片机上传波形进行调试。最多可显示20条曲线,支持滚轮Y轴缩放、左键拖拽。波形帧协议兼容匿名四轴调试上位机,并增加了16进制转换,支持GB2312中文编码。详细介绍见:波形绘图上位机源码下载
GitHub中为最终发布代码,可能与讲解教程中略有不同,望自行对照。
1. 布局UI界面
- 创建QMainWindow工程。
- 布局UI界面:拖入2个 Plain Text Edit,作为串口数据的接收显示和发送框。
- 再拖入几个 Combo Box 、 Label 和 Push Button,使用两个Widget进行布局。如下所示。
- 将接收区的 Plain Text Edit,属性勾选上 readOnly,使其运行时不会被人为的输入操作影响。
- 加入一些弹簧,使布局美观。(有必要可以加设置,固定窗口大小,不能最大化。)
2. 添加下拉列表项
- 在ui设计界面中,双击 Combo Box添加列表项。
- 更改下拉框的默认值。修改 currentIndex属性,改变默认值。
3. 修改控件名称
- 将控件的命名统一,方便管理和程序书写。
4. 添加串口类,自动扫描可用串口
- 在工程 .pro文件中,添加 "QT += core gui serialport",以支持串口功能。
- 窗口构造函数中,添加自动扫描可用串口代码。(由于代码位于窗口构造函数中,故仅在应用刚打开时扫描串口,以后会增加点击下拉框时扫描,需要对鼠标点击事件进行重写)
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); QStringList serialPortName; // 自动扫描当前可用串口,返回值追加到字符数组中 foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){ serialPortName << info.portName(); } // 可用串口号,显示到串口选择下拉框中 ui->cmbSerialPort->addItems(serialPortName); }
- 如果电脑连接了串口设备,运行Qt应用,点击下拉框,会看到可用的串口号。
- 与标准的串口助手对比,结果一致。
5. 打开串口、翻转开关
- 功能:点击 "打开串口" 按钮,初始化串口参数,并按照设置好的参数打开串口。
- 在 "打开串口" 按钮位置,右键,转到槽...。编写串口配置代码。
- 将字符串,转换为可以代码设置的枚举,根据当前值,设置参数。之后打开对应串口端口。
// 打开/关闭串口 槽函数 void MainWindow::on_btnSwitch_clicked() { QSerialPort::BaudRate baudRate; QSerialPort::DataBits dataBits; QSerialPort::StopBits stopBits; QSerialPort::Parity checkBits; // 获取串口波特率 // 有没有直接字符串转换为 int的方法??? //baudRate = ui->cmbBaudRate->currentText().toInt(); if(ui->cmbBaudRate->currentText() == "9600"){ baudRate = QSerialPort::Baud9600; }else if(ui->cmbBaudRate->currentText() == "38400"){ baudRate = QSerialPort::Baud38400; }else if(ui->cmbBaudRate->currentText() == "115200"){ baudRate = QSerialPort::Baud115200; }else{ } // 获取串口数据位 if(ui->cmbData->currentText() == "5"){ dataBits = QSerialPort::Data5; }else if(ui->cmbData->currentText() == "6"){ dataBits = QSerialPort::Data6; }else if(ui->cmbData->currentText() == "7"){ dataBits = QSerialPort::Data7; }else if(ui->cmbData->currentText() == "8"){ dataBits = QSerialPort::Data8; }else{ } // 获取串口停止位 if(ui->cmbStop->currentText() == "1"){ stopBits = QSerialPort::OneStop; }else if(ui->cmbStop->currentText() == "1.5"){ stopBits = QSerialPort::OneAndHalfStop; }else if(ui->cmbStop->currentText() == "2"){ stopBits = QSerialPort::TwoStop; }else{ } // 获取串口奇偶校验位 if(ui->cmbCheck->currentText() == "无"){ checkBits = QSerialPort::NoParity; }else if(ui->cmbCheck->currentText() == "奇校验"){ checkBits = QSerialPort::OddParity; }else if(ui->cmbCheck->currentText() == "偶校验"){ checkBits = QSerialPort::EvenParity; }else{ } // 想想用 substr strchr怎么从带有信息的字符串中提前串口号字符串 // 初始化串口属性,设置 端口号、波特率、数据位、停止位、奇偶校验位数 mySerialPort->setBaudRate(baudRate); mySerialPort->setDataBits(dataBits); mySerialPort->setStopBits(stopBits); mySerialPort->setParity(checkBits); mySerialPort->setPortName(ui->cmbSerialPort->currentText());// 不匹配带有串口设备信息的文本 // 根据初始化好的串口属性,打开串口 // 如果打开成功,反转打开按钮显示和功能。打开失败,无变化,并且弹出错误对话框。 if(ui->btnSwitch->text() == "打开串口"){ if(mySerialPort->open(QIODevice::ReadWrite) == true){ //QMessageBox:: ui->btnSwitch->setText("关闭串口"); // 让端口号下拉框不可选,避免误操作(选择功能不可用,控件背景为灰色) ui->cmbSerialPort->setEnabled(false); }else{ QMessageBox::critical(this, "错误提示", "串口打开失败!!!\r\n该串口可能被占用\r\n请选择正确的串口"); } }else{ mySerialPort->close(); ui->btnSwitch->setText("打开串口"); // 端口号下拉框恢复可选,避免误操作 ui->cmbSerialPort->setEnabled(true); } }
- 为了避免串口被占用,或者其他错误,我们对打开也做相应的处理:如果串口被占用,会弹出错误提示框。
- 并且为了防止端口在打开时,被篡改参数,避免改动串口号,做了处理:端口在打开后,下拉框是灰色不可选的。
- 最终实现 打开串口 / 关闭串口 的功能转换与翻转显示。
6. 接收串口数据
- 预计结果:接收到串口数据后,将其显示到接收文本框中。
- 构造函数中建立信号槽,将串口读准备信号,关联接收区显示处理槽函数。
- 文本框内容使用插入显示。并在每次追加后移动光标
- 对于接收的显示处理方式,我总结了如下四种。各位有兴趣可以将注释打开,运行去了解。
- 其中第3、4种都是将文本框全部读取再追加内容,运行效率很低,而网上很多开源代码是使用的这种处理方式。这正是使我想要搭建自己开发框架的原因,追求可靠、高效。
- 我在代码中,使用的是第2种处理方式。解决了不同硬件的插入换行问题,兼容 CH340、CP2102等常用设备。(现象、成因和解决方法在后面序章有说)
7. 发送串口数据
发送按钮,右键,转到槽...。
添加发送字符串代码。
8. 清空收区、发区文本内容
发送按钮,右键,转到槽...。
添加清空代码。
运行结果
- 运行时是没有左上角图标的,这是我后来测试生成可执行文件时添加的。
总结
Qt编程串口调试助手的过程比较简单,但仍有很多地方待完善。例如端口扫描、16进制转换,硬件兼容性,这些会在后面的篇幅进行讲解和添加,力求精简、可靠、高效、便于当作框架二次开发。欢迎各位使用和作为框架进行二次开发,并提出改进意见。
GitHub源码仓库:Qt串口调试助手源码下载