RK3588工业HMI开发:Qt交叉编译与EGLFS渲染实战
2026/9/19 1:20:00 网站建设 项目流程

1. 为什么是RK3588:工业HMI选型与这块板子的底细

1.1 一份板子半部"家底":核心硬件资源盘点

做工业HMI(人机交互界面)项目时,芯片选型往往比选Qt版本更让人纠结。RK3588这几年在国产工业板卡里出镜率极高,原因很实际:4个Cortex-A76大核加4个Cortex-A55小核,8nm工艺,单看CPU性能就比过去常见的四核A53、四核A72方案高出一大截。我们做Qt应用,最怕的就是界面稍微复杂一点,CPU占用就飙到80%以上,动画还是掉帧。A76大核跑QML场景图、做复杂绘制,A55小核做串口采集、协议解析,这个分工非常舒服。

GPU部分是Arm Mali-G610 MP4。放在工控领域,这块GPU的性能算是"溢出"的,OpenGL ES 3.2、Vulkan 1.2都支持。很多老工程师一听到GPU就想到游戏显卡,其实在嵌入式场景里,GPU是拿来给界面做硬件加速的:大图缩放、模糊特效、视频叠加、多屏拼接,全部由GPU扛,CPU留出来干业务。Mali-G610跑1080p界面的动态特效,余量非常大,4K界面只要不过度设计,也能维持在不错的帧率。

创龙这块板子是典型的"核心板+底板"结构,核心板负责CPU、内存、存储,底板引出串口、网口、CAN、USB、显示接口这些工业接口。这种结构对做产品很友好:核心板固定,底板按项目重新画,绕开了BGA贴片和DDR布线这些高风险环节。工业温度范围、长供货周期,再加上国产化替代的大背景,这类板子在电力、交通、医疗设备、数控机床项目里越来越常见。

1.2 Qt在这块板子上到底承担什么角色

RK3588上跑Qt,不只是画几个按钮那么简单。一个典型的工业设备界面,Qt要同时管好几件事:HMI人机交互、实时数据曲线绘制、报警事件列表、参数配置页面、通信状态监控,有时候还要内嵌视频流窗口。纯用QWidget硬画所有东西,代码能写,性能不一定扛得住;纯用QML做一切,碰到复杂的工业协议交互又有点绕。

我自己的习惯是:界面层用QML搭骨架,业务逻辑用C++写,通过模型/视图机制对接。QML适合做动效和布局,C++适合做采集、通信、算法。串口、Modbus、CAN这些工业通信协议,全部放在C++线程里,通过信号槽抛给QML层刷新。这套架构在这个性能档位的板子上非常稳定,也是后面所有交叉编译、GPU渲染工作的前提。

2. 交叉编译环境搭建:工具链、sysroot与Qt源码构建

2.1 工具链选择与sysroot准备

交叉编译的第一步不是下载Qt源码,而是先把交叉编译工具链准备好。RK3588是64位ARM架构,所以目标是aarch64。首选方案是直接用创龙SDK里自带的交叉编译工具链,别自己从Linaro或其他地方另搞一套。厂家的工具链和他们的内核、库版本是配套测过的,省去一堆ABI兼容问题。我见过太多人在这里翻车:自己装了个gcc-aarch64-linux-gnu,结果编译出来的程序板子上跑不起来,报GLIBC版本不兼容,折腾半天才发现是工具链版本和板端系统库对不上。

如果手头没有厂家SDK,Ubuntu上装一个基础工具链也很简单:

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

但这里有个关键点:工具链只是编译器本身,编译Qt和第三方库时还需要目标板的头文件和库,也就是sysroot。没有sysroot,编译器就不知道目标板上有哪些库、什么版本、头文件在哪。最简单可靠的sysroot获取方式,是从板子上直接同步:

