这篇文章记录这段过程里最重要的几个问题:开发环境怎么分工,Qt 工具和交叉编译器怎么配合,怎样确认生成的是 ARM 程序,以及界面启动后为什么还可能收不到数据。

本文使用的板卡终端标识为 RK3588-Tronlong,因此按创龙 RK3588 记录。具体板卡型号、系统镜像和 SDK 版本仍需以自己的设备资料为准。

本文是项目实践整理,不是适用于所有 RK3588 镜像的一键安装教程。环境版本和运行日志来自实际记录;下面重新整理的构建命令是操作模板,并非完整恢复的历史命令,也未在本文编写环境重新执行板端测试。路径、网口及 SDK 初始化方式需要按实际环境核对。

环境部署

本项目采用 Windows VS Code 远程连接 Ubuntu 虚拟机开发,Ubuntu 中使用 RK3588 LinuxSDK 提供的 Qt 交叉编译环境,通过 qmake 和 make 编译生成 ARM64 可执行程序,再通过 scp 上传到 RK3588 开发板,最后在 HDMI 显示器上使用 eglfs 平台运行 Qt 界面程序。

一、整体开发链路

1
2
3
4
5
6
7
8
9
Windows 本机
↓ VS Code Remote SSH
Ubuntu 虚拟机
↓ Qt 交叉编译
生成 ARM64 可执行程序
↓ scp 上传
RK3588 开发板
↓ HDMI 显示运行
Qt 界面程序运行

Windows 负责编码,Ubuntu 虚拟机负责编译,RK3588 负责运行

二、开发环境分工

项目 配置
Qt 5.15.8,Qt Widgets
构建工具 qmake + make
当前项目语言基线 C++14,以当前 .pro 为准
编译器 Linaro GCC 10.2.1,aarch64-linux-gnu
SDK rk3588_linux_release_v1.2.1
qmake mkspec linux-aarch64-gnu-g++
目标程序 tankae-host-aebss
构建目录 build-rk3588

为什么需要交叉编译

普通桌面编译器生成的 x86_64 Linux 程序,不能直接作为 ARM64 程序运行在 RK3588 上。Windows 的 .exe 也不是复制过去就能运行。

这里的交叉编译,就是在 x86_64 Ubuntu 上运行编译工具,生成 ARM64 Linux 可执行文件。

三、准备条件

1. Windows 本机

安装:

1
2
VS Code
Remote - SSH 插件

2. Ubuntu 虚拟机

需要有:

1
2
3
4
RK3588 LinuxSDK
Qt sysroot
qmake
交叉编译器 aarch64-linux-gnu-g++

核心 SDK 目录示例:

1
/home/lxt/RK3588/rk3588_linux_release_v1.2.1

3. RK3588 开发板

需要确认:

1
cat /etc/os-release

应为 Debian 系统。

确认 HDMI:

1
cat /sys/class/drm/card0-HDMI-A-1/status

正常应为:

1
connected

四、配置 Ubuntu 虚拟机环境

进入 SDK 目录:

1
cd ~/RK3588/rk3588_linux_release_v1.2.1

如果还没有 Qt 交叉环境,执行:

1
./build.sh debian

如何配QT的交叉环境
这一块是重点和难点

1. 安装 LinuxSDK

1
Host# sudo apt-get install -y git ssh make gcc libssl-dev \ liblz4-tool expect expect-dev g++ patchelf chrpath gawk texinfo chrpath \ diffstat binfmt-support qemu-user-static live-build bison flex fakeroot \ cmake gcc-multilib g++-multilib unzip device-tree-compiler ncurses-dev \ libgucharmap-2-90-dev bzip2 expat gpgv2 cpp-aarch64-linux-gnu libgmp-dev \ libmpc-dev libmpfr-dev

具体内容参考自带的说明书在这里不做过多追述。

2. 确认 SDK 路径与工具链

1
/home/user/RK3588/rk3588_linux_release_v1.2.1

重点核对以下位置:

