RK3568嵌入式Linux多屏显示实战:Qt与DRM框架深度整合

张开发
2026/4/11 8:58:27 15 分钟阅读

分享文章

RK3568嵌入式Linux多屏显示实战:Qt与DRM框架深度整合
1. RK3568多屏显示架构解析在RK3568平台上构建多屏显示系统本质上是在玩一场硬件与软件的俄罗斯方块游戏。这块Rockchip的明星芯片内置了强大的VOPVideo Output Processor视频输出控制器就像个经验丰富的调度员能够同时管理多个显示通道。我去年在工业HMI项目中使用这套方案时发现其最大优势在于能同时驱动HDMI、MIPI-DSI、LVDS等不同接口的显示屏就像用同一个遥控器控制家里所有电器。整个技术栈可以分为三个关键层硬件抽象层DRMDirect Rendering Manager框架是Linux内核的显示管家负责与RK3568的显示子系统对话。通过/dev/dri/card0这个设备节点我们可以直接操纵显示控制器、CRTC、编码器等硬件模块中间适配层Qt的eglfs_kms插件在这里扮演翻译官角色把Qt应用的绘制指令转换为DRM能听懂的语言。特别要注意的是Rockchip对标准DRM做了定制扩展比如支持RGA硬件加速应用层我们的Qt程序只需要关心要在哪个屏幕显示什么内容底层的脏活累活都交给前面两层处理实测中发现一个有趣现象当同时连接HDMI和MIPI屏时RK3568的VOP会自动将两个显示通道的时序同步这比某些需要手动调时序的芯片省心多了。不过要注意内存带宽分配建议在设备树中为每个显示通道预留足够的带宽。2. 开发环境搭建实战第一次配置这个环境时我踩了不少坑。最头疼的是内核版本兼容性问题——某些4.4内核的BSP包虽然能跑但DRM驱动功能残缺。后来换用主线内核5.10以上版本所有功能都正常了。下面是血泪教训总结的配置清单系统基础环境# 检查内核DRM支持 grep -i drm /boot/config-$(uname -r) # 确认存在视频设备节点 ls -l /dev/dri/Qt编译关键参数./configure -prefix /opt/qt5.15 \ -platform linux-g \ -opengl es2 \ -eglfs \ -kms \ -no-xcb \ -skip qtvirtualkeyboard \ -nomake examples这里有个容易忽略的点必须禁用xcb后端否则Qt会优先使用X11而不是DRM。我在某次编译后忘记加-no-xcb参数结果多屏功能死活不生效排查了两天才发现这个问题。依赖库版本要求libdrm ≥ 2.4.104支持Atomic模式提交Mesa3D ≥ 20.2提供完整的OpenGL ES支持Linux内核 ≥ 5.10完整支持Rockchip DRM驱动建议用buildroot或Yocto构建完整文件系统时把这些依赖项版本锁死。我有次因为libdrm自动升级导致Qt应用闪退最后不得不回滚版本。3. Qt多屏方案深度优化3.1 自动检测与窗口分配Qt自带的QScreen类能自动发现所有连接的显示屏但默认行为可能不符合预期。比如在车载双屏项目中主驾屏和副驾屏需要显示完全不同内容。这是我的改进方案// 创建带屏幕感知的窗口 class ScreenAwareWindow : public QWindow { public: explicit ScreenAwareWindow(QScreen* targetScreen) { setScreen(targetScreen); // 关键设置窗口与屏幕相同尺寸 setGeometry(screen()-geometry()); // 启用硬件加速 setSurfaceType(QSurface::OpenGLSurface); } }; // 应用初始化 QGuiApplication app(argc, argv); foreach (QScreen* screen, app.screens()) { qDebug() Found screen: screen-name() Geometry: screen-geometry(); if (screen-name().contains(HDMI)) { // 主控制界面 ScreenAwareWindow* mainWin new ScreenAwareWindow(screen); mainWin-setTitle(Control Panel); } else if (screen-name().contains(DSI)) { // 状态显示界面 ScreenAwareWindow* statusWin new ScreenAwareWindow(screen); statusWin-setTitle(Status Monitor); } }3.2 动态分辨率适配不同显示设备的最佳分辨率各异我在医疗设备项目中遇到过4K监视器和800x600的工控屏混用的情况。Qt的QScreen虽然能获取当前分辨率但有时需要强制特定模式# 通过环境变量指定主屏分辨率 export QT_QPA_EGLFS_WIDTH1920 export QT_QPA_EGLFS_HEIGHT1080更专业的做法是配置qt-kms.json文件{ device: /dev/dri/card0, outputs: [ { name: HDMI-A-1, mode: 3840x2160, position: 0,0 }, { name: DSI-1, mode: 800x600, position: 3840,0 } ] }3.3 屏幕旋转与触摸校准工业平板常需要竖屏显示除了配置rotation: 90参数外还要处理触摸坐标转换// 旋转屏幕后修正触摸事件 bool eventFilter(QObject* obj, QEvent* event) { if (event-type() QEvent::TouchUpdate) { QTouchEvent* touch static_castQTouchEvent*(event); foreach (QTouchEvent::TouchPoint point, touch-touchPoints()) { QPointF pos point.pos(); // 根据旋转角度转换坐标 if (screen()-orientation() Qt::PortraitOrientation) { pos QPointF(size().height() - pos.y(), pos.x()); } // 处理转换后的坐标... } return true; } return false; }4. DRM直接操作进阶技巧当Qt的抽象层无法满足需求时直接操作DRM API是终极解决方案。我在数字标牌项目中就用这种方法实现了精确到帧的同步控制。4.1 原子模式提交现代DRM推荐使用Atomic模式配置显示管线这是防止画面撕裂的关键drmModeAtomicReq* req drmModeAtomicAlloc(); // 设置CRTC属性 drmModeAtomicAddProperty(req, crtc_id, prop_active, 1); drmModeAtomicAddProperty(req, crtc_id, prop_mode_id, mode_id); // 设置连接器属性 drmModeAtomicAddProperty(req, connector_id, prop_crtc_id, crtc_id); // 提交变更 int ret drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); if (ret 0) { qWarning() Atomic commit failed: strerror(errno); } drmModeAtomicFree(req);4.2 多屏帧同步要实现类似视频墙的完美同步需要启用DRM的VSYNC同步机制// 设置VSYNC事件监听 drmEventContext evctx { .version 2, .vblank_handler vblank_handler, .page_flip_handler page_flip_handler }; // 在页面翻转时等待VSYNC drmModePageFlip(fd, crtc_id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, NULL); // 事件处理循环 while (true) { fd_set fds; FD_ZERO(fds); FD_SET(fd, fds); select(fd 1, fds, NULL, NULL, NULL); drmHandleEvent(fd, evctx); }4.3 内存分配策略多屏系统对内存带宽极其敏感建议使用DRM的GEM分配器并设置缓存策略struct drm_rockchip_gem_create gem_create { .size width * height * 4, .flags ROCKCHIP_BO_CONTIG | ROCKCHIP_BO_CACHABLE }; ioctl(fd, DRM_IOCTL_ROCKCHIP_GEM_CREATE, gem_create); // 设置内存域 struct drm_mode_map_dumb map_req { .handle gem_create.handle }; ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, map_req);5. 性能调优实战记录在跑车仪表盘项目里我们实现了三屏4K60fps的极致性能以下是关键优化点5.1 渲染流水线优化// 使用OpenGL ES直接渲染到纹理 QOpenGLFramebufferObject fbo(size()); fbo.bind(); glViewport(0, 0, width(), height()); // 绘制操作... fbo.release(); // 将纹理提交到DRM EGLImageKHR image eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, fbo-texture(), NULL); drmModeAddFB2WithModifiers(fd, width, height, DRM_FORMAT_ARGB8888, handles, strides, offsets, modifiers, fb_id, 0);5.2 硬件加速妙用RK3568的RGARaster Graphic Acceleration单元能大幅提升2D操作效率# 启用RGA加速 export QT_EGLFS_IMX6_NO_FB_MULTI_BUFFER1 export QT_EGLFS_IMX6_DISABLE_GPU05.3 内存带宽监控# 实时查看内存带宽占用 cat /sys/kernel/debug/dri/0/memory_stats # 监控VOP状态 cat /sys/kernel/debug/dri/0/vop/status6. 典型问题排查指南黑屏但背光亮# 检查连接器状态 modetest -M rockchip # 查看当前CRTC配置 cat /sys/kernel/debug/dri/0/state画面撕裂// 确保启用VSYNC drmModeAtomicAddProperty(req, crtc_id, prop_vsync_enable, 1);多屏不同步# 强制刷新所有CRTC echo 1 /sys/class/drm/card0/device/vblank_update7. 项目部署经验谈在产线部署时我总结了一套标准化流程固化显示配置# 生成初始modeset配置 modetest -M rockchip -s 1080x72060 # 保存为持久化配置 cp /etc/qt-kms.conf /etc/qt-kms.conf.factory自动化测试脚本def test_display(connector): subprocess.run([modetest, -M, rockchip, -c, connector], checkTrue) assert os.path.exists(f/sys/class/drm/{connector}/status)现场恢复方案# 备份原始配置 dd if/dev/mmcblk0p1 of/recovery.img # 快速恢复 rockchip_flash_tool -i /recovery.img

更多文章