☰
嵌入式Qt应用交叉编译与部署:以RK3506开发板为例
2026/9/28 20:03:53 网站建设 项目流程

拿到一块瑞芯微RK3506的开发板,想在板子上跑一个Qt应用,很多人的第一反应是:我在电脑上已经写好了Qt程序,编译一下拷过去不就行了?等你真把程序拷到板子上,敲下运行命令,大概率会收获一个“No such file or directory”或者直接没有任何反应。问题出在哪?其实不是你的代码写得不对,而是你缺少一套完整的交叉编译思维。这篇文章我就用RK3506开发板作为目标平台,把Qt应用从编译到运行的完整链路拆开讲一遍,从原理、工具链搭建、Qt源码交叉编译、板端部署,到常见问题排查,都给你捋清楚。不管你用的是RK3506、RK3588、T113还是其他ARM开发板,这套流程基本通用,认真看完能少走很多弯路。

1. 直击原理:为什么在RK3506上跑Qt应用必须先搞懂交叉编译

1.1 交叉编译到底在解决什么问题

先想一个很朴素的问题:电脑上的CPU是x86_64架构,开发板上的RK3506是ARM架构,两者执行的机器码格式完全不同。你写在桌面上的Qt程序,编译出来的二进制文件只能用桌面CPU执行,根本跑不到开发板上,就好比你用中文写的信,得找一个人翻译成英文,对方才能看懂,交叉编译器就是这个“翻译官”。

交叉编译说白了,就是在一台硬件平台(主机)上,编译出另一台硬件平台(目标机)能执行的程序。整个Qt嵌入式开发的核心链路是“主机交叉编译 → 生成目标板可执行文件 → 拷贝到板子 → 在板子上运行”,这和PC上开发的逻辑有天壤之别。很多新手第一次接触嵌入式Qt时,还在用Qt Creator点“调试”,程序却无论如何都运行不起来,源头就是没跨过交叉编译这道坎。

1.2 RK3506开发板是什么类型:选板子前先看架构

RK3506是瑞芯微旗下一款面向工业控制和智能终端的处理器,四核Cortex-A35,主频通常能做到1.5GHz左右,集成Mali-G31 GPU,支持1080P视频解码,接口上有千兆以太网、USB、UART、CAN、I2C/SPI等,很适合做带屏幕的HMI人机交互设备、边缘网关、充电桩屏幕、智能楼宇面板等场景。

市面上的RK3506开发板主要有两种形态:一种是“核心板+底板”,核心板自带CPU、DDR、eMMC,底板扩展各种接口,好处是方便批量制板;另一种是整板,接口全部焊好,拿到手就能开机。你选板子的时候,除了看CPU好不好用,更要看内存大小、Flash类型、屏幕接口(RGB/LVDS/MIPI/HDMI),因为Qt跑得是否流畅,内存和GPU驱动往往比CPU更关键。

顺便说一句,当前同类型的开发板还有很多,比如瑞芯微高端一点的RK3588,性能更强、能跑安卓或桌面级Linux,但功耗和成本也高;国产另一家的T113,主核是双核Cortex-A7,性能偏低,跑复杂Qt界面会吃力。RK3506恰好卡在中间档位——性价比高、性能够用,是学习和做轻量级产品比较务实的选择。

1.3 为什么Qt离线安装包不能直接用于开发板

很多人在百度找到“qt 5.15.2下载安装”或者“qt离线安装包下载5.14”的教程,在Windows或者Ubuntu上装好了Qt,打开Qt Creator写了个窗口程序,编译一下,在PC上跑通了,然后把整个目录拷贝到开发板,结果运行报错。这是很典型的场景。

原因很简单:官方下载的这些Qt安装包,只有x86、x64、Windows/macOS/Linux通用桌面平台版本,包里没有ARM平台的库。你要想让Qt跑在RK3506上,必须自己下载Qt的源码包,比如qt-everywhere-opensource-src-5.15.2.tar.xz,然后用交叉编译工具链去编译出ARM架构的Qt运行库。这就是整篇文章的核心,也是嵌入式Qt工程师和纯桌面Qt开发者的分水岭。