mkdir -p ~/rk3588-sysroot sudo rsync -avz --delete \ --exclude='/proc' --exclude='/sys' --exclude='/dev' --exclude='/run' \ root@<板子IP>:/ ~/rk3588-sysroot/

同步完以后,编译时通过--sysroot参数指过去,编译器就会先去sysroot里找目标架构的头文件和库。用rsync同步时一定要保留符号链接,-a选项已经包含这个行为,别自己手贱再加-L把符号链接解引用,否则板子上原本指向libmali.so.1的链接全变成物理文件,后面排查问题会非常痛苦。

2.2 Qt源码编译的configure参数

Qt源码建议用qt-everywhere-src-5.15.2,这是目前工业项目里最主流的LTS版本。5.15.10、5.15.12的编译参数也基本一致,参数有问题排查思路完全相同。解压后进入目录,执行configure。我给出一个在实际项目中验证过的配置:

./configure \ -prefix /opt/qt5.15.2-aarch64 \ -xplatform linux-aarch64-gnu-g++ \ -opensource -confirm-license \ -release -strip -shared \ -opengl es2 \ -eglfs -linuxfb \ -nomake examples -nomake tests -no-compile-examples \ -skip qtwebengine \ -skip qt3d \ -skip qtcanvas3d \ -skip qtdoc \ -skip qtgamepad \ -skip qtpurchasing \ -skip qtvirtualkeyboard \ -skip qtwebglplugin \ -skip qtscript \ -no-feature-xcb

逐个解释关键参数为什么这么设。-xplatform linux-aarch64-gnu-g++指定交叉编译平台,对应Qt源码里自带的mkSpec文件。-opengl es2必须保留,RK3588的Mali-G610走的就是OpenGL ES路径,编译成桌面的desktop OpenGL没有意义,它只支持ES。-eglfs生成EGLFS平台插件,这是无桌面环境下Qt直接操作显示设备的核心。-linuxfb作为备用的软件渲染后端留着,EGLFS万一出问题还能降级跑个基础界面。

-skip qtwebengine是必须的,WebEngine内核交叉编译极其痛苦,工业项目基本用不到浏览器内核。其他skip的模块都是按"当前项目用不到"裁剪的,如果你项目里需要3D、需要虚拟键盘,对应的-skip要去掉。这里要特别注意-skip qtmultimedia,视频播放、摄像头采集往往依赖这个模块,但交叉编译时它又依赖gstreamer,要不要保留得提前想清楚,别为了省编译时间全部skip,后面又追悔莫及。

configure命令执行完后,编译安装:

make -j$(nproc) sudo make install

编译时间取决于机器性能,16核机器大概40分钟到一个小时。configure阶段如果报缺依赖,多数是xcb相关库在交叉编译环境里找不到,-no-feature-xcb已经把这个坑提前踩掉了。

2.3 常见模块缺失的排查思路

很多人在网上搜到"unknown module(s) in qt: serialport"这个报错,问遍了社区也找不到答案。这个问题的根源多半出在configure阶段:Qt的超级模块工程里,serialport不是qtbase自带的,而是独立模块,configure时被某个-skip误伤,或者你只编译了qtbase没有编其他模块。

排查思路按顺序来:

  1. 在编译机上用安装好的qmake查询配置信息:/opt/qt5.15.2-aarch64/bin/qmake -query,能看到QT_INSTALL_PREFIX等路径。
  2. 检查ls /opt/qt5.15.2-aarch64/lib/cmake/,如果里面没有Qt5SerialPort目录,说明这个模块根本没装。
  3. 检查configure时的输出日志,搜serialport关键字,看是被skip了还是编译失败了。

确认是模块缺失后,没必要重新编译整个Qt。单独编译serialport模块就行:

cd qt-everywhere-src-5.15.2/qtserialport /opt/qt5.15.2-aarch64/bin/qmake make -j$(nproc) sudo make install

编译安装完,qmake -query里就能看到serialport相关配置,工程文件里的QT += serialport也能正常解析。

