SerialPort 是 Qt 框架中 Qt Serial Port 模块的核心类,用于在跨平台(Windows , Linux, macOS, QNX 等)环境下进行串行端口(COM 口/TTY)的读写操作。
它提供了同步和异步两种操作模式,但在 Qt 开发中,强烈推荐使用基于信号与槽的异步模式,以避免阻塞主线程(UI 线程)。
以下是 QSerialPort 的详细功能解析、常用配置及最佳实践:
1. 核心功能概览
| 功能类别 | 主要方法/信号 | 说明 |
|---|---|---|
| 生命周期 | open(), close(), isOpen() | 打开/关闭串口,检查状态 |
| 参数配置 | setBaudRate(), setDataBits(), setParity(), setStopBits(), setFlowControl() | 设置波特率、数据位、校验位、停止位、流控 |
| 信息获取 | portName(), baudRate(), error(), errorString() | 获取当前配置或错误信息 |
| 数据写入 | write(), waitForBytesWritten() | 发送数据(异步),或等待发送完成(同步,慎用) |
| 数据读取 | read(), readAll(), bytesAvailable(), waitForReadyRead() | 读取数据(通常配合 readyRead 信号使用) |
| 控制信号 | setDataTerminalReady(), setRequestToSend() | 控制 DTR/RTS 引脚(常用于复位单片机或控制继电器) |
| 关键信号 | readyRead, errorOccurred, bytesWritten | 数据到达、发生错误、数据发送完成 |
2. 标准初始化 流程 (代码片段)
这是最标准的异步使用方式:
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QDebug>
// 1. 创建对象
QSerialPort *serial = new QSerialPort(this);
// 2. 设置端口名 (例如 Windows 的 "COM3", Linux 的 "/dev/ttyUSB0")
serial->setPortName("COM3");
// 3. 打开端口 (读写模式)
if (!serial->open(QIODevice::ReadWrite)) {
qWarning() << "打开失败:" << serial->errorString();
delete serial;
return;
}
// 4. 配置通信参数
serial->setBaudRate(QSerialPort::Baud9600); // 波特率 9600
serial->setDataBits(QSerialPort::Data8); // 8 位数据
serial->setParity(QSerialPort::NoParity); // 无校验
serial->setStopBits(QSerialPort::OneStop); // 1 位停止位
serial->setFlowControl(QSerialPort::NoFlowControl); // 无流控
// 5. 连接信号 (异步处理的核心)
connect(serial, &QSerialPort::readyRead, this, [=]() {
QByteArray data = serial->readAll();
qDebug() << "收到数据:" << data.toHex(' ');
// 在这里处理数据解析逻辑
});
connect(serial, QOverload<QSerialPort::SerialPortError>::of(&QSerialPort::errorOccurred),
this, [=](QSerialPort::SerialPortError error) {
if (error == QSerialPort::NoError) return;
qWarning() << "串口错误:" << serial->errorString();
});
// 6. 发送数据
serial->write("Hello World");
// 注意:write 是异步的,数据放入缓冲区后函数立即返回
3. 关键参数详解
A. 波特率 (BaudRate)
支持标准值:Baud1200 到 Baud115200,甚至更高(如 Baud230400, Baud921600),具体取决于硬件驱动支持。
serial->setBaudRate(QSerialPort::Baud115200);
// 或者使用整数值 (Qt 5.1+)
serial->setBaudRate(115200);
B. 数据位、校验位、停止位
- DataBits: Data5 ~ Data8 (常用 Data8)
- Parity: NoParity (无), EvenParity (偶), OddParity (奇), SpaceParity, MarkParity
- StopBits: OneStop, OneAndHalfStop, TwoStop
C. 流控制 (FlowControl)
防止数据丢失的重要机制:
NoFlowControl: 无流控(最常用,适用于短距离或低速)。HardwareControl: 硬件流控 (RTS/CTS),需要接线支持。SoftwareControl: 软件流控 (XON/XOFF)。
4. 常见痛点与解决方案
痛点 1:数据接收不完整 (粘包/拆包)
readyRead 信号触发时,操作系统内核缓冲区可能有 1 个字节,也可能有 100 个字节,不一定刚好是一帧完整的数据。
解决方案:建立应用层缓冲区和状态机。
// 伪代码示例:假设协议以 0xAA 开头,0x55 结尾
QByteArray m_buffer;
void onReadyRead() {
m_buffer.append(serial->readAll()); // 追加到缓冲区
while (true) {
int start = m_buffer.indexOf(0xAA);
if (start == -1) {
m_buffer.clear(); // 没找到头,丢弃(或根据策略保留)
break;
}
int end = m_buffer.indexOf(0x55, start);
if (end == -1) {
break; // 还没收完,等待下一次 readyRead
}
// 提取完整帧 [start, end]
QByteArray frame = m_buffer.mid(start, end - start + 1);
processFrame(frame); // 处理这一帧
// 从缓冲区移除已处理的数据
m_buffer.remove(0, end + 1);
}
}
痛点 2:界面卡顿
如果在 readyRead 槽函数中进行了复杂的耗时计算(如大量数据解析、绘图、数据库写入),会阻塞 UI。
解决方案:
- 快速读取:在槽函数中只执行
readAll()并将数据存入队列。 - 异步处理:发射一个自定义信号,让其他对象(或工作线程)处理数据。
- 多线程:将
QSerialPort对象moveToThread到一个专门的 Worker 线程中运行。
痛点 3: DTR/RTS 引脚控制
很多单片机(如 Arduino)通过 DTR 引脚复位,或者某些工业设备需要 RTS 信号使能。
// 拉低 DTR (常用于复位 Arduino)
serial->setDataTerminalReady(false);
QThread::msleep(100);
serial->setDataTerminalReady(true);
// 控制 RTS
serial->setRequestToSend(true);
痛点 4: 枚举可用串口
不要硬编码 "COM3",因为插拔顺序会变。
#include <QSerialPortInfo>
const auto infos = QSerialPortInfo::availablePorts();
for (const QSerialPortInfo &info : infos) {
qDebug() << "端口名:" << info.portName();
qDebug() << "描述:" << info.description();
qDebug() << "制造商:" << info.manufacturer();
// 可以根据 vendorIdentifier 和 productIdentifier 过滤特定设备
}
5. 进阶:Qt Serial Bus (针对 Modbus 等)
如果你需要做 Modbus RTU, CAN Bus 等工业协议 ,不要手动解析 QSerialPort 的字节流。Qt 提供了更上层的模块 Qt Serial Bus。
- 类:
QModbusRtuSerialMaster - 优势: 自动处理地址校验、CRC 校验、超时重发、帧拼接。
- 依赖:
QT += serialbus
6. 调试技巧
- 虚拟串口: 使用 com0com (Windows) 或 socat (Linux) 创建一对虚拟串口(如 COM3 <-> COM4),用两个程序或一个程序的两个实例互发数据测试。
- 十六进制显示: 调试二进制协议时,务必使用 data.toHex(' ') 打印,不要直接 qDebug() << data,否则不可见字符会导致困惑。
- 错误码: 遇到 ResourceError 通常意味着端口被其他程序占用(如串口助手没关);PermissionError 在 Linux 下通常是用户组权限问题。
7871

被折叠的 条评论
为什么被折叠?



