QT-串口,完结!
Qt串口
Qt串口编程是使用Qt框架进行串口通信的一种技术,它的主要作用在于实现应用程序与外部设备之间的数据交换,特别是那些通过串口进行通信的设备。串口通信广泛用于连接嵌入式系统、嵌入式设备和传感器、单片机、无线模块等外部硬件设备。它在很多嵌入式系统、嵌入式设备和传感器等应用中被广泛使用。
一、串口编程基础
1.1 串口通信基础
目前使用最广泛的串口为 DB9 接口,适用于较近距离的通信。一般小于10米,DB9接口有9个针脚。
串口通信的主要参数如下:
- 波特率:衡量通信速度的参数,表示每秒钟传送的bit的个数。例如9600波特表示每秒钟发送9600个bit。
- 数据位:衡量通信中实际数据位的参数,当计算机发送一个信息包,实际包含的有效数据位个数。
- 停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。
- 奇偶校验位:串口通信中一种检错方式。常用的检错方式有:偶、奇校验。
1.2 QtSerialPort模块简介
QtSerialPort模块是QT5中附加模块的一个模块,为硬件和虚拟的串口提供统一的接口。
串口由于其简单和可靠,目前在像嵌入式系统、机器人等工业中依旧用得很多。使用QtSerialPort模块,开发者可以大大缩短开发串口相关的应用程序的周期。
Qt SerialPort 提供了基本的功能,包括配置、I/O操作、获取和设置RS-232引脚的信号。
要在应用程序中使用QtSerialPort,需要包括如下的声明:
#include <QSerialPortInfo>
#include <QSerialPort>
要链接 QtSerialPort 模块,需要在.pro文件中添加如下内容:
QT += serialport
QSerialPort
提供了访问串口的接口函数。使用辅助类QSerialPortInfo
可以获取可用的串口信息。将 QSerialPortInfo
辅助类对象做为参数,使用setPort()
或setPortName()
函数可以设置要访问的串口设备。
设置好端口后,可以使用open()
函数以只读、只写或读写的模式打开使用。使用close()
函数关闭串口并且取消IO操作。
注意:串口使用独占方式打开。
串口成功打开后,QSerialPort会尝试确定串口的当前配置并初始化。可以使用setBaudRate()
、setDataBits()
、setParity()
、
setStopBits()
和setFlowControl()
函数重新配置端口设置。
有一对名为QSerialPort::dataTerminalReady
、QSerialPort::requestToSend
的属性QSerialPort提供了中止正在调用线程直到
信号触发的一系列函数。这些函数用于阻塞串口。
waitForReadyRead()
:阻塞调用,直到有新的数据可读
waitForBytesWritten()
:阻塞调用,直到数据以及写入串口
阻塞串口编程与非阻塞串口编程完全不同。阻塞串口不会要求时间循环并且通常会简化代码。然而,在GUI程序中,为了避免冻结用户界面,阻塞串口编程只能用于非GUI线程。
QSerialPort也能使用 QTextStream
和 QDataStream
的流操作符。在试图使用流操作符 >>
读时,需要确保有足够可用的数据。
1.3 QSerialPort 成员函数
构造函数
QSerialPort::QSerialPort(QObject *parent = Q_NULLPTR)
QSerialPort::QSerialPort(const QString &name, QObject *parent = Q_NULLPTR)
QSerialPort::QSerialPort(const QSerialPortInfo &serialPortInfo, QObject *parent = Q_NULLPTR)
如果当前没有数据可读,返回 true
[virtual] bool QSerialPort::atEnd() const
波特率改变后,信号触发
[signal] void QSerialPort::baudRateChanged(qint32 baudRate, QSerialPort::Directions directions)
返回可读数据的字节数
[virtual] qint64 QSerialPort::bytesAvailable() const
返回可写数据的字节数
[virtual] qint64 QSerialPort::bytesToWrite() const
关闭串口
[virtual] void QSerialPort::close()
设置串口端口信息为 serialPortInfo
void QSerialPort::setPort(const QSerialPortInfo &serialPortInfo)
设置串口名为name
void QSerialPort::setPortName(const QString &name)
#include <QCoreApplication>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QDebug>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 创建串口对象(无父对象)QSerialPort serial;// 设置串口名serial.setPortName("COM3"); // 右侧注释:指定要操作的串口设备名// 打开串口(读写模式)if (!serial.open(QSerialPort::ReadWrite)) {qDebug() << "串口打开失败";return -1;}// 设置波特率serial.setBaudRate(QSerialPort::Baud9600); // 右侧注释:设置通信速率为9600// 设置数据位、校验位、停止位serial.setDataBits(QSerialPort::Data8); // 数据位8serial.setParity(QSerialPort::NoParity); // 无校验serial.setStopBits(QSerialPort::OneStop); // 1个停止位serial.setFlowControl(QSerialPort::NoFlowControl); // 无流控// 检查是否有可读数据if (serial.atEnd()) { // 右侧注释:如果当前没有数据可读,则返回trueqDebug() << "没有可读数据";}// 获取可读数据字节数qint64 bytesAvailable = serial.bytesAvailable(); // 右侧注释:返回当前可读数据字节数qDebug() << "可读字节数:" << bytesAvailable;// 写入数据QByteArray data = "Hello Serial";qint64 bytesWritten = serial.write(data); // 右侧注释:写入数据到串口,返回写入字节数qDebug() << "写入字节数:" << bytesWritten;// 获取等待写入的字节数qint64 toWrite = serial.bytesToWrite(); // 右侧注释:返回仍然在缓冲区等待写入的数据字节数qDebug() << "待写字节数:" << toWrite;// 关闭串口serial.close(); // 右侧注释:关闭串口设备return a.exec();
}
二、QSerialPortInfo
2.1 QSerialPortInfo简介
QSerialPortInfo
类提供已有串口设备的信息。使用 QSerialPortInfo
类的静态成员函数生成 QSerialPortInfo
对象的链表。
链表中的每个QSerialPortInfo
对象代表一个串口,每个串口可以使用端口名、系统定位、描述、制造商查询。QSerialPortInfo
类
对象也可以用做QSerialPort
类的setPort()
成员函数的参数。
2.2 QSerialPortInfo成员函数
构造函数
QSerialPortInfo::QSerialPortInfo(const QSerialPort &port)
QSerialPortInfo::QSerialPortInfo(const QString &name)
QSerialPortInfo::QSerialPortInfo(const QSerialPortInfo &other)
返回当前系统可用串口的链表
[static] QList<QSerialPortInfo> QSerialPortInfo::availablePorts()
如果串口可用,返回串口的描述信息
QString QSerialPortInfo::description() const
如果有一个合法的16位生产码,返回true
bool QSerialPortInfo::hasProductIdentifier() const
如果有一个合法的16位制造商编码,返回true
bool QSerialPortInfo::hasVendorIdentifier() const
如果串口当前正忙,返回true
bool QSerialPortInfo::isBusy() const
如果串口可用,返回串口的制造商的名字
QString QSerialPortInfo::manufacturer() const
返回串口的名字
QString QSerialPortInfo::portName() const
如果串口可用,返回串口的16位的生产编码
quint16 QSerialPortInfo::productIdentifier() const
如果串口可用,返回串口的序列号
QString QSerialPortInfo::serialNumber() const
返回目标平台支持的可用的标准波特率的链表
[static] QList<qint32> QSerialPortInfo::standardBaudRates()
使用other交换QSerialPortInfo对象
void QSerialPortInfo::swap(QSerialPortInfo &other)
返回串口的系统位置
QString QSerialPortInfo::systemLocation() const
如果串口可用,返回16位的制造商编码
quint16 QSerialPortInfo::vendorIdentifier() const
#include <QCoreApplication>
#include <QSerialPortInfo>
#include <QDebug>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 获取系统当前可用串口的链表QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts(); // 静态函数,返回所有可用串口信息// 遍历每一个串口foreach (const QSerialPortInfo &info, ports) {qDebug() << "--------------------------------------";qDebug() << "端口名:" << info.portName(); // 返回串口的名字,例如 "COM3"qDebug() << "系统位置:" << info.systemLocation(); // 串口在系统中的路径,例如 "/dev/ttyS0" 或 "COM3"qDebug() << "描述信息:" << info.description(); // 返回串口的描述,例如 "USB-SERIAL CH340"qDebug() << "制造商:" << info.manufacturer(); // 返回串口制造商,例如 "wch.cn"qDebug() << "序列号:" << info.serialNumber(); // 返回串口序列号(若有)if (info.hasVendorIdentifier()) { // 判断是否有合法的16位制造商编码qDebug() << "厂商ID:" << info.vendorIdentifier(); // 返回厂商ID,例如 6790}if (info.hasProductIdentifier()) { // 判断是否有合法的16位产品编码qDebug() << "产品ID:" << info.productIdentifier(); // 返回产品ID,例如 29987}qDebug() << "是否忙:" << (info.isBusy() ? "是" : "否"); // 串口当前是否被占用}// 获取支持的标准波特率列表QList<qint32> baudRates = QSerialPortInfo::standardBaudRates(); // 返回可用的标准波特率链表qDebug() << "支持的标准波特率:" << baudRates;return a.exec();
}
2.3 QSerialPortInfo显示串口信息实例
#include <QCoreApplication>
#include <QSerialPortInfo>
#include <QSerialPort>
#include <QList>
#include <QDebug>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);QList<QSerialPortInfo> list = QSerialPortInfo::availablePorts(); // 获取系统中可用串口列表qDebug() << "Total number of availiable ports:" << list.count(); // 输出可用串口的总数foreach(const QSerialPortInfo &serialportinfo, list) {qDebug() << "Port: " << serialportinfo.portName(); // 串口名称,例如 "COM3"qDebug() << "Location: " << serialportinfo.systemLocation(); // 串口系统位置,例如 "COM3" 或 "/dev/ttyUSB0"qDebug() << "Description: " << serialportinfo.description(); // 串口描述信息,例如 "USB-SERIAL CH340"qDebug() << "Manufactutor: " << serialportinfo.manufacturer(); // 制造商名称,例如 "wch.cn"qDebug() << "Vendor Indentifier: " << serialportinfo.vendorIdentifier(); // 厂商ID,16位十六进制数qDebug() << "Busy: " << serialportinfo.isBusy(); // 串口是否被占用,true/false}return a.exec(); // 进入事件循环(QCoreApplication要求)
}
实现简易串口
mainwindow.h
#ifndef MYWIDGET_H
#define MYWIDGET_H#include <QWidget>
#include <QSerialPort> // 串口类
QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; } // 前向声明 UI 界面类
QT_END_NAMESPACEclass MyWidget : public QWidget
{Q_OBJECT // Qt 宏,启用信号槽机制public:MyWidget(QWidget *parent = nullptr); // 构造函数,初始化窗口~MyWidget(); // 析构函数,释放资源public slots:void onOpenPort(); // 打开串口的槽函数void reviceData(); // 接收串口消息的槽函数void on_pushButton_clicked(); // 发送消息的槽函数(按钮点击)private:Ui::MyWidget *ui; // 界面指针,管理 UI 控件QSerialPort *serial; // 串口对象,用于收发数据
};
#endif // MYWIDGET_H
mainwindow.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include <QSerialPortInfo>
#include <QDebug>
#include <QSerialPort>MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget)
{ui->setupUi(this);serial = new QSerialPort; // 创建串口对象//// 设置端口 QList<QSerialPortInfo> list = QSerialPortInfo::availablePorts(); // 获取可用串口列表for(auto info: list){ui->comboBox_port->addItem(info.portName()); // 将串口名添加到下拉框}// 设置波特率ui->comboBox_baudRate->addItem("9600", QSerialPort::Baud9600);ui->comboBox_baudRate->addItem("19200", QSerialPort::Baud19200);ui->comboBox_baudRate->addItem(QStringLiteral("38400"), QSerialPort::Baud38400);ui->comboBox_baudRate->addItem(QStringLiteral("115200"), QSerialPort::Baud115200);// 设置数据位ui->comboBox_dataBit->addItem(QStringLiteral("5"), QSerialPort::Data5);ui->comboBox_dataBit->addItem(QStringLiteral("6"), QSerialPort::Data6);ui->comboBox_dataBit->addItem(QStringLiteral("7"), QSerialPort::Data7);ui->comboBox_dataBit->addItem(QStringLiteral("8"), QSerialPort::Data8);// 设置奇偶校验位ui->comboBox_parity->addItem(tr("None"), QSerialPort::NoParity);ui->comboBox_parity->addItem(tr("Even"), QSerialPort::EvenParity);ui->comboBox_parity->addItem(tr("Odd"), QSerialPort::OddParity);ui->comboBox_parity->addItem(tr("Mark"), QSerialPort::MarkParity);ui->comboBox_parity->addItem(tr("Space"), QSerialPort::SpaceParity);// 设置停止位ui->comboBox_stopBit->addItem(QStringLiteral("1"), QSerialPort::OneStop);ui->comboBox_stopBit->addItem(QStringLiteral("2"), QSerialPort::TwoStop);// 添加流控ui->comboBox_flowBit->addItem(tr("None"), QSerialPort::NoFlowControl);ui->comboBox_flowBit->addItem(tr("RTS/CTS"), QSerialPort::HardwareControl);ui->comboBox_flowBit->addItem(tr("XON/XOFF"), QSerialPort::SoftwareControl);// 打开串口按钮点击信号连接到槽函数connect(ui->pushButton_3,&QPushButton::clicked,this,&MyWidget::onOpenPort);
}MyWidget::~MyWidget()
{delete ui; // 释放 UI 内存
}void MyWidget::onOpenPort()
{if(ui->pushButton_3->text() == "打开串口"){ // 如果按钮显示“打开串口”,说明串口未打开// 获取串口信息并设置serial->setPortName(ui->comboBox_port->currentText()); // 设置端口名serial->setBaudRate(ui->comboBox_baudRate->currentText().toInt()); // 设置波特率serial->setDataBits(QSerialPort::Data8); // 数据位serial->setParity(QSerialPort::NoParity); // 奇偶校验serial->setStopBits(QSerialPort::OneStop); // 停止位serial->setFlowControl(QSerialPort::NoFlowControl); // 流控if(serial->open(QIODevice::ReadWrite)){ // 打开串口ui->pushButton_3->setText("关闭串口"); // 按钮改为关闭串口qDebug() << "打开串口" << serial->portName() << "成功";// 串口有新数据可读时触发 reviceData() 槽connect(serial,&QSerialPort::readyRead,this,&MyWidget::reviceData);}} else {// 关闭串口if(serial->isOpen()){serial->close(); // 关闭串口}ui->pushButton_3->setText("打开串口"); // 按钮改为打开串口}
}void MyWidget::reviceData()
{QByteArray buf;buf += serial->readAll(); // 读取串口缓冲区所有数据if(!buf.isEmpty()){ui->plainTextEdit->appendPlainText(buf); // 显示到文本框}
}void MyWidget::on_pushButton_clicked()
{serial->write(ui->plainTextEdit_2->toPlainText().toUtf8()); // 将输入框数据写入串口
}