这里还有个小细节:qmake编译模块时,会自动去QT_INSTALL_PREFIX路径下找同版本Qt的头文件。如果你之前configure时改了prefix,编译模块前一定要确认prefix是否一致,否则会出现模块编译成功、但装到了另一个路径的情况。

3. 渲染链路拆解:EGLFS、Mali驱动与Qt绘制路径的选择

3.1 从QML到一个像素上屏的完整旅程

Qt程序在RK3588上画一帧画面,从底层到上层的路径是很多人没搞明白的地方。简单说:屏幕是DRM/KMS管的,GPU是Mali驱动的用户态库管的,Qt的GUI层通过EGL把两者接起来。

我画过一张脑图,分成四层:

  • 最上层是Qt的渲染引擎。QWidget用的是光栅化引擎(CPU绘制),QML用的是场景图(Scene Graph)引擎,默认走OpenGL ES加速。
  • 第二层是EGL。EGL是Khronos定义的窗口系统与渲染API之间的中间层,负责创建渲染表面、管理上下文。Qt通过EGL把OpenGL ES命令提交给GPU。
  • 第三层是Mali用户态驱动,就是板子上那个libmali.so。它提供EGL、GLES、Vulkan、OpenCL这些API的实现,直接和内核里的DRM驱动交互。
  • 最底层是内核DRM/KMS,负责控制显示控制器、扫描输出到HDMI、DSI、eDP这些物理接口。

明白了这条链路,后面遇到任何显示问题都能按层排查:画面不出来,先查DRM;颜色不对,查EGL配置;卡顿,查GLES命令是否触发了大量纹理上传;花屏,查Mali驱动和内核版本是否匹配。

3.2 三种绘制路径的性能差异:光栅、OpenGL与QML场景图

Qt 5.x里有个长期存在的误解:QWidget程序就是慢,QML程序就是快。其实不对。

QWidget默认的绘制方式是光栅化(Raster),所有绘制指令由CPU执行,画完以后整帧上传到GPU做显示。这种方式的优点是兼容性好、实现简单,缺点是CPU就是性能瓶颈。你画一条复杂曲线,画一张大图缩放,CPU占用会直接拉满。在RK3588这种8核平台上,A76大核能力很强,简单界面看不出问题,但一旦涉及到全屏刷新、透明混合、大图频繁缩放,CPU就会成为瓶颈。

QWidget也可以切换到OpenGL渲染路径,但真正愿意折腾这个的人很少,配置复杂且部分API不生效。更常见的方案是用QOpenGLWidget,在OpenGL环境里用QPainter画,或者直接用原生GLES命令绘制。这样既保留了QPainter的绘制接口,又让GPU承担了光栅化工作。

QML这条路走的是Scene Graph,节点树的渲染在GPU上完成。所以QML做动效、做转场、做粒子效果、做视频叠加,天生就有优势。工业HMI如果要做一个现代化风格的界面,各种滑动、缩放、渐变效果,QML是首选。

我把三种路径的适用场景整理成一个对比表,方便直接对照选型:

渲染路径执行单元适合场景典型瓶颈
QWidget + RasterCPU参数表单、日志列表、简单报警页面大图缩放、全屏重绘
QWidget + QOpenGLWidgetGPU需要QPainter接口又要硬件加速纹理上传、GL状态切换
QML + Scene GraphGPU动效界面、大屏数据可视化、视频叠加过度绘制、纹理数量过多

3.3 写工业曲线控件,为什么我最终选择了自定义GL节点

去年做一个温度采集项目,要求实时显示16路温度曲线,每路每秒刷新一次,历史曲线可以缩放拖拽。最开始用QWidget的QPainter画,1080p全屏下CPU占用直接到35%,缩放时卡顿明显。A76核心确实强,但QPainter每次都要重建整个曲线路径,几百个点加抗锯齿,CPU扛不住。