SDK 内相对路径 作用
extra-tools/gcc-linaro-10.2.1-2021.01-x86_64_aarch64-linux-gnu/bin 交叉编译器
debian/sysroots/x86_64-linux/bin 在 Ubuntu 上运行的 qmake、moc、uic、rcc
debian/sysroots/armv8a-linux 目标系统的 sysroot

sysroot 可以理解成编译时使用的一份目标系统开发文件集合,包含目标平台的头文件、库和链接所需文件。它不是任意 Linux 根目录,都能拿来替换。

最容易混淆的一点是:qmake、moc 等工具运行在电脑上,所以它们是主机程序;最终链接的 Qt 库和生成的业务程序才需要是 ARM64。

不要看到工具目录中有 x86_64 就认定拿错了,也不要把板端的 ARM64 moc 拿到 Ubuntu x86_64 上执行。

五、检查环境并生成 Makefile

如果厂商 SDK 提供环境初始化脚本,优先按对应版本文档加载。下面的变量只是便于定位和检查,不等价于所有厂商的完整初始化脚本。

在 Ubuntu 终端执行:

1
2
3
4
5
6
7
8
9
10
export RK_SDK=/home/lxt/RK3588/rk3588_linux_release_v1.2.1
export RK_TOOLBIN="$RK_SDK/extra-tools/gcc-linaro-10.2.1-2021.01-x86_64_aarch64-linux-gnu/bin"
export RK_QT_HOSTBIN="$RK_SDK/debian/sysroots/x86_64-linux/bin"
export RK_SYSROOT="$RK_SDK/debian/sysroots/armv8a-linux"
export PATH="$RK_TOOLBIN:$PATH"

"$RK_TOOLBIN/aarch64-linux-gnu-g++" --version
"$RK_TOOLBIN/aarch64-linux-gnu-g++" -dumpmachine
"$RK_QT_HOSTBIN/qmake" -v
"$RK_QT_HOSTBIN/qmake" -query

检查目标架构是否是 aarch64-linux-gnu,Qt 版本是否符合项目要求,以及 qmake 查询中的 sysroot、头文件和库路径是否对应这套 SDK。

不要只看 qmake -v 显示了 5.15.8 就认为配置正确:同一个版本也可能有桌面 Qt 和交叉 Qt 两套安装。

如果 SDK 被移动到新目录后仍引用旧路径,应检查厂商的 SDK 重定位方法及 qmake 配置,不要在每个源码文件里硬编码路径补漏洞。

检查工程文件

本项目沿用 Qt Widgets 和 .pro 工程。示意配置如下,不要直接覆盖已有文件:

1
2
3
4
5
6
7
8
QT += core gui widgets printsupport opengl svg
CONFIG += c++14
TEMPLATE = app
TARGET = project

# SOURCES、HEADERS、FORMS、RESOURCES 沿用实际项目配置。
# 若使用 QUdpSocket 等 Qt Network 类,再确保加入:
# QT += network

使用 Linux/POSIX socket 的工程,并不一定需要 Qt Network 模块。模块清单要按真实代码决定。

六、使用独立目录交叉编译

先进入包含 project.pro 的工程根目录。确认 SDK 的 qmake 已配置好目标 sysroot 和库路径后,再执行下面的构建模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir -p build-rk3588
cd build-rk3588

"$RK_QT_HOSTBIN/qmake" ../tankae-host.pro \
-spec linux-aarch64-gnu-g++ \
"CONFIG+=release" "CONFIG-=debug" \
"QMAKE_CC=$RK_TOOLBIN/aarch64-linux-gnu-gcc" \
"QMAKE_CXX=$RK_TOOLBIN/aarch64-linux-gnu-g++" \
"QMAKE_LINK=$RK_TOOLBIN/aarch64-linux-gnu-g++" \
"QMAKE_AR=$RK_TOOLBIN/aarch64-linux-gnu-ar cqs" \
"QMAKE_MOC=$RK_QT_HOSTBIN/moc" \
"QMAKE_UIC=$RK_QT_HOSTBIN/uic" \
"QMAKE_RCC=$RK_QT_HOSTBIN/rcc"

这个命令明确了工具选择,但仅仅指定 -spec 或编译器,并不能把一套桌面 Qt 变成可用的交叉 Qt。sysroot 和 ARM64 Qt 库仍必须正确配置。

