QSerialPort 是 Qt 提供的一个功能强大、简单易用的串口通信类。通过本文的学习,您应该对 QSerialPort 的基本使用、高级应用技巧及相关注意事项有了全面的理解。在实际项目中,QSerialPort 可以帮助实现与外部设备的串口通信,确保数据的可靠传输和接收。希望本文能帮助您更好地理解和使用 QSerialPort,在各种应用场景中实现高效稳定的串口通信。
🧑 博主简介:现任阿里巴巴嵌入式技术专家,15年工作经验,深耕嵌入式+人工智能领域,精通嵌入式领域开发、技术管理、简历招聘面试。CSDN优质创作者,提供产品测评、学习辅导、简历面试辅导、毕设辅导、项目开发、C/C++/Java/Python/Linux/AI等方面的服务,如有需要请站内私信或者联系任意文章底部的的VX名片(ID:
gylzbk
)
💬 博主粉丝群介绍:① 群内初中生、高中生、本科生、研究生、博士生遍布,可互相学习,交流困惑。② 热榜top10的常客也在群里,也有数不清的万粉大佬,可以交流写作技巧,上榜经验,涨粉秘籍。③ 群内也有职场精英,大厂大佬,可交流技术、面试、找工作的经验。④ 进群免费赠送写作秘籍一份,助你由写作小白晋升为创作大佬。⑤ 进群赠送CSDN评论防封脚本,送真活跃粉丝,助你提升文章热度。有兴趣的加文末联系方式,备注自己的CSDN昵称,拉你进群,互相学习共同进步。
【QT开发】串口通信管理QSerialPort类详解及实战应用
-
- 🌟 概述
- 📖 QSerialPort 类介绍
-
- 常用方法
- 信号和槽机制
- 常用枚举类型
- 💻 示例代码
-
- 示例:基本串口通信操作
-
- 头文件:mainwindow.h
- 源文件:mainwindow.cpp
- 代码解释
- 方法分析
- 高级应用技巧
-
- 1. 实现串口数据完整性校验
- 2. 使用线程处理串口通信
- 3. 使用信号槽机制处理串口事件
- 4. 动态调整串口参数
- 🚩 常见问题及解决方法
-
- 1. 串口无法打开
- 2. 数据丢失或乱码
- 3. 串口通信延迟
- 📌 总结
🌟 概述
QSerialPort 是 Qt 串口通信模块中的一个类,用于管理和控制串行端口通信。它提供了一种与串行设备(如传感器、终端、工业设备等)进行数据通信的便捷方式。通过 QSerialPort,您可以打开和配置串口、发送和接收数据、处理串口错误等。
QSerialPort 常用于需要与硬件设备进行串口通信的场景,比如串口调试工具,工业自动化系统,嵌入式系统通信等。
📖 QSerialPort 类介绍
在 Qt 官方文档中,QSerialPort
类的定义如下:
class QSerialPort : public QIODevice
{
Q_OBJECT
//...
}
QSerialPort 继承自 QIODevice,是一个用于串口通信的类。以下是一些关键特性和功能:
- 打开和关闭串口:能够打开和关闭指定的串口。
- 配置串口参数:支持设置串口的波特率、数据位、停止位、校验位和流控制。
- 读写数据:能够发送和接收数据。
- 事件处理:通过信号槽机制处理串口的各种事件(如数据可读、错误发生等)。
常用方法
以下是 QSerialPort 类中一些常用的方法及其简要介绍:
构造函数和析构函数:
QSerialPort(QObject *parent = nullptr)
:默认构造函数。QSerialPort(const QString &name, QObject *parent = nullptr)
:通过串口名称构造。QSerialPort(const QSerialPortInfo &info, QObject *parent = nullptr)
:通过 QSerialPortInfo 对象构造。~QSerialPort()
:析构函数,关闭串口并释放资源。
打开和关闭串口:
bool open(QIODevice::OpenMode mode)
:以指定模式打开串口。void close()
:关闭串口。
配置串口参数:
bool setBaudRate(qint32 baudRate, Directions directions = AllDirections)
:设置波特率。qint32 baudRate(Directions directions = AllDirections) const
:返回当前波特率。bool setDataBits(QSerialPort::DataBits dataBits)
:设置数据位。QSerialPort::DataBits dataBits() const
:返回当前数据位。bool setParity(QSerialPort::Parity parity)
:设置校验位。QSerialPort::Parity parity() const
:返回当前校验位。bool setStopBits(QSerialPort::StopBits stopBits)
:设置停止位。QSerialPort::StopBits stopBits() const
:返回当前停止位。bool setFlowControl(QSerialPort::FlowControl flowControl)
:设置流控制。QSerialPort::FlowControl flowControl() const
:返回当前流控制。
读写数据:
qint64 write(const char *data, qint64 maxSize)
:写入数据到串口。qint64 write(const QByteArray &byteArray)
:写入数据到串口。QByteArray readAll()
:读取所有可用数据。qint64 read(char *data, qint64 maxSize)
:读取数据。qint64 bytesAvailable() const
:返回可读的字节数。bool waitForReadyRead(int msecs)
:等待数据可读。
串口控制:
bool setBreakEnabled(bool set)
:设置或清除串口的断点信号。bool flush()
:刷新串口缓冲区。
信号和槽机制
以下是 QSerialPort 类中一些常用的信号及其简要介绍:
- 信号:
void readyRead()
:当有数据可读时发出信号。void bytesWritten(qint64 bytes)
:当数据写入完成时发出信号。void errorOccurred(QSerialPort::SerialPortError error)
:当错误发生时发出信号。
常用枚举类型
以下是 QSerialPort 类中一些常用的枚举类型及其简要介绍:
QSerialPort::BaudRate:波特率。
Baud1200
、Baud2400
、Baud4800
、Baud9600
、Baud19200
、Baud38400
、Baud57600
、Baud115200
、UnknownBaud
。
QSerialPort::DataBits:数据位。
Data5
、Data6
、Data7
、Data8
、UnknownDataBits
。
QSerialPort::Parity:校验位。
NoParity
、EvenParity
、OddParity
、MarkParity
、SpaceParity
、UnknownParity
。
QSerialPort::StopBits:停止位。
OneStop
、OneAndHalfStop
、TwoStop
、UnknownStopBits
。
QSerialPort::FlowControl:流控制。
NoFlowControl
、HardwareControl
、SoftwareControl
、UnknownFlowControl
。
QSerialPort::SerialPortError:串口错误类型。
NoError
、DeviceNotFoundError
、PermissionError
、OpenError
、ParityError
、FramingError
、BreakConditionError
、WriteError
、ReadError
、ResourceError
、UnsupportedOperationError
、UnknownError
。
💻 示例代码
下面是一个简单的示例,用来演示如何使用 QSerialPort 进行基本的串口通信操作。该示例展示了如何打开和关闭串口、配置串口参数、发送和接收数据以及处理串口错误。
示例:基本串口通信操作
头文件:mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QPushButton>
#include <QVBoxLayout>
#include <QTextEdit>
#include <QLabel>
#include <QComboBox>
#include <QLineEdit>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void openSerialPort();
void closeSerialPort();
void sendData();
void readData();
void handleError(QSerialPort::SerialPortError error);
private:
QSerialPort *serialPort;
QTextEdit *outputTextEdit;
QLineEdit *inputLineEdit;
QPushButton *openButton;
QPushButton *closeButton;
QPushButton *sendButton;
QComboBox *portNameComboBox;
QComboBox *baudRateComboBox;
};
#endif // MAINWINDOW_H
源文件:mainwindow.cpp
#include "mainwindow.h"
#include <QSerialPortInfo>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), serialPort(new QSerialPort(this))
{
setWindowTitle("QSerialPort Example");
resize(400, 300);
// 创建UI组件
portNameComboBox = new QComboBox();
baudRateComboBox = new QComboBox();
openButton = new QPushButton("Open Serial Port");
closeButton = new QPushButton("Close Serial Port");
sendButton = new QPushButton("Send Data");
inputLineEdit = new QLineEdit();
outputTextEdit = new QTextEdit();
outputTextEdit->setReadOnly(true);
// 填充端口名称和波特率
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
portNameComboBox->addItem(info.portName());
}
baudRateComboBox->addItem("9600", QSerialPort::Baud9600);
baudRateComboBox->addItem("19200", QSerialPort::Baud19200);
baudRateComboBox->addItem("38400", QSerialPort::Baud38400);
baudRateComboBox->addItem("115200", QSerialPort::Baud115200);
// 布局管理
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(new QLabel("Port Name:"));
layout->addWidget(portNameComboBox);
layout->addWidget(new QLabel("Baud Rate:"));
layout->addWidget(baudRateComboBox);
layout->addWidget(openButton);
layout->addWidget(closeButton);
layout->addWidget(new QLabel("Input:"));
layout->addWidget(inputLineEdit);
layout->addWidget(sendButton);
layout->addWidget(new QLabel("Output:"));
layout->addWidget(outputTextEdit);
QWidget *centralWidget = new QWidget;
centralWidget->setLayout(layout);
setCentralWidget(centralWidget);
// 连接信号和槽
connect(openButton, &QPushButton::clicked, this, &MainWindow::openSerialPort);
connect(closeButton, &QPushButton::clicked, this, &MainWindow::closeSerialPort);
connect(sendButton, &QPushButton::clicked, this, &MainWindow::sendData);
connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::readData);
connect(serialPort, &QSerialPort::errorOccurred, this, &MainWindow::handleError);
}
MainWindow::~MainWindow() = default;
void MainWindow::openSerialPort()
{
serialPort->setPortName(portNameComboBox->currentText());
serialPort->setBaudRate(static_cast<QSerialPort::BaudRate>(baudRateComboBox->currentData().toInt()));
serialPort->setDataBits(QSerialPort::Data8);
serialPort->setParity(QSerialPort::NoParity);
serialPort->setStopBits(QSerialPort::OneStop);
serialPort->setFlowControl(QSerialPort::NoFlowControl);
if (serialPort->open(QIODevice::ReadWrite)) {
outputTextEdit->append("Serial port opened.");
} else {
outputTextEdit->append("Failed to open serial port: " + serialPort->errorString());
}
}
void MainWindow::closeSerialPort()
{
if (serialPort->isOpen())
serialPort->close();
outputTextEdit->append("Serial port closed.");
}
void MainWindow::sendData()
{
if (serialPort->isOpen()) {
QByteArray data = inputLineEdit->text().toUtf8();
serialPort->write(data);
outputTextEdit->append("Data sent: " + data);
} else {
outputTextEdit->append("Serial port is not open.");
}
}
void MainWindow::readData()
{
QByteArray data = serialPort->readAll();
outputTextEdit->append("Data received: " + data);
}
void MainWindow::handleError(QSerialPort::SerialPortError error)
{
if (error == QSerialPort::ResourceError) {
outputTextEdit->append("Critical Error: " + serialPort->errorString());
closeSerialPort();
}
}
代码解释
创建主窗口,并设置其标题和大小:
MainWindow(QWidget *parent) : QMainWindow(parent), serialPort(new QSerialPort(this)) { setWindowTitle("QSerialPort Example"); resize(400, 300); }
创建 QSerialPort 对象:
serialPort = new QSerialPort(this);
创建 UI 组件,包括端口名选择框、波特率选择框、按钮、文本编辑区域等:
portNameComboBox = new QComboBox(); baudRateComboBox = new QComboBox(); openButton = new QPushButton("Open Serial Port"); closeButton = new QPushButton("Close Serial Port"); sendButton = new QPushButton("Send Data"); inputLineEdit = new QLineEdit(); outputTextEdit = new QTextEdit(); outputTextEdit->setReadOnly(true);
填充端口名称和波特率:
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { portNameComboBox->addItem(info.portName()); } baudRateComboBox->addItem("9600", QSerialPort::Baud9600); baudRateComboBox->addItem("19200", QSerialPort::Baud19200); baudRateComboBox->addItem("38400", QSerialPort::Baud38400); baudRateComboBox->addItem("115200", QSerialPort::Baud115200);
布局管理,将 UI 组件添加到布局中并设置为窗口中央控件:
QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(new QLabel("Port Name:")); layout->addWidget(portNameComboBox); layout->addWidget(new QLabel("Baud Rate:")); layout->addWidget(baudRateComboBox); layout->addWidget(openButton); layout->addWidget(closeButton); layout->addWidget(new QLabel("Input:")); layout->addWidget(inputLineEdit); layout->addWidget(sendButton); layout->addWidget(new QLabel("Output:")); layout->addWidget(outputTextEdit); QWidget *centralWidget = new QWidget; centralWidget->setLayout(layout); setCentralWidget(centralWidget);
连接按钮点击信号和 QSerialPort 信号到槽函数:
connect(openButton, &QPushButton::clicked, this, &MainWindow::openSerialPort); connect(closeButton, &QPushButton::clicked, this, &MainWindow::closeSerialPort); connect(sendButton, &QPushButton::clicked, this, &MainWindow::sendData); connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::readData); connect(serialPort, &QSerialPort::errorOccurred, this, &MainWindow::handleError);
实现打开串口的槽函数:
void MainWindow::openSerialPort() { serialPort->setPortName(portNameComboBox->currentText()); serialPort->setBaudRate(static_cast<QSerialPort::BaudRate>(baudRateComboBox->currentData().toInt())); serialPort->setDataBits(QSerialPort::Data8); serialPort->setParity(QSerialPort::NoParity); serialPort->setStopBits(QSerialPort::OneStop); serialPort->setFlowControl(QSerialPort::NoFlowControl); if (serialPort->open(QIODevice::ReadWrite)) { outputTextEdit->append("Serial port opened."); } else { outputTextEdit->append("Failed to open serial port: " + serialPort->errorString()); } }
实现关闭串口的槽函数:
void MainWindow::closeSerialPort() { if (serialPort->isOpen()) serialPort->close(); outputTextEdit->append("Serial port closed."); }
实现发送数据的槽函数:
void MainWindow::sendData() { if (serialPort->isOpen()) { QByteArray data = inputLineEdit->text().toUtf8(); serialPort->write(data); outputTextEdit->append("Data sent: " + data); } else { outputTextEdit->append("Serial port is not open."); } }
实现接收数据的槽函数:
void MainWindow::readData() { QByteArray data = serialPort->readAll(); outputTextEdit->append("Data received: " + data); }
实现处理串口错误的槽函数:
void MainWindow::handleError(QSerialPort::SerialPortError error) { if (error == QSerialPort::ResourceError) { outputTextEdit->append("Critical Error: " + serialPort->errorString()); closeSerialPort(); } }
方法分析
构造函数:
QSerialPort(QObject *parent = nullptr) QSerialPort(const QString &name, QObject *parent = nullptr) QSerialPort(const QSerialPortInfo &info, QObject *parent = nullptr)
用于创建一个 QSerialPort 对象,指定串口名称或通过 QSerialPortInfo 对象来创建。
示例:
QSerialPort serialPort1; QSerialPort serialPort2("COM1"); QSerialPortInfo portInfo; QSerialPort serialPort3(portInfo);
bool open(QIODevice::OpenMode mode)
:以指定模式打开串口,可以是QIODevice::ReadOnly
,QIODevice::WriteOnly
, 或QIODevice::ReadWrite
。bool open(QIODevice::OpenMode mode)
以指定的模式打开串口,返回值表示操作是否成功。
示例:
if (serialPort.open(QIODevice::ReadWrite)) { qDebug() << "Serial port opened successfully."; } else { qDebug() << "Failed to open serial port:" << serialPort.errorString(); }
void close()
:关闭串口。void close()
关闭当前打开的串口。
示例:
serialPort.close(); qDebug() << "Serial port closed.";
bool setBaudRate(qint32 baudRate, Directions directions = AllDirections)
:设置串口的波特率。bool setBaudRate(qint32 baudRate, Directions directions = AllDirections)
设置串口的波特率,可以指定方向(读、写或两者)。返回值表示操作是否成功。
示例:
if (serialPort.setBaudRate(QSerialPort::Baud9600)) { qDebug() << "Baud rate set to 9600."; } else { qDebug() << "Failed to set baud rate:" << serialPort.errorString(); }
qint32 baudRate(Directions directions = AllDirections) const
:返回当前波特率。qint32 baudRate(Directions directions = AllDirections) const
返回当前设置的波特率。
示例:
qint32 currentBaudRate = serialPort.baudRate(); qDebug() << "Current baud rate:" << currentBaudRate;
bool setDataBits(QSerialPort::DataBits dataBits)
:设置数据位。bool setDataBits(QSerialPort::DataBits dataBits)
设置串口的数据位,常用值包括:
QSerialPort::Data8
、QSerialPort::Data7
等。返回值表示操作是否成功。示例:
if (serialPort.setDataBits(QSerialPort::Data8)) { qDebug() << "Data bits set to 8."; } else { qDebug() << "Failed to set data bits:" << serialPort.errorString(); }
QSerialPort::DataBits dataBits() const
:返回当前数据位。QSerialPort::DataBits dataBits() const
返回当前设置的数据位。
示例:
QSerialPort::DataBits currentDataBits = serialPort.dataBits(); qDebug() << "Current data bits:" << currentDataBits;
bool setParity(QSerialPort::Parity parity)
:设置校验位。bool setParity(QSerialPort::Parity parity)
设置串口的校验位,包括:无校验(
QSerialPort::NoParity
)、偶校验(QSerialPort::EvenParity
)、奇校验(QSerialPort::OddParity
)等。返回值表示操作是否成功。示例:
if (serialPort.setParity(QSerialPort::NoParity)) { qDebug() << "No parity set."; } else { qDebug() << "Failed to set parity:" << serialPort.errorString(); }
QSerialPort::Parity parity() const
:返回当前校验位。QSerialPort::Parity parity() const
返回当前设置的校验位。
示例:
QSerialPort::Parity currentParity = serialPort.parity(); qDebug() << "Current parity:" << currentParity;
bool setStopBits(QSerialPort::StopBits stopBits)
:设置停止位。bool setStopBits(QSerialPort::StopBits stopBits)
设置串口的停止位,包括:一个停止位(
QSerialPort::OneStop
)和两个停止位(QSerialPort::TwoStop
)等。返回值表示操作是否成功。示例:
if (serialPort.setStopBits(QSerialPort::OneStop)) { qDebug() << "Stop bits set to one."; } else { qDebug() << "Failed to set stop bits:" << serialPort.errorString(); }
QSerialPort::StopBits stopBits() const
:返回当前停止位。QSerialPort::StopBits stopBits() const
返回当前设置的停止位。
示例:
QSerialPort::StopBits currentStopBits = serialPort.stopBits(); qDebug() << "Current stop bits:" << currentStopBits;
bool setFlowControl(QSerialPort::FlowControl flowControl)
:设置流控制。bool setFlowControl(QSerialPort::FlowControl flowControl)
设置串口的流控制,包括:无流控制(
QSerialPort::NoFlowControl
)、硬件流控制(QSerialPort::HardwareControl
)、软件流控制(QSerialPort::SoftwareControl
)等。返回值表示操作是否成功。示例:
if (serialPort.setFlowControl(QSerialPort::NoFlowControl)) { qDebug() << "No flow control set."; } else { qDebug() << "Failed to set flow control:" << serialPort.errorString(); }
QSerialPort::FlowControl flowControl() const
:返回当前流控制。QSerialPort::FlowControl flowControl() const
返回当前设置的流控制方式。
示例:
QSerialPort::FlowControl currentFlowControl = serialPort.flowControl(); qDebug() << "Current flow control:" << currentFlowControl;
qint64 write(const char *data, qint64 maxSize)
:写入数据到串口。qint64 write(const char *data, qint64 maxSize)
将指定的数据写入串口,返回写入的字节数。如果返回值为-1,则表示写入失败。
示例:
const char *data = "Hello"; qint64 bytesWritten = serialPort.write(data, strlen(data)); qDebug() << "Bytes written:" << bytesWritten;
qint64 write(const QByteArray &byteArray)
:写入数据到串口。qint64 write(const QByteArray &byteArray)
将 QByteArray 类型的数据写入串口,返回写入的字节数。如果返回值为-1,则表示写入失败。
示例:
QByteArray data = "Hello"; qint64 bytesWritten = serialPort.write(data); qDebug() << "Bytes written:" << bytesWritten;
QByteArray readAll()
:读取所有可用数据。QByteArray readAll()
读取串口中所有可用的数据,并将其作为 QByteArray 返回。
示例:
QByteArray data = serialPort.readAll(); qDebug() << "Data received:" << data;
qint64 read(char *data, qint64 maxSize)
:读取数据。qint64 read(char *data, qint64 maxSize)
从串口读取最多 maxSize 字节的数据,存储到 data 缓冲区中,返回读取的字节数。如果返回值为-1,则表示读取失败。
示例:
char buffer[100]; qint64 bytesRead = serialPort.read(buffer, sizeof(buffer)); qDebug() << "Bytes read:" << bytesRead;
qint64 bytesAvailable() const
:返回可读的字节数。qint64 bytesAvailable() const
该方法返回单个读缓冲区中当前要读取的字节数。它可以用于在读取数据之前确定有多少数据可用。
示例:
qint64 availableBytes = serialPort->bytesAvailable(); qDebug() << "Bytes available to read:" << availableBytes;
bool waitForReadyRead(int msecs)
:等待数据可读。bool waitForReadyRead(int msecs)
该方法在指定的毫秒数内等待数据变得可读,如果在超时时间内有数据可读,则返回
true
;否则返回false
。它通常与无事件驱动的嵌入式系统中的阻塞 I/O 一起使用。示例:
if (serialPort->waitForReadyRead(5000)) { QByteArray data = serialPort->readAll(); qDebug() << "Data received:" << data; } else { qDebug() << "Read timed out."; }
bool setBreakEnabled(bool set)
:设置或清除串口的断点信号。bool setBreakEnabled(bool set)
该方法设置或清除串口的断点信号。如果成功设置或清除断点信号,返回
true
;否则,返回false
。示例:
if (!serialPort->setBreakEnabled(true)) { qDebug() << "Failed to set break:" << serialPort->errorString(); }
bool flush()
:刷新串口缓冲区。bool flush()
该方法刷新串口的输入和输出缓冲区。如果成功,则返回
true
;否则,返回false
。示例:
if (serialPort->flush()) { qDebug() << "Serial port buffer flushed."; } else { qDebug() << "Failed to flush buffer:" << serialPort->errorString(); }
高级应用技巧
1. 实现串口数据完整性校验
在串口通信中,使用校验和(checksum)或 CRC(循环冗余校验)来确保数据的完整性是常见做法。以下是一个简单的校验和计算示例:
uint8_t calculateChecksum(const QByteArray &data)
{
uint8_t checksum = 0;
for (char byte : data) {
checksum += static_cast<uint8_t>(byte);
}
return checksum;
}
在发送和接收数据时,可以附加校验和,并在接收时进行验证:
void MainWindow::sendDataWithChecksum(const QByteArray &data)
{
QByteArray packet = data;
uint8_t checksum = calculateChecksum(data);
packet.append(checksum);
serialPort->write(packet);
}
void MainWindow::readData()
{
QByteArray data = serialPort->readAll();
if (!data.isEmpty()) {
uint8_t receivedChecksum = data.at(data.size() - 1);
QByteArray payload = data.left(data.size() - 1);
if (calculateChecksum(payload) == receivedChecksum) {
outputTextEdit->append("Data received (valid): " + payload);
} else {
outputTextEdit->append("Data received (invalid checksum).");
}
}
}
2. 使用线程处理串口通信
为了避免在主线程中处理串口通信而导致界面卡顿,可以将串口通信放到单独的线程中。使用工作者/线程模式实现:
class SerialWorker : public QObject
{
Q_OBJECT
public:
SerialWorker(const QString &portName, QObject *parent = nullptr)
: QObject(parent), serialPort(new QSerialPort(portName, this)) {}
public slots:
void start()
{
if (serialPort->open(QIODevice::ReadWrite)) {
connect(serialPort, &QSerialPort::readyRead, this, &SerialWorker::readData);
qDebug() << "Serial port opened.";
} else {
qDebug() << "Failed to open serial port:" << serialPort->errorString();
}
}
void stop()
{
if (serialPort->isOpen()) {
serialPort->close();
}
qDebug() << "Serial port closed.";
}
void readData()
{
QByteArray data = serialPort->readAll();
emit dataReceived(data);
}
void writeData(const QByteArray &data)
{
if (serialPort->isOpen()) {
serialPort->write(data);
}
}
signals:
void dataReceived(const QByteArray &data);
private:
QSerialPort *serialPort;
};
在主程序中启动串口通信线程:
SerialWorker *worker = new SerialWorker("COM1");
QThread *thread = new QThread;
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &SerialWorker::start);
connect(thread, &QThread::finished, worker, &SerialWorker::stop);
connect(worker, &SerialWorker::dataReceived, [](const QByteArray &data) {
qDebug() << "Data received in main thread:" << data;
});
thread->start();
// 在需要时停止和清理线程
thread->quit();
thread->wait();
delete worker;
delete thread;
通过将串口通信移到单独的线程中,可以避免主线程的卡顿,确保界面响应流畅。
3. 使用信号槽机制处理串口事件
充分利用 QSerialPort 的信号槽机制,可以处理串口的各种事件(如数据可读、数据写入完成、错误发生等),从而实现异步通信。
// 主窗口类中的信号和槽连接
connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::readData);
connect(serialPort, &QSerialPort::bytesWritten, this, &MainWindow::bytesWrittenHandler);
connect(serialPort, &QSerialPort::errorOccurred, this, &MainWindow::handleError);
// 槽函数
void MainWindow::bytesWrittenHandler(qint64 bytes)
{
qDebug() << bytes << "bytes written.";
}
void MainWindow::handleError(QSerialPort::SerialPortError error)
{
if (error == QSerialPort::ResourceError) {
qDebug() << "Critical Error:" << serialPort->errorString();
closeSerialPort();
}
}
4. 动态调整串口参数
在有些应用场景中,需要根据外部因素动态调整串口参数(如波特率、数据位等)。可以使用 QSerialPort 提供的相关方法来实现。
void MainWindow::adjustSerialPortParameters(qint32 baudRate, QSerialPort::DataBits dataBits)
{
if (serialPort->isOpen()) {
serialPort->setBaudRate(baudRate);
serialPort->setDataBits(dataBits);
qDebug() << "Serial port parameters adjusted:" <<
"BaudRate:" << baudRate <<
"DataBits:" << dataBits;
}
}
通过提供一个接口,允许用户或程序动态调整串口参数,以满足不同的通信需求。
🚩 常见问题及解决方法
1. 串口无法打开
在使用 QSerialPort 打开串口时,可能会遇到各种错误,其中一些常见原因包括:
- 串口被占用:检查串口是否被其他程序占用。
- 权限问题:在 Linux 系统上,确保当前用户有权访问串口设备(通常需要属于
dialout
组)。
解决方法:
if (!serialPort->open(QIODevice::ReadWrite)) {
qDebug() << "Failed to open serial port:" << serialPort->errorString();
}
2. 数据丢失或乱码
数据丢失或乱码是串口通信中的常见问题,通常是由于串口参数(波特率、数据位、校验位、停止位)设置不匹配导致的。
解决方法:
- 确保通信双方的串口参数一致。
- 检查串口缓冲区是否溢出,尝试增大缓冲区大小或更频繁地读取数据。
- 使用适当的校验和/CRC 校验确保数据完整性。
3. 串口通信延迟
在某些情况下,串口通信可能会出现延迟,导致数据传输不及时。这可能是由多种因素引起的,包括硬件性能限制、串口配置、线程调度等。
解决方法:
- 尽量使用更高的波特率,以提高数据传输速度。
- 将串口通信移到单独的线程中,避免主线程卡顿影响串口通信。
- 优化数据传输和处理逻辑,减少不必要的延迟。
📌 总结
QSerialPort 是 Qt 提供的一个功能强大、简单易用的串口通信类。通过本文的学习,您应该对 QSerialPort 的基本使用、高级应用技巧及相关注意事项有了全面的理解。在实际项目中,QSerialPort 可以帮助实现与外部设备的串口通信,确保数据的可靠传输和接收。希望本文能帮助您更好地理解和使用 QSerialPort,在各种应用场景中实现高效稳定的串口通信。