后来改成QQuickPaintedItem,直接在QML里画,CPU占用降了一截,但还是不够理想。最后用Scene Graph的自定义节点(QSGGeometryNode)把曲线顶点直接以三角形条带形式提交给GPU,每帧只需要更新顶点buffer,CPU占用降到5%。这个改动让整个界面从"勉强能用"变成"非常顺滑"。

这个案例想说明的是:在RK3588上,GPU资源是富余的,真正缺的是好的绘制策略。能预编译成纹理的就编译成纹理,能用顶点数据算的就用GPU算,别让CPU去逐帧画所有东西。

4. 上板部署与第一帧画面:镜像、环境变量与运行检查

4.1 确认开发板GPU环境是健康的

拿到板子后,别急着拷程序上去,先确认GPU环境没问题。以创龙的Ubuntu镜像为例,登录板子后跑一趟glmark2-es2:

sudo apt install glmark2-es2 glmark2-es2

glmark2-es2是一个基于OpenGL ES 2.0的基准测试工具,跑分能通过说明EGL、GLES、DRM这条链路是通的。RK3588在这类工具上的分数在2000到4000之间,具体数值受镜像版本、驱动版本、散热状态影响,不用纠结具体数字,只要它正常跑完整个测试场景,GPU环境就是好的。

还要检查几个关键节点:

ls /dev/dri dmesg | grep -i mali ls /usr/lib/aarch64-linux-gnu/ | grep -i mali

/dev/dri下面要有card0和renderD128,这是DRM设备节点。dmesg里应该有Mali驱动的初始化日志,没有的话说明驱动没加载。库文件里应该能看到类似libmali.so的文件,注意它的存在形式,很多BSP是通过符号链接把libEGL、libGLESv2指向libmali的。

4.2 运行时环境变量:EGLFS上板前必做的配置

在板子上跑Qt程序,最省资源的方案是不启动桌面环境,直接用EGLFS平台插件,让Qt进程自己接管显示设备。运行前设置这几个环境变量:

export QT_QPA_PLATFORM=eglfs export QT_QPA_EGLFS_INTEGRATION=eglfs_kms export QT_QPA_EGLFS_ALWAYS_SET_MODE=1 export QT_QPA_EGLFS_HIDECURSOR=0

逐个说说它们的含义。QT_QPA_PLATFORM=eglfs告诉Qt使用EGLFS插件而不是xcb或linuxfb。QT_QPA_EGLFS_INTEGRATION=eglfs_kms指定EGLFS的后端集成方式,在RK3588上,kms后端通过DRM/GBM直接操作显示控制器,这是最主流的方案。有的BSP也提供eglfs_drm或者专有的eglfs_rk后端,具体用哪个,以创龙BSP文档为准。

QT_QPA_EGLFS_ALWAYS_SET_MODE=1表示每次切换全屏时都重新设置显示模式。默认值就是1,它保证了分辨率跟屏幕EDID一致。把值改成0可以减少切换时的闪烁,但可能导致分辨率不被正确检测,画面偏小或偏移。

还有一个容易踩的坑:EGLFS下Qt程序无法直接使用中文输入法和X11风格的鼠标光标。工业触摸屏场景无所谓,但如果你用普通显示器调试,会发现鼠标光标不见了。想恢复光标,可以设置QT_QPA_EGLFS_HIDECURSOR=0,然后在程序里加载一个自定义光标图像,或者在BSP支持的情况下使用tslib/libinput的touch插件。

4.3 第一帧画面上屏:常见启动失败对照表

程序编译出来后,通过scp拷到板子,运行前做一件事:ldd检查动态库依赖。

ldd ./your_app

ldd会列出程序依赖的所有动态库,如果某个库显示"not found",说明板子上缺少运行库。最常见的缺失是libGLESv2.so、libEGL.so这两个符号链接。Mali驱动库通常只提供版本化文件(比如libGLESv2.so.2),但编译时链接的是非版本化的libGLESv2.so,板子上没有这个链接文件就会启动失败。解决方法是手动补:

sudo ln -s /usr/lib/aarch64-linux-gnu/libGLESv2.so.2 /usr/lib/aarch64-linux-gnu/libGLESv2.so sudo ln -s /usr/lib/aarch64-linux-gnu/libEGL.so.1 /usr/lib/aarch64-linux-gnu/libEGL.so

启动失败时,按这个对照表排查:

现象可能原因排查命令
黑屏但进程在跑EGLFS未能正确初始化KMS后端QT_QPA_EGLFS_INTEGRATION=eglfs_kms是否正确;检查/dev/dri节点
启动即报错"eglfs: Failed to create window surface"Mali库版本与EGLFS不匹配确认板端libmali是否支持GBM,必要时换eglfs_drm后端
颜色偏色/发紫EGLFS色彩格式与屏幕不匹配尝试export QT_QPA_EGLFS_FORCE_888=1强制RGB888
程序运行但无响应可能是CPU架构不对,跑的是x86程序file命令确认ELF格式为aarch64

第一帧画面上屏之后,整个交叉编译环境就算彻底打通了。接下来才是重头戏:性能调优。

5. 性能实测与调优:从帧率统计到渲染瓶颈定位

5.1 帧率统计的两种手段

调优的前提是能量化性能。我在板子上通常用两种方式测帧率。

第一种是QML里的Approach。在根窗口上连frameSwapped信号,统计每秒触发次数:

import QtQuick 2.15 Window { id: win property int frameCount: 0 property double lastTime: 0 property double fps: 0 onFrameSwapped: { frameCount++ var now = Date.now() if (now - lastTime >= 1000) { fps = frameCount * 1000 / (now - lastTime) frameCount = 0 lastTime = now console.log("fps:", fps) } } }

这个值用于判断趋势足够了,但不要把小数点后的数字当精确值。更精确的做法是在C++侧创建一个QQuickWindow的子类,在beforeRendering信号里用QElapsedTimer记录帧间隔,因为渲染线程的信号比QML脚本里的回调更贴近真实渲染时机。

第二种是针对QWidget的粗糙统计法:在paintEvent里加一个计数器,用一个QTimer每秒打印一次。虽然QWidget在无变化时不会触发重绘,导致这个计数看起来是0,但这正好能帮你判断"控件是否在不必要地疯狂重绘"——如果界面静止时paintEvent还在以几十甚至上百次每秒的频率触发,说明代码里某个属性绑定导致无限重绘循环,这是QWidget程序卡顿的头号元凶。

5.2 性能瓶颈的定位顺序:CPU、GPU还是纹理上传

遇到界面卡顿,别急着改代码,按顺序排查才能对症下药。

第一,用top看CPU占用。如果CPU占用很高,说明瓶颈在CPU侧:可能是QPainter在大量绘制,可能是QML脚本里有重型计算,也可能是某个属性反复触发重绘。CPU侧瓶颈的解法很直观:把绘制搬给GPU,把计算搬到C++线程。

第二,如果CPU占用不高但界面仍然卡顿,问题多半在GPU侧。Mali驱动在有些BSP里会通过debugfs暴露GPU频率和占用率,可以查/sys/kernel/debug/mali/下的节点,看GPU是否满负荷。如果GPU占用高,就要考虑减少绘制复杂度:降低过度绘制、减少透明混合层级、缩小纹理尺寸、避免逐帧创建销毁缓冲对象。

第三,最容易忽略的是纹理上传瓶颈。QML或者QOpenGLWidget里,如果把QImage转成QSGTexture或者QOpenGLTexture,这个上传过程涉及CPU到GPU的拷贝,开销很大。我见过一个案例,一个界面只是改了背景图片大小,每帧都在重新上传整张纹理,导致GPU占用不高但帧率上不去。解法是把纹理上传放到初始化阶段,运行期只复用纹理ID,不重新上传。

