2. 开发环境搭建

嵌入式 HMI 开发与桌面应用开发最大的区别在于:代码最终运行在 ARM 目标板上,而编译工作通常在 x86_64 的 Linux 主机上完成。因此,开发环境不是"装一个 IDE"这么简单,而是一条从主机到目标板的完整工具链。本节把这条链路拆成四层:Qt Creator(编辑与调试前端)、Qt SDK(Qt 库与 QML 运行时)、交叉编译工具链(把源码变成 ARM 可执行文件)、目标板连接(部署与运行)。四层打通后,第一个 Hello World HMI 才有意义。

一、核心原理:为什么需要"交叉"

目标板通常是 ARM Cortex-A 系列 SoC,资源有限,无法在上面跑完整的编译环境。于是采用"宿主机编译、目标板运行"的模式:宿主机上的编译器不是本机的 gcc,而是面向 ARM 架构的交叉编译器,例如 arm-linux-gnueabihf-gcc 或 aarch64-linux-gnu-gcc。它生成的是 ARM 指令集的 ELF 文件,通过 scp、NFS 或 SD 卡拷贝到目标板执行。

Qt 程序还多一层依赖:Qt 库本身也必须被交叉编译成 ARM 版本。宿主机上 apt 安装的 Qt 是 x86_64 的,不能链接到 ARM 程序里。所以 Qt SDK 需要针对目标板重新构建,或者使用芯片厂商提供的 BSP SDK(如 Yocto 生成的 SDK、Buildroot 输出的 staging 目录)。

关键判断:交叉编译器的三元组前缀(如 aarch64-linux-gnu-)必须与目标板 rootfs 的 libc 类型一致。glibc 与 musl、硬浮点与软浮点不匹配,程序会在目标板上报 "No such file or directory" 或直接段错误。

二、四层环境搭建流程

下面这张图展示了从主机到目标板的完整信号与数据流:源码在 Qt Creator 中编辑,经 qmake/CMake 调用交叉工具链编译,链接交叉编译的 Qt 库,产出 ARM 可执行文件,再通过部署通道送到目标板运行。

宿主机(x86_64 Linux) Qt Creator 编辑 / 构建 / 调试前端 Qt SDK(ARM 版) QtCore/QtGui/QtQml 库 交叉编译工具链 aarch64-linux-gnu-gcc 构建产物:main.qml + hello_hmi(ARM ELF 可执行文件) qmake / CMake 驱动,链接 ARM 版 Qt 库 目标板(ARM 嵌入式 Linux) 部署通道 scp / NFS / SD 卡 目标板 rootfs /opt/hello_hmi/ 运行环境 EGLFS / LinuxFB / Wayland 运行:./hello_hmi -platform eglfs → 屏幕显示 Hello World 部署

三、关键配置与命令

Qt Creator 中需要配置一个 Kit,把编译器、Qt 版本、设备三者绑定。核心是 qmake 的路径必须指向 ARM 版 Qt,而不是系统 Qt。

# 1. 检查交叉编译器
aarch64-linux-gnu-gcc -v

# 2. 配置 ARM 版 Qt(示例:源码构建)
./configure -prefix /opt/qt-arm \
  -xplatform linux-aarch64-gnu-g++ \
  -no-opengl -eglfs -qt-libjpeg -qt-zlib

# 3. Qt Creator 中 Kit 关键项
#    Compiler:  aarch64-linux-gnu-g++
#    Qt version: /opt/qt-arm/bin/qmake
#    Device:    Generic Linux Device (SSH)

# 4. 部署并运行
scp hello_hmi root@192.168.1.100:/opt/hello_hmi/
ssh root@192.168.1.100
cd /opt/hello_hmi && ./hello_hmi -platform eglfs

四、第一个 Hello World HMI 程序

最小可运行的 QML HMI 由三部分组成:main.cpp 启动 QGuiApplication 并加载 QML;main.qml 描述界面;hello_hmi.pro 声明依赖。注意嵌入式下通常使用 QGuiApplication 而非 QApplication,避免引入 Widgets 模块增大体积。

// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
    return app.exec();
}

// main.qml
import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    visible: true
    width: 800; height: 480
    color: "#0d1b2a"
    Text {
        anchors.centerIn: parent
        text: "Hello World HMI"
        color: "#4fc3f7"
        font.pixelSize: 48
    }
}

五、环境要素对照清单

层级 组件 典型路径/命令 验证方式
前端 Qt Creator Tools → Options → Kits Kit 无黄色感叹号
库 Qt SDK(ARM) /opt/qt-arm/bin/qmake qmake -query QT_VERSION
编译 交叉工具链 aarch64-linux-gnu-gcc file hello_hmi 显示 ARM aarch64
连接 SSH / NFS ssh root@192.168.1.100 能登录并执行 uname -m
运行 平台插件 -platform eglfs 屏幕出现文字,无 Qt 报错

六、常见误区与复盘

误区一:用系统 Qt 编译后直接拷贝到板子。现象是运行时报 error while loading shared libraries: libQt5Core.so.5: cannot open shared object file。原因是链接的是 x86_64 库。复盘:用 file 和 ldd 检查产物架构与依赖路径。

误区二:忽略平台插件。不指定 -platform eglfs 时,Qt 默认尝试 xcb,目标板没有 X Server 会直接退出。复盘:在目标板设置 export QT_QPA_PLATFORM=eglfs,或启动参数显式指定。

误区三:QML 资源未打包。直接拷贝 main.qml 到板子,路径写错就白屏。复盘:用 qrc 资源系统把 QML 编进可执行文件,避免路径依赖。

建议第一次搭建时按"先跑通命令行交叉编译,再配置 Qt Creator Kit"的顺序推进。命令行能编译出 ARM 可执行文件,说明工具链和 Qt 库没问题;此时再配 IDE,排错范围会小很多。第一个 Hello World 的目标不是界面好看,而是验证"主机编译 → 目标板运行"这条链路完全贯通。