先检查生成结果:

1
rg -n '^(CC|CXX|LINK|AR|MOC|UIC|RCC|CFLAGS|CXXFLAGS|INCPATH|LFLAGS|LIBS)[[:space:]]*=' Makefile

重点看 C/C++ 编译器和链接器是否属于 aarch64 工具链,Qt 头文件和库是否指向目标 SDK,以及编译、链接是否有效使用了目标 sysroot。某些配置通过编译器默认项传递 sysroot,不一定每一行都显式出现 --sysroot,必要时结合编译器查询和实际命令检查。

确认后构建:

1
make -j"$(nproc)"

虚拟机内存较少时可以改为 make -j2。并行任务越多,不一定越快。

为什么不继续手写 Makefile

早期尝试出现过:

1
No rule to make target 'moc_mainwindow.o'

Qt 中带有 Q_OBJECT 的类需要 moc 生成相应代码。手写构建规则容易遗漏依赖,而 qmake 可以根据工程配置生成相关规则。遇到这类问题,应该检查类头文件是否被工程正确管理,并重新生成构建文件,而不是删除 Q_OBJECT 来绕过错误。

如果换了工具链或 Qt 版本,使用一个新的构建目录验证,避免混入上一次留下的目标文件。

七、确认产物架构并上传开发板

在 Ubuntu 的构建目录中检查:

1
2
3
file ./tankae-host-aebss
"$RK_TOOLBIN/aarch64-linux-gnu-readelf" -h ./tankae-host-aebss
"$RK_TOOLBIN/aarch64-linux-gnu-readelf" -l ./tankae-host-aebss

项目已有产物记录显示为 ARM aarch64 ELF,动态加载器为 /lib/ld-linux-aarch64.so.1。检查到 x86-64 则说明仍然用了错误的构建配置。

“编译完成”只说明产物生成了,动态依赖和图形环境仍需在板端检查。

在上传之前确认目标目录存在,并先备份板上已有程序。下面是路径模板,替换账号、IP 和目录后使用:

1
scp ./tankae-host-aebss <板端账号>@<RK3588管理IP>:<部署目录>/

尖括号表示占位符,不能原样复制执行。SSH 管理 IP 也不一定等于连接 FPGA 的那个网口 IP。

板端运行记录中的目录是 /home/Tronlong/QT1:

1
2
3
4
cd /home/Tronlong/QT1
chmod +x ./tankae-host-aebss
ldd ./tankae-host-aebss
./tankae-host-aebss

ldd 在目标板上检查自己的可信程序,留意 not found。缺少库时,使用匹配板端系统的 ARM64 库,不要复制 Ubuntu x86_64 的库,也不要随意替换板端系统库。

八、排查窗口无法打开的问题

现象 优先检查
Exec format error 可执行文件架构是否正确
文件存在却提示 No such file or directory ELF 动态加载器是否存在;若为脚本则检查解释器
error while loading shared libraries 动态库是否缺失或搜索路径错误
Could not load the Qt platform plugin 平台插件、依赖库和图形会话
界面能打开,但没有波形 通信、采集状态及数据解析

平台插件排查时可在板端执行:

1
QT_DEBUG_PLUGINS=1 ./tankae-host-aebss

有桌面的镜像可能使用 X11 或 Wayland;无桌面的嵌入式配置可能使用 EGLFS 等方式。选择取决于当前镜像和 Qt 构建,不能把设置 QT_QPA_PLATFORM=eglfs 当成所有板子的通用修复。

尤其是通过 SSH 启动时,还涉及图形会话的访问权限。先在板端已有桌面终端运行通常更容易区分问题,不要用关闭显示访问控制的方式绕过权限。

九、参考资料与适用范围

本文的设备环境、路径和错误日志来自本项目开发记录;Qt 通用机制可结合以下官方资料理解:

以上在线链接为 Qt 6 文档,仅用于通用概念参考;本项目使用 Qt 5.15.8,不能将 Qt 6 的配置或 CMake 命令直接替换进本项目。实际操作仍以对应版本的厂商 SDK 文档为准。