5.3 双屏拼接显示:KMS配置实战

RK3588的显示控制单元支持多屏输出,很多工业项目会做"一个屏显示参数,一个屏显示视频流"这种设计。EGLFS的KMS后端支持多屏配置,通过一个JSON文件描述每个输出的位置、分辨率和旋转:

{ "device": "/dev/dri/card0", "outputs": [ { "name": "HDMI-A-1", "mode": "1920x1080", "pos": "0,0" }, { "name": "eDP-1", "mode": "1280x800", "pos": "1920,0", "rotation": "90" } ] }

设置环境变量让Qt读取配置:

export QT_QPA_EGLFS_KMS_CONFIG=/path/to/kms-config.json

这个功能的前提是KMS后端能正确枚举显示器,先通过modetest(libdrm-tools包提供)看两个接口是否都被识别。多屏配置在Qt不同小版本上表现有差异,5.15.2相对稳定,个别版本在旋转参数上存在bug,如果旋转后画面错乱,先确认板端镜像的Qt版本和驱动版本是否匹配。

5.4 稳定帧率的关键:别让UI线程做脏活

调优最后一定会落到代码结构上。工业HMI项目里最大的性能杀手,是在UI线程里做文件IO、数据库查询、网络请求。RK3588的CPU再强,也扛不住UI线程被一个网络超时卡住几百毫秒。

我的习惯是:所有串口、Modbus、CAN、Socket操作全部放独立线程,跨线程通信只通过信号槽传递数据副本。UI侧只负责把数据刷到界面上。还要注意信号槽连接方式,高频数据流如果使用Qt::QueuedConnection,事件循环处理不过来时界面就会卡顿。数据量大的场景,建议用线程内缓存加定时器批量刷新界面的方式,比如每100ms统一刷新一次所有温度值,而不是每收到一帧数据就更新一次文本。

6. 工业项目里的"隐藏坑":串口模块、第三方库与版本一致性

6.1 serialport模块缺失的完整排查记录

前面第2章简单提过serialport模块,这里展开讲一次完整的排查过程,因为这个问题在工业Qt项目里太常见了。

一个同事拿着编译好的程序到板子上跑,启动后界面正常,但程序日志里一直报qt.qpa.plugin: Could not load the Qt platform plugin "eglfs",仔细看不是platform的问题,是程序初始化时加载QtSerialPort模块失败,运行时报错Unknown module(s) in QT: serialport

排查分四步。第一步,在编译机上用安装好的qmake检查模块:/opt/qt5.15.2-aarch64/bin/qmake -query,确认QT_INSTALL_PREFIX指向正确。第二步,查安装目录下有没有serialport相关内容:find /opt/qt5.15.2-aarch64 -name "*serialport*",结果为空,确认模块根本没装。第三步,回想当时的configure参数,果然把一些模块加入了-skip列表,serialport就在其中。当时为了赶编译时间,一次性skip了十多个模块,没仔细核对每个模块的用途。第四步,单独编译serialport模块并安装,问题解决。

这个案例值得警惕的地方不是"模块怎么装回来",而是"为什么一开始会skip掉它"。很多Qt模块在configure阶段看似无关紧要,实际运行时却是项目依赖。建议在configure前,把项目工程文件里的QT +=逐项列出来,对照模块清单检查,不要凭感觉批量skip。

6.2 Boost库与第三方库的交叉编译模板

工业项目里绕不开Boost库。RK3588上编译Boost比Qt简单,但有个细节:Boost的b2构建系统默认会使用主机编译器,必须显式指定交叉编译工具集。

先创建user-config.jam:

using gcc : arm : aarch64-linux-gnu-g++ ;

然后执行:

./bootstrap.sh --prefix=$PWD/_install ./b2 --user-config=user-config.jam toolset=gcc-arm target-os=linux \ --prefix=$PWD/_install install

