3. 环境搭建与第一个Demo:Qt Creator配置串口模块、扫描可用串口、打开/关闭串口、发送Hello World
好,咱们正式开始动手了。这一章的目标很明确——让你在Qt Creator里把串口跑起来。不搞虚的,就是配置环境、扫描串口、打开关闭、发一条"Hello World"。
我刚开始学Qt串口编程的时候,踩过一个坑:明明代码写对了,串口就是打不开。后来发现是.pro文件里忘了加serialport模块。这种低级错误,我相信你肯定不想犯。所以咱们一步步来。
3.1 Qt Creator中启用串口模块
Qt的串口功能不是默认就有的。你需要手动告诉编译器:我要用这个模块。
3.1.1 在.pro文件中添加模块
打开你的.pro文件,找到QT那一行。默认大概是这样的:
QT += core gui
改成:
QT += core gui serialport
就这么简单。加上serialport,Qt就知道你要用串口相关的类了。
3.1.2 包含必要的头文件
在需要使用串口的源文件里,加上这两行:
#include <QSerialPort>
#include <QSerialPortInfo>
QSerialPort是串口操作的核心类。QSerialPortInfo用来获取系统里有哪些可用串口。这两个类,基本够你完成90%的串口任务了。
3.2 扫描可用串口
写串口程序,第一步往往是让用户选串口。你不能硬编码一个"COM1"或者"/dev/ttyUSB0",因为不同机器上串口号不一样。
3.2.1 使用QSerialPortInfo扫描
代码很简单:
QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
foreach (const QSerialPortInfo &info, ports) {
qDebug() << "Port:" << info.portName();
qDebug() << "Description:" << info.description();
qDebug() << "Manufacturer:" << info.manufacturer();
}
这段代码会列出系统里所有可用的串口。每个串口对象info里还带着描述信息、厂商信息等。我个人习惯把这些信息显示在UI的下拉框里,方便用户选择。
3.2.2 处理扫描结果为空的情况
有时候扫描结果为空。可能是没插设备,也可能是驱动没装。我曾经在一个项目里遇到过:用户插了USB转串口,但系统没识别到。程序里扫描不到任何串口,用户就懵了。
我的建议是:如果扫描结果为空,弹个提示框,告诉用户检查硬件连接和驱动。别让用户干等。
if (ports.isEmpty()) {
QMessageBox::warning(this, "提示", "未检测到可用串口,请检查设备连接和驱动。");
}
3.3 打开与关闭串口
扫描到串口之后,下一步就是打开它。打开串口需要设置一些参数,比如波特率、数据位、停止位、校验位等。
3.3.1 创建并配置串口对象
QSerialPort *serial = new QSerialPort(this);
serial->setPortName("COM1"); // 从下拉框获取
serial->setBaudRate(QSerialPort::Baud115200);
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
这些参数要和你的下位机(比如单片机、传感器)匹配。不匹配的话,数据就是乱码,或者根本收不到。
| 参数 | 常用值 | 说明 |
|---|---|---|
| 波特率 | 9600, 115200, 460800 | 速度越快,但稳定性可能下降 |
| 数据位 | 8 | 几乎都用8位 |
| 校验位 | None, Even, Odd | None最常用 |
| 停止位 | 1, 2 | 1位最常用 |
| 流控制 | None, RTS/CTS, XON/XOFF | 一般不用 |
3.3.2 打开串口
if (serial->open(QIODevice::ReadWrite)) {
qDebug() << "串口打开成功";
} else {
qDebug() << "串口打开失败:" << serial->errorString();
}
打开失败的原因很多:串口被占用、权限不够、设备不存在等。我建议你把错误信息显示给用户,方便排查。
3.3.3 关闭串口
serial->close();
关闭串口很简单。但要注意:程序退出时一定要关闭串口,否则下次可能打不开。我习惯在析构函数里加一句serial->close()。
3.4 发送"Hello World"
串口打开了,该发数据了。咱们发个经典的"Hello World"。
3.4.1 发送字符串
QByteArray data = "Hello World\r\n";
serial->write(data);
这里我加上了\r\n(回车换行)。很多下位机协议以回车换行作为一帧数据的结束。不加的话,对方可能收不到完整数据。
QByteArray而不是直接传字符串。因为串口数据本质上是字节流,QByteArray更灵活,可以处理二进制数据。
3.4.2 检查发送是否成功
qint64 bytesWritten = serial->write(data);
if (bytesWritten == -1) {
qDebug() << "发送失败:" << serial->errorString();
} else {
qDebug() << "成功发送" << bytesWritten << "字节";
}
write()返回实际写入的字节数。如果返回-1,说明发送失败。这种情况不多见,但一旦出现,往往是串口已经关闭或者硬件故障。
3.5 完整Demo代码框架
把上面的代码串起来,就是一个完整的Demo。我贴一个简化版的:
// mainwindow.h
#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
QSerialPort *serial;
void scanPorts();
void openPort(const QString &portName);
void sendData(const QByteArray &data);
};
// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
serial = new QSerialPort(this);
scanPorts();
openPort("COM1");
sendData("Hello World\r\n");
}
MainWindow::~MainWindow()
{
serial->close();
}
void MainWindow::scanPorts()
{
QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
foreach (const QSerialPortInfo &info, ports) {
qDebug() << info.portName();
}
}
void MainWindow::openPort(const QString &portName)
{
serial->setPortName(portName);
serial->setBaudRate(QSerialPort::Baud115200);
if (serial->open(QIODevice::ReadWrite)) {
qDebug() << "打开成功";
}
}
void MainWindow::sendData(const QByteArray &data)
{
serial->write(data);
}
这个Demo虽然简单,但包含了串口编程的核心流程:扫描→打开→发送→关闭。你可以在它的基础上扩展,比如加上接收功能、UI界面等。
3.6 本章知识结构图
下面这张图帮你理清串口编程的基本流程:
3.7 常见问题与避坑指南
- 串口打不开? 检查.pro文件有没有加
serialport,检查串口是否被其他程序占用(比如串口助手)。 - 发送数据没反应? 检查波特率、数据位等参数是否匹配。我曾经调了半天,发现是波特率设成了9600,下位机是115200。
- 扫描不到串口? 检查驱动。Windows上可以去设备管理器看有没有未知设备。Linux上可以用
ls /dev/tty*查看。 - 程序退出后串口还占着? 确保在析构函数或关闭事件里调用了
serial->close()。
write()只是把数据写入发送缓冲区,并不保证数据已经发出。如果需要确认发送完成,可以连接bytesWritten信号。这个后面章节会详细讲。