
拿到一块瑞芯微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 qtwebengineQt WebEngine体积庞大且依赖很多嵌入式场景基本用不到直接跳过。如果你后面想用OpenGL做动画效果RK3506有Mali GPU可以尝试编译EGLFS版本但需要厂家提供GPU的用户态驱动和libEGL库工作量会大不少。刚上手跑通流程我建议先不折腾GPUlinuxfb足够了。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,prototcp 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_PLATFORMlinuxfb 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_HIDEFREQUENCY60如果你的板子接的是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 serialportqmake也找不到对应模块。先确认你编译安装的Qt库里有没有libQt5SerialPort.so.5find /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目录失败我列一个排查顺序先看主机和开发板能不能ping通网络不通一切免谈。主机上执行showmount -e 主机IP如果显示不出共享目录说明NFS服务配置问题检查/etc/exports。挂载时带上nolock命令改成mount -t nfs -o nolock。开发板没跑rpc.statd的情况下不带nolock很容易挂死挂载命令卡住无响应。尽量把主机防火墙关掉或者放行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程序早日在板子上亮起来。