注意target-os必须指定为linux,否则b2会按Windows或者macOS的ABI规则处理。编译完成后,_install/include_install/lib下的头文件、库文件,在Qt工程文件里通过INCLUDEPATH和LIBS指过去即可。

其他常规C库的交叉编译套路更简单,几乎都是三板斧:

./configure --host=aarch64-linux-gnu --prefix=$PWD/install make -j$(nproc) make install

重点说一下--host参数,它告诉configure脚本目标平台是aarch64-linux-gnu,configure会据此检测交叉编译环境并设置正确的编译器和前缀。忘记加--host是新手最容易犯的错,结果就是configure检测到的是主机环境,make的时候整出各种奇怪的链接错误。

6.3 版本一致性管理:避免ABI不兼容的玄学问题

最后说一个容易被人忽略、但实际项目中坑最多的问题:版本一致性。

我见过最典型的场景是:编译机上装的是Qt 5.15.2,板子上跑的是系统自带的Qt 5.12库,程序拷贝过去后要么缺符号直接崩溃,要么界面控件布局错乱。交叉编译项目里,编译环境和运行环境的版本必须严格一致,包括Qt版本、编译器版本、库版本,差一个小版本都可能引入ABI不兼容。

管理版本一致性有几个实用习惯:

  1. 编译机上固定使用/opt/qt5.15.2-aarch64这个独立目录,不跟系统自带的Qt混用。
  2. 板子部署时不要依赖板端系统库里的Qt库,而是把编译机上安装的Qt库目录整个拷到板子,通过LD_LIBRARY_PATH指定运行路径。比如把/opt/qt5.15.2-aarch64/lib对应拷到板子的/opt/qt5.15.2-aarch64/lib
  3. 第三方库(Boost、OpenSSL、libgpiod等)交叉编译完,记录下configure参数和编译日期,方便后续回溯。

BSP镜像升级后,也要重新验证一遍GPU环境和Qt库兼容性。RK3588的Mali驱动跟随BSP迭代,有些版本对EGLFS的支持细节有变化,升级镜像后最好重跑一次glmark2-es2和你的核心界面场景,避免旧库新镜像这种组合导致的问题。

6.4 开机自启与看门狗:Qt程序部署的最后一步

工业设备上Qt程序一般通过systemd管理开机自启。这里有一个容易翻车的细节:systemd服务文件里必须显式设置EGLFS相关环境变量,否则程序虽然启动,但会尝试加载xcb插件失败,日志里全是"could not load platform plugin"。

一个典型的service文件示例:

[Unit] Description=Industrial HMI Application After=network.target [Service] Type=simple User=root Environment=QT_QPA_PLATFORM=eglfs Environment=QT_QPA_EGLFS_INTEGRATION=eglfs_kms Environment=QT_QPA_EGLFS_ALWAYS_SET_MODE=1 ExecStart=/opt/app/hmi_app Restart=always RestartSec=3 [Install] WantedBy=multi-user.target

看门狗也是个容易被忽略的点。工业现场程序跑几个月不重启是常态,一旦UI线程卡死,整台设备就"假死"了。可以在Qt程序里开一个独立线程定期刷新一个时间戳,外部看门狗脚本检查时间戳超过阈值就强制重启进程。这种做法不算优雅,但在很多工控项目里是最可靠的兜底方案。

最后再分享一个对新项目最实用的习惯:拿到开发板的第一周,先别急着写业务代码,把编译环境、部署流程、性能基线全部跑通。工具链怎么配、sysroot从哪来、Qt编译需要哪些参数、第一帧画面上屏要哪几步、glmark2跑多少分,这些全部记录成文档或者脚本,后续项目都能复用。我当时就是先把这套流程固化成脚本,后面几个项目都基于同一套环境,几乎没有再为环境问题浪费过时间。

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

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

立即咨询