2. 环境准备:工具链、Qt源码和依赖库一网打尽

2.1 主机系统准备与虚拟机注意事项

交叉编译工具链和一些脚本命令,最好在Linux环境里跑。我自己长期用的是Ubuntu 20.04 LTS 64位,也推荐你选18.04或20.04这种稳定性好的版本。如果你只有Windows,可以在虚拟机里装Ubuntu。

这里有一个很实际的坑:如果你在VMware里跑Ubuntu,虚拟机和主机之间没有装好VMware Tools,共享目录和自动调整分辨率会一直不生效,经常看到“继续运行脚本未能在虚拟机中成功运行”之类的提示。遇到这种情况不要慌,直接重新安装open-vm-tools即可:

sudo apt update sudo apt install open-vm-tools -y

装完之后重启虚拟机,共享目录才能正常工作。否则后面你要把Qt源码从Windows拖进Ubuntu,会特别痛苦。

2.2 安装交叉编译工具链

工具链就相当于翻译官,负责把C/C++源码编译成ARM机器码。对RK3506这种64位ARM芯片,最直接的方法是装Ubuntu官方源里的aarch64工具链:

sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu -y

安装完可以验证一下版本:

aarch64-linux-gnu-gcc -v

如果你手里有瑞芯微官方SDK或者Buildroot,也可以用SDK里自带的交叉工具链,路径和版本可能有差别,但思路一样。这里多说一句,工具链的glibc版本要和开发板系统的glibc版本匹配,否则即使编译出的二进制是ARM格式,拷到板子上也可能报“version GLIBC_2.29 not found”。所以我更建议直接使用开发板厂家SDK里配套的工具链,能省掉很多恶魔化的问题。

2.3 准备Qt源码

先在PC上写Qt程序、做UI调试,用官方离线安装包拉到Qt Creator即可,比如Windows上装Qt 5.14或5.15.2,桌面开发没有太多讲究。但要让程序真正跑在RK3506上,必须重新交叉编译一套ARM版Qt库,建议下载官方源码包:

wget https://download.qt.io/archive/qt/5.15/5.15.2/single/qt-everywhere-opensource-src-5.15.2.tar.xz tar -xf qt-everywhere-opensource-src-5.15.2.tar.xz cd qt-everywhere-opensource-src-5.15.2

源码包解压之后,里面有qtbase、qtdeclarative、qtserialport、qtmultimedia等各个模块目录,编译时根据需要选择。其中qtbase是核心模块,包含QtCore、QtGui、QtWidgets以及linuxfb、eglfs等平台插件。你一定得先把qtbase搞明白,其他模块都是基于它扩展的。

2.4 安装主机依赖和目标板依赖库

在configure的时候,Qt会检测主机上的很多开发库,如果缺了会直接中断报错,所以先装一波基础依赖:

sudo apt install build-essential libxcb1-dev libxcb-keysyms1-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libxkbcommon-x11-dev libgl1-mesa-dev libts-dev -y

这里尤其要注意libts-dev,触摸屏的tslib库。如果你的开发板用的是电阻屏,Qt需要依赖tslib来做坐标校准,但如果你用的是电容屏,内核已经有evdev协议,Qt的evdev插件可以直接读取触摸事件,那tslib可以不装。工程实践中,我建议不管什么屏,尽量把tslib加上,后面调触摸会省心很多。

3. Qt源码交叉编译:从configure到make install的完整过程

3.1 configure参数解析:哪些配置不能抄错

进入源码目录后,创建一个build目录,然后运行configure。这是整个流程里最需要耐心的环节,参数没选对,后面全都白搭。下面是我在RK3506板上验证过可用的一套配置:

mkdir build && cd build ../configure \ -prefix /usr/local/qt5 \ -xplatform linux-aarch64-gnu-g++ \ -opensource \ -confirm-license \ -release \ -no-opengl \ -no-xcb \ -no-wayland \ -no-eglfs \ -linuxfb \ -tslib \ -qt-libpng \ -qt-libjpeg \ -qt-zlib \ -qt-pcre \ -nomake examples \ -nomake tests \ -skip qt3d \ -skip qtwebengine \ -skip qtmultimedia \ -no-gui

