1. 课程导入

各位同学好,我是这门课的主讲。咱们直接进入正题——Qt串口编程与内存管理。说实话,这两个话题放在一起,是因为我在实际项目中吃过不少亏。串口数据收发看似简单,但一旦加上内存泄漏、缓冲区溢出这些问题,调试起来真是让人头疼。

这门课一共30章,我会带着大家从零开始,一步步搭建一个健壮的串口通信程序。今天先搞定两件事:搞清楚我们要学什么,以及把开发环境搭好。

1.1 Qt串口编程概述

串口通信,说白了就是设备之间通过一根线,一个bit一个bit地传数据。在嵌入式领域,串口无处不在——GPS模块、传感器、工业控制器、单片机调试……我做过一个车载终端项目,同时要跟三个串口设备通信,那时候就深刻体会到,串口编程绝不仅仅是打开端口、读写数据那么简单。

Qt从5.1版本开始提供了QSerialPort类,封装了跨平台的串口操作。你想想看,Windows上用CreateFile,Linux上用open,macOS上又是另一套——Qt帮你把这些差异全抹平了。我个人习惯用Qt做串口工具,因为代码写一遍,到处编译都能跑。

核心知识点:
  • 串口通信基础:波特率、数据位、停止位、校验位
  • QSerialPort类:打开/关闭端口、读写数据、信号槽机制
  • QSerialPortInfo类:枚举可用串口、获取设备信息
  • 异步与同步编程:事件驱动 vs 阻塞读写

我记得刚接触Qt串口时,最让我困惑的是它的异步机制。数据来了怎么通知我?答案是信号槽——readyRead()信号一发射,你就知道缓冲区里有数据了。这个机制用好了,程序既高效又优雅。

1.2 内存管理的重要性

聊完串口,咱们说说内存管理。为什么要把这两个话题放一起?因为串口程序是内存问题的重灾区。

你想想看,串口数据是源源不断进来的。如果每次收到数据都new一块内存,忘了delete,程序跑几个小时就崩了。我在一个工业采集项目中遇到过这种情况——设备运行8小时后突然卡死,查了两天才发现是串口接收缓冲区没及时释放。

常见内存问题:
  • 内存泄漏:new了没delete,程序内存只增不减
  • 野指针:指向已释放的内存,程序随时崩溃
  • 缓冲区溢出:写入数据超过分配空间,破坏相邻内存
  • 重复释放:同一块内存释放两次,double free错误

Qt有自己的内存管理机制——对象树和父子关系。父对象销毁时,会自动删除所有子对象。这个机制用好了能省不少事,但也不是万能的。比如你用QThread时,跨线程的对象管理就得特别小心。

我曾经在一个项目中,因为没搞清楚Qt的内存所有权,导致一个QTimer对象被重复释放,程序直接崩溃。从那以后,我养成了一个习惯:每个new出来的对象,都要明确它的归属和生命周期。

1.3 Qt开发环境安装与配置

好,理论说完了,咱们动手搭环境。我建议用Qt Creator + MinGW(Windows)或系统自带编译器(Linux/macOS)。MSVC也可以,但MinGW更轻量,适合嵌入式开发。

1.3.1 下载与安装

  1. 访问Qt官网,下载Qt在线安装器
  2. 选择Qt 5.15或6.x版本(我推荐5.15 LTS,稳定)
  3. 组件选择时,勾选:
    • Qt 5.15.2 → MinGW 8.1.0 64-bit
    • Qt 5.15.2 → Qt Serial Port
    • Developer and Designer Tools → Qt Creator
  4. 安装路径不要有中文和空格
小技巧: 如果你硬盘空间够,可以把Debug和Release的库都装上。调试时用Debug库,发布时用Release库,省得后面再补装。

1.3.2 配置Qt Creator

安装完成后,打开Qt Creator,配置编译器:

步骤 操作 说明
1 工具 → 选项 → Kits 查看已检测到的编译器
2 添加 → MinGW 如果自动检测不到,手动指定g++.exe路径
3 选择Qt版本 确保qmake路径指向安装目录
4 构建套件(Kit) 名称建议写"Qt 5.15.2 MinGW 64-bit"

配置好之后,咱们验证一下。新建一个项目,选"Qt Console Application",一路默认,然后编译运行。如果控制台输出了"Hello World",环境就搭好了。

1.4 第一个Qt控制台程序

光说不练假把式。咱们写一个真正的Qt控制台程序,把串口模块也带上,验证环境是否完整。

// main.cpp
#include <QCoreApplication>
#include <QSerialPortInfo>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "=== Qt Serial Port Demo ===";

    // 枚举所有可用串口
    QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
    qDebug() << "Available serial ports:" << ports.size();

    for (const QSerialPortInfo &info : ports) {
        qDebug() << "Port:" << info.portName()
                 << "Description:" << info.description()
                 << "Manufacturer:" << info.manufacturer();
    }

    return 0;
}

这段代码做了三件事:

  • 引入QSerialPortInfo头文件
  • 调用availablePorts()枚举串口
  • 打印每个串口的名称、描述和制造商

编译运行后,如果你电脑上插了USB转串口设备,就能看到它的信息。没插设备也没关系,程序会正常退出,只是列表为空。

注意: 如果编译报错"QSerialPortInfo: No such file or directory",说明你没勾选Serial Port模块。回到安装器,补装Qt Serial Port库即可。

我个人习惯在.pro文件里明确声明依赖:

QT += core serialport
CONFIG += console
CONFIG -= app_bundle

SOURCES += main.cpp

这样写,别人拿到你的源码,一看就知道用了哪些模块。

1.5 本章知识体系

下面这张图,把本章的核心内容串起来了。你可以把它当作一个思维导图,后面每学一章,都可以往这个框架里填东西。

第1章:课程导论与环境搭建 串口编程概述 内存管理重要性 环境搭建与验证 QSerialPort / QSerialPortInfo 异步信号槽:readyRead() 内存泄漏 / 野指针 Qt对象树与父子关系 Qt Creator + MinGW配置 第一个控制台程序验证 核心目标 理解串口编程基础 → 建立内存管理意识 → 搭建可复用的开发环境

这张图里,三个模块是并列关系。串口编程是我们要实现的功能,内存管理是保证程序稳定性的基石,环境搭建是这一切的前提。后面每一章,都会围绕这三个维度展开。

我的建议: 学完一章,就动手写代码。光看不练,看十遍不如写一遍。尤其是串口编程,你只有真正连上设备,看到数据在收发,才能理解那些API到底在干什么。