我来逐个解释这些参数的实际意义:

  • -prefix /usr/local/qt5:指定最后make install的安装路径,编译出来的库最终会被装到这个目录,后面拷板子时直接把整个目录拷过去。
  • -xplatform linux-aarch64-gnu-g++:告诉Qt使用aarch64交叉编译器。Qt源码里已经内置了这套mkspec,不需要你额外写。
  • -opensource -confirm-license:走开源协议,跳过交互确认。
  • -release:编译release版本,体积和性能都比debug好很多。
  • -no-opengl -no-xcb -no-wayland -no-eglfs:如果你的板子只是简单跑一个Qt窗口界面,不需要OpenGL加速和X11桌面环境,关掉这些可以减少编译时间,也能避免缺依赖的问题。
  • -linuxfb:使用Linux帧缓冲(framebuffer)作为Qt的平台插件,这是嵌入式无桌面环境下最常用的方式,屏幕直接通过/dev/fb0绘制。
  • -tslib:启用触摸库支持。
  • -qt-libpng -qt-libjpeg -qt-zlib -qt-pcre:让Qt使用内置的第三方库,避免主机上版本差异导致的链接问题。
  • -nomake examples -nomake tests:不编译示例和测试代码,能大幅缩短编译时间。
  • -skip qtwebengine:Qt WebEngine体积庞大且依赖很多,嵌入式场景基本用不到,直接跳过。

如果你后面想用OpenGL做动画效果,RK3506有Mali GPU,可以尝试编译EGLFS版本,但需要厂家提供GPU的用户态驱动和libEGL库,工作量会大不少。刚上手跑通流程,我建议先不折腾GPU,linuxfb足够了。

3.2 编译过程与常见错误处理

configure通过之后,就进入漫长的编译期:

make -j4 sudo make install

即便用了-j4,在虚拟机里编译完整Qt库也得半小时到一个小时,所以一定要找个稳定的网络和主机环境,别中途断了。如果configure之后你又改了参数,再次configure前老老实实执行make distclean清理一次,否则上次的cache会一直干扰你。

编译过程中我碰到过几个典型的错误,先列出来给你打个预防针:

  • 报错Could not find libts,说明libts-dev没装好,回去检查上文的依赖。
  • 报错GLES/gl.h not found,说明configure时没有正确禁用OpenGL,确认-no-opengl参数已加上。
  • 报错g++: internal compiler error,多半是虚拟机内存给少了,建议给Vmware虚拟机分配至少4GB内存。

你还要注意一点,configure生成的config.summary里有一段“Qt is now configured”,最后一行会写出QMAKE_CC、QMAKE_CXX是否指向aarch64工具链,如果看到的是gcc而不是aarch64-linux-gnu-gcc,说明xplatform的mkspec没生效,必须停下来排查,别硬着头皮编,否则白花时间。

3.3 单独编译Qt模块:serialport等附加模块的坑

用qt-everywhere-opensource-src源码包编译时,qtbase是默认编译的,但很多附加模块不一定被编译,比如串口功能的QtSerialPort。你如果辛辛苦苦编完Qt,回到自己的工程里qmake时,却看到“unknown module in qt: serialport”这样的报错,就是这个问题。

原因很直接:源码包的configure没有自动处理qtbase之外的模块。解决方法很简单,单独编译那个模块就行:

cd qt-everywhere-opensource-src-5.15.2/qtserialport mkdir build && cd build qmake ../qtserialport.pro make -j4 sudo make install

这里的qmake不是系统里的qmake,而是指你刚make install出来的交叉编译版qmake。如果没设置PATH,可以直接写完整路径,比如:

/usr/local/qt5/bin/qmake ../qtserialport.pro

编译完模块后,再回到你自己的Qt工程里编译,就不会出现“unknown module”的报错了。同理,QtMqtt、QtCharts、QtWebSocket这些模块,如果你的应用需要,也照这个方式单独qmake、make、make install,不要指望一次configure全给你带上。

3.4 验证编译产物是否真的是ARM版

编译安装结束后,不要急着打包,先做个静态检查:

file /usr/local/qt5/lib/libQt5Core.so.5

正常会输出类似这样的信息:

ELF 64-bit LSB shared object, ARM aarch64

只要看到“ARM aarch64”,说明这个库是给开发板用的,可以放心。如果显示的是x86-64,说明编译过程某个环节还是用了主机编译器,整个编译链条要重新排查。

然后再写一个最简测试工程拿来验证工具链和Qt库是否正常工作。新建一个目录,里面放一个main.cpp:

#include <QApplication> #include <QLabel> int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label("Hello RK3506 Qt"); label.resize(320, 160); label.show(); return app.exec(); }

工程文件用最常见的写法:

QT += widgets SOURCES += main.cpp TARGET = hellork

接着调用交叉编译qmake来构建:

/usr/local/qt5/bin/qmake make

生产出来的hellork可执行文件,同样用file hellork验证一下架构,确认无误后,就可以进入部署阶段了。

4. 应用部署与板端运行:从拷贝到出界面的最佳路径

4.1 把Qt运行库拷贝到开发板

这一阶段的目标,是把开发板跑Qt所需的所有库和可执行程序都放到板上。最直接的办法是用scp拷贝:

scp -r /usr/local/qt5 root@开发板IP:/usr/local/qt5 scp hellork root@开发板IP:/root/

如果你的开发板空间紧张,可以不用全拷贝,只拷贝动态库和plugins目录即可:

scp /usr/local/qt5/lib/libQt5*.so* root@开发板IP:/usr/local/qt5/lib/ scp -r /usr/local/qt5/plugins/ platform root@开发板IP:/usr/local/qt5/

先把整个目录传过去,再删掉不需要的库,是更省事的方案。

4.2 用NFS挂载开发板访问Ubuntu目录

跟scp一次性拷贝相比,开发阶段我更推荐“开发板挂载Ubuntu目录”。原理很朴素:把主机Ubuntu上的某个目录共享出来,开发板通过网络把它挂载到本地的/mnt目录,这样编译完的程序立刻能在开发板上看到,不需要反复拷贝,调试效率高很多。

先确认Ubuntu主机装了NFS服务:

sudo apt install nfs-kernel-server -y

编辑/etc/exports,把/opt/rootfs这个目录共享给局域网内的开发板:

/opt/rootfs *(rw,sync,no_root_squash,no_subtree_check)

重启服务并确认:

sudo exportfs -ra sudo systemctl restart nfs-kernel-server

开发板端挂载:

mount -t nfs -o nolock,proto=tcp Ubuntu主机IP:/opt/rootfs /mnt

挂载成功后,你在主机/opt/rootfs里放的东西,在开发板的/mnt目录下就能直接看到。调试Qt程序的时候,直接把你的工程目录放到共享目录里,在板子上cd /mnt/xxx && ./hellork,改代码重新make一跑,不用再折腾scp。

NFS挂载不成功时,十有八九是主机防火墙拦截或者nolock没加。先sudo ufw allow 2049放行NFS使用的端口,再检查/etc/exports里的路径是否真的有权限。这里强烈建议挂载参数带上nolock,嵌入式开发板上没有常用锁服务,缺了这个参数很容易出现挂载僵死。

4.3 设置板端环境变量:让Qt找到库和插件

可执行文件和库都就位后,运行前还要告诉系统几件事:Qt的库放哪里、插件去哪里找、用哪个平台插件。把下面这些写进开发板的/etc/profile,开机自动生效:

export QTDIR=/usr/local/qt5 export LD_LIBRARY_PATH=/usr/local/qt5/lib:$LD_LIBRARY_PATH export QT_QPA_PLATFORM=linuxfb export QT_QPA_PLATFORM_PLUGIN_PATH=/usr/local/qt5/plugins/platforms export QT_QPA_FONTDIR=/usr/local/qt5/fonts

这里解释一下QT_QPA_PLATFORM。Qt5用一套叫QPA的抽象层来控制底层显示和输入,linuxfb表示直接把整个窗口输出到Linux帧缓冲设备/dev/fb0上。如果你的屏幕是多点电容屏,并且内核支持evdev触摸事件,也可以设置成linuxfb加上触摸设备环境变量:

export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=/dev/input/event1

具体event几,要看板子上cat /proc/bus/input/devices查出来的触摸屏节点。

4.4 运行Qt应用:linuxfb还是eglfs

环境变量设置好之后,就可以在板子上运行程序了:

cd /root ./hellork

如果能弹出“Hello RK3506 Qt”窗口,恭喜你,Qt应用在开发板上跑起来了。这时候你还可以做个小测试,验证一下Qt的绘图功能是否正常:

#include <QApplication> #include <QWidget> #include <QPainter> class MyWindow : public QWidget { protected: void paintEvent(QPaintEvent *) override { QPainter p(this); p.fillRect(rect(), Qt::white); p.setPen(Qt::blue); p.drawText(50, 100, "Qt Draw Test"); p.drawLine(10, 10, 200, 200); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); MyWindow w; w.resize(320, 240); w.show(); return app.exec(); }

这个程序如果能在板上正常画出线条和文字,说明Qt GUI渲染链路是通的。如果画面有撕裂感,可以设置QT_QPA_FB_HIDEFREQUENCY参数来避免屏幕闪烁,例如:

export QT_QPA_FB_HIDEFREQUENCY=60

如果你的板子接的是HDMI显示器,linuxfb可能显示效果一般,可以试试eglfs。不过前面说过了,用eglfs需要厂家的GPU驱动配合,这一步往后放,先把linuxfb跑通更重要。

4.5 调试技巧:用Qt模拟鼠标点击事件

开发过程中,经常需要在没有物理鼠标或触摸屏的情况下测试界面交互。一个常见的办法是在Qt程序里用QTest模块模拟鼠标事件。比如你要测试一个按钮点击:

#include <QTest> #include <QPushButton> QPushButton button("click"); button.show(); QTest::mouseClick(&button, Qt::LeftButton);

前提是你的工程文件里加了QT += testlib。这个写进单元测试比较方便,但如果你想在真实板子上模拟鼠标点击,QTest只能做到程序内部事件注入,作用范围有限。

更底层一点的办法,是直接往内核输入子系统写入evdev事件。假设你的开发板有一个/dev/input/event0是触摸设备,可以用一个小程序模拟发送触摸坐标:

echo "sendevent /dev/input/event0 3 53 160" | sh echo "sendevent /dev/input/event0 3 54 120" | sh echo "sendevent /dev/input/event0 1 330 1" | sh echo "sendevent /dev/input/event0 1 330 0" | sh

这些命令转成事件码就是一次屏幕坐标下的触摸动作。虽然实际项目里直接调这么多底层命令不常见,但你在做自动化测试时非常有用。我还听说有人用xdotool在X11环境下来做,但嵌入式无桌面系统里没有X11,还是在evdev层面做事件注入更实际。

5. 常见问题与排查手册:这些坑我替你踩过

5.1 Qt程序在板上启动失败,先检查这几项

见过最多的错误是:

-bash: ./hellork: No such file or directory

程序文件明明存在,却报“找不到”?这种情况十有八九不是文件缺失,而是动态链接器路径不对。执行:

file hellork

如果输出显示interpreter /lib/ld-linux-aarch64.so.1,那说明系统里少了这个动态链接器,或者它的路径不在默认位置。检查开发板上是否存在/lib/ld-linux-aarch64.so.1,没有的话在Qt库编译工具链目录里找一份拷贝过去。

还有一个高频问题是程序一启动就崩溃,没有任何输出。这时候用:

ldd hellork

在开发板上查看依赖的共享库是否全部存在。嵌入式板子的库一般不全,缺libQt5Core.so.5、libstdc++.so.6很常见。把主机交叉工具链里的libstdc++.so.6、libgcc_s.so.1也一起拷贝到板子的/usr/lib目录,问题基本能解决。

5.2 报错“unknown module in qt: serialport”怎么处理

这个问题前面单独提过,这里再强化一下。它本质上是Qt没有编译串口模块,工程文件里即使写了QT += serialport,qmake也找不到对应模块。先确认你编译安装的Qt库里有没有libQt5SerialPort.so.5:

find /usr/local/qt5 -name "*SerialPort*"

没有的话,就回到源码的qtserialport目录,用你的交叉qmake单独编译安装。装好后再qmake你的工程,就不会再报这个错。

5.3 中文显示乱码或缺字

Qt程序在PC上显示中文正常,到板子上全成了方块,核心原因是板子上没有中文字体。Qt默认的font目录是lib/fonts,要手动把字体拷进去:

mkdir -p /usr/local/qt5/fonts cp /usr/share/fonts/truetype/wqy/wqy-microhei.ttc /usr/local/qt5/fonts/

然后在环境变量里加上:

export QT_QPA_FONTDIR=/usr/local/qt5/fonts

有的板子字体还够,但是系统缺字体配置,可以在板子上运行fc-list看看字体是否被系统识别。显示乱码这个问题特别烦人,但原因就这么简单:字体文件缺失。

5.4 NFS挂载开发板失败排查

开发板挂载Ubuntu目录失败,我列一个排查顺序:

  1. 先看主机和开发板能不能ping通,网络不通一切免谈。
  2. 主机上执行showmount -e 主机IP,如果显示不出共享目录,说明NFS服务配置问题,检查/etc/exports。
  3. 挂载时带上nolock,命令改成mount -t nfs -o nolock。
  4. 开发板没跑rpc.statd的情况下,不带nolock很容易挂死,挂载命令卡住无响应。
  5. 尽量把主机防火墙关掉,或者放行NFS相关端口。

5.5 触摸屏点击不准或没反应

液晶屏上显示正常,但点击屏幕位置漂移,通常是tslib校准没做。配合tslib的环境变量和校准工具:

export TSLIB_TSDEVICE=/dev/input/event0 export TSLIB_CONFFILE=/etc/ts.conf export TSLIB_CALIBFILE=/etc/pointercal

然后运行:

ts_calibrate

按屏幕提示点几个校准点,生成的/etc/pointercal文件会保存触摸坐标和屏幕坐标的映射关系。这个文件丢了或被删掉,触摸就会重新漂移。如果你用的是电容屏,又不需要tslib,可以把TSLIB_TSDEVICE指向实际触摸设备节点,再确认内核evdev驱动工作正常。

5.6 编译期异常与虚拟机环境的坑

编译Qt源码的时候,虚拟机内存小会导致编译中断甚至卡死。建议给虚拟机分配至少2个CPU核和4GB内存,关掉不必要的后台软件再编译。如果你在Windows上写代码、在Ubuntu虚拟机里编译,一定要用“真正的Linux文件系统”来放源码,不要放在VirtualBox/VMware的共享文件夹里,否则编译过程中会碰到大量的文件权限和符号链接错误,这个坑很隐蔽,我至少帮两个朋友解决过。

还有一类“编译期异常”,比如源码本身在Windows编辑器里留下了BOM头或CRLF换行符,拷到Linux里编译时,报错信息五花八门。建议统一用VS Code或vim打开源码,并设置换行符为LF,能减少很多莫名奇妙的编译错误。

结尾

从原理到部署,这一整条链路走下来,你会发现嵌入式Qt应用其实没有多少高深莫测的东西:无非是交叉编译工具链、Qt源码编译、板端环境配置这三件事。我自己第一次在RK3506上跑通Qt窗口,前前后后折腾了快三天,最后发现不过是一个环境变量没设置好的问题。后来做得多了,总结出一套流程:先在主机上把Qt源码交叉编译装好,开发板上配好NFS挂载,所有Qt工程都放在共享目录里,改完代码直接交叉编译再运行,调试速度和PC开发几乎没有差距。

最后再分享一个实用的小技巧:把你常用的configure参数和环境变量写成一个shell脚本,存到工程仓库里,换一台机器或换一块开发板时,只需要改工具链前缀和IP,就能瞬间恢复环境,不用每次重新回忆。这个习惯帮我节省了大量时间,也是我在多个RK系列、T系列板卡之间切换时保持高效的原因。希望这篇文章能帮你少走一些弯路,祝你的Qt程序早日在板子上亮起来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询