news 2026/6/10 9:01:47

RK3568双屏异显实现:framebuffer实战案例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RK3568双屏异显实现:framebuffer实战案例

RK3568双屏异显实战:从帧缓冲到工业级显示控制

你有没有遇到过这样的场景?一台工控设备,主屏要跑操作界面,副屏却得实时显示监控视频流或广告轮播——两个屏幕内容完全不同,刷新节奏也不一致。传统的桌面系统镜像模式显然不够用,而上X11或Wayland又太重,启动慢、资源占用高。

这时候,framebuffer就成了嵌入式开发者的“秘密武器”。

以瑞芯微RK3568为例,这款四核A55的国产SoC不仅性能强劲,其显示子系统更是支持HDMI、LVDS、MIPI DSI等多种接口组合。本文不讲理论套话,直接带你动手实现双屏异显,深入Linux图形栈底层,看看如何用最轻量的方式掌控两块屏幕。


为什么选 framebuffer?一个被低估的“老将”

很多人一提图形就想到Qt、Wayland甚至Android HAL,但在工业控制、自助终端这类对稳定性要求极高的场景里,越简单越可靠

framebuffer是什么?

你可以把它理解为一块“映射到内存的画布”。只要往这块内存写像素数据,屏幕就会实时更新。它没有窗口管理器、没有合成器、不需要复杂的协议交互——open → mmap → 写内存 → 刷新完成,整个过程干净利落。

更重要的是:
- 启动速度快(内核加载完就能用)
- 资源消耗低(几百KB内存搞定)
- 多屏原生支持(/dev/fb0,/dev/fb1独立存在)
- 编程模型直观(指针操作即可绘图)

别看它是“古董级”接口,在RK3568这种现代平台上,它的背后其实是DRM/KMS 驱动在支撑,硬件能力一点没打折。


双屏是怎么“分家”的?VOP架构揭秘

RK3568能实现真正的“异显”,核心在于它的双VOP设计

VOP(Video Output Processor)是Rockchip自研的显示控制器模块。RK3568有两个独立的VOP单元:VOP0 和 VOP1,每个都可以驱动一个显示通道。比如:

  • VOP0 → HDMI(1920×1080@60Hz)
  • VOP1 → LVDS(1024×600@60Hz)

这两个输出完全独立,分辨率、刷新率、色彩格式互不影响。它们通过DRM子系统创建出两个不同的CRTC(直白说就是“显示管道”),最终分别绑定到/dev/fb0/dev/fb1

这意味着什么?

你可以让fb0显示一张静态图片,fb1播放动画,两者并行不悖,毫无干扰。

这可不是简单的“复制粘贴”,而是真正意义上的多任务并行显示


实战第一步:确认你的双屏已就位

在动手编码前,先确保硬件和驱动已经正确识别了两个屏幕。

检查设备节点

ls /dev/fb* # 应该看到: # /dev/fb0 # /dev/fb1

查看各屏信息

# 查看fb0的基本参数 fbset -fb /dev/fb0 # 输出示例: # geometry 1920 1080 1920 1080-1 32 # 这表示:宽1920,高1080,虚拟宽度1920,深度32位

也可以读取sysfs信息:

cat /sys/class/graphics/fb0/name # 输出可能为:rockchipdrmfb-vop0 cat /sys/class/graphics/fb1/name # 输出可能为:rockchipdrmfb-vop1

如果名字不同且都指向rockchipdrm,则说明双通道已激活。


核心代码:如何向两个屏幕分别绘图?

下面这个例子会告诉你,如何用C语言同时控制两个屏幕,各自绘制不同图形。

#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> #include <linux/fb.h> #include <unistd.h> #include <pthread.h> // 屏幕绘制线程函数 void* draw_screen(void *arg) { int fb_num = *(int*)arg; char dev_path[32]; sprintf(dev_path, "/dev/fb%d", fb_num); int fd = open(dev_path, O_RDWR); if (fd == -1) { perror("无法打开 framebuffer"); return NULL; } struct fb_var_screeninfo vinfo; struct fb_fix_screeninfo finfo; ioctl(fd, FBIOGET_VSCREENINFO, &vinfo); ioctl(fd, FBIOGET_FSCREENINFO, &finfo); long screensize = vinfo.xres * vinfo.yres * (vinfo.bits_per_pixel / 8); void *fbp = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (fbp == MAP_FAILED) { perror("mmap失败"); close(fd); return NULL; } // 根据fb编号决定画什么 if (fb_num == 0) { draw_red_box(fbp, &vinfo, &finfo); // 主屏画红框 } else { draw_green_circle(fbp, &vinfo, &finfo); // 副屏画绿圈 } munmap(fbp, screensize); close(fd); return NULL; }

上面只是框架,关键在于draw_red_boxdraw_green_circle的实现。我们来看其中一个细节——怎么在一个32位ARGB缓冲中画矩形?

void draw_red_box(void *fbp, struct fb_var_screeninfo *vinfo, struct fb_fix_screeninfo *finfo) { int x, y; for (y = 100; y < 300; y++) { for (x = 100; x < 400; x++) { long location = (x + vinfo->xoffset) * (vinfo->bits_per_pixel / 8) + (y + vinfo->yoffset) * finfo->line_length; // 假设格式为XRGB8888:[B][G][R][X] *((uint32_t*)(fbp + location)) = 0x00FF0000; // 红色 } } }

注意这里的location计算方式:
-line_length是每行字节数(可能大于 xres × bpp,因为有对齐)
-xoffset/yoffset是虚拟屏幕偏移(用于滚动或双缓冲)

只要你搞清了像素布局,就能精准操控每一个点。


工程要点:这些坑我替你踩过了

1. 显存不够?系统直接卡死!

RK3568的显存是预留给GPU和显示系统的,必须在设备树里提前预留。否则双屏一开,OOM(内存溢出)直接宕机。

dts文件中添加:

reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; gpu_memory: framebuffer@0 { compatible = "shared-dma-pool"; reusable; reg = <0x0 0x70000000 0x0 0x8000000>; // 128MB @ 0x70000000 label = "gpu_mem"; }; };

并通过bootargs告诉内核使用这段内存:

rockchip_iommu_dma=1 earlycon console=ttyFIQ0 root=/dev/mmcblk0p5 rw rootwait video=HDMI-A-1:1920x1080@60 video=DSI-1:1024x600@60 mem=3G coherent_pool=1M dma-contiguous-size=128M

建议至少预留64~128MB给显存,特别是跑高清视频时。


2. 图像撕裂怎么办?加个垂直同步!

如果你发现动画闪烁、画面撕裂,那是因为CPU写显存的速度赶不上显示器扫描速度。

解决办法:启用页翻转(page flipping) + vsync同步

虽然标准framebuffer API不直接支持,但我们可以通过DRM ioctl间接操作。不过更实用的做法是:

使用双缓冲机制,在内存中维护两个缓冲区,只在vblank期间交换。

简单模拟如下:

// 分配双倍显存空间(需驱动支持) vinfo.yres_virtual = vinfo.yres * 2; // 更新完成后,通过ioctl切换当前显示缓冲 vinfo.yoffset = next_buffer ? vinfo.yres : 0; ioctl(fd, FBIOPUT_VSCREENINFO, &vinfo);

这样就能避免边刷边读造成的撕裂现象。


3. 权限问题:普通用户打不开/dev/fbX

默认只有root才能访问帧缓冲设备。生产环境中不可能让用户都sudo运行程序。

解决方案:添加udev规则。

创建文件/etc/udev/rules.d/99-fb-permissions.rules

SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", GROUP="video", MODE="0660"

然后把应用用户加入video组:

groupadd video usermod -aG video your_app_user

重启udev服务后,非root也能安全访问fb设备。


性能优化:别让CPU累趴下

纯软件绘图很灵活,但全靠CPU处理像素,效率不高。尤其当你想在副屏播放视频时,很容易成为瓶颈。

几个实用优化技巧:

方法效果
局部刷新只更新变化区域,减少memcpy量
DMA-BUF共享与GPU/VPU共享缓冲区,避免拷贝
使用PRIMEDRM跨设备传输buffer零拷贝
结合SDL/DirectFB利用已有库做加速渲染

例如,你想在副屏播放摄像头画面,完全可以:
1. VPU解码视频到DMA-BUF
2. 将buffer映射到用户空间
3. 直接复制到/dev/fb1的显存区域

全程无需CPU参与像素转换,效率极高。


工业场景实录:某智能售货机的设计思路

我们曾为一款国产智能售货机做过类似方案:

  • 主屏(HDMI,1080P):显示商品列表 + 支付二维码
  • 副屏(LVDS,7寸触摸屏):播放广告视频 + 触控引导

最终选择:
- 主屏用 framebuffer + 自绘UI(极简,响应快)
- 副屏用 GStreamer 解码视频,输出到/dev/fb1
- 关键状态通过共享内存同步,避免进程通信延迟

整机从上电到显示第一帧不到3秒,连续运行半年无异常。客户评价:“比安卓方案稳太多了。”


结语:简单,才是最高级的复杂

尽管Wayland、Zink、Vulkan等新技术层出不穷,但在许多真实世界的应用中,我们并不需要那么“先进”

RK3568上的双屏异显,用 framebuffer 几百行代码就能搞定,稳定、高效、可控。它不像高级图形栈那样华丽,却像一把瑞士军刀——小巧、结实、关键时刻总能派上用场。

掌握 framebuffer,不只是学会一种编程接口,更是建立起对显示系统本质的理解

屏幕不过是内存的一面镜子,你写什么,它就显示什么。

下次当你面对一个多屏需求时,不妨先问问自己:
真的需要整个图形生态吗?还是,一块内存就够了?

如果你正在做RK3568项目,欢迎留言交流具体配置问题。我可以分享一份经过验证的设备树片段和启动参数模板。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/5 12:35:13

联邦学习实战:基于TensorFlow Privacy的隐私保护

联邦学习实战&#xff1a;基于TensorFlow Privacy的隐私保护 在医疗、金融和智能终端等高敏感领域&#xff0c;AI模型的价值往往依赖于海量用户数据。然而&#xff0c;将这些数据集中存储和训练的传统做法&#xff0c;正面临越来越严峻的法律与伦理挑战——从欧盟GDPR到中国《个…

作者头像 李华
网站建设 2026/6/10 0:15:46

终极npx使用指南:快速执行npm包命令的完整教程

终极npx使用指南&#xff1a;快速执行npm包命令的完整教程 【免费下载链接】npx execute npm package binaries (moved) 项目地址: https://gitcode.com/gh_mirrors/np/npx npx是一个强大的Node.js包执行工具&#xff0c;它能够让你无需全局安装即可直接运行npm包中的命…

作者头像 李华
网站建设 2026/6/5 8:33:57

Vortex模组管理器深度使用指南:从新手到专家的5大进阶技巧

Vortex模组管理器深度使用指南&#xff1a;从新手到专家的5大进阶技巧 【免费下载链接】Vortex Vortex: Nexus-Mods开发的游戏模组管理器&#xff0c;用于简化模组的安装和管理过程。 项目地址: https://gitcode.com/gh_mirrors/vor/Vortex 还在为游戏模组管理而烦恼吗&…

作者头像 李华
网站建设 2026/5/22 21:17:21

揭秘Open-AutoGLM写材料黑科技:如何3步打造专业级文档?

第一章&#xff1a;Open-AutoGLM写材料的技术背景与核心价值在人工智能快速演进的背景下&#xff0c;大语言模型&#xff08;LLM&#xff09;已从通用文本生成逐步迈向垂直场景深度应用。Open-AutoGLM作为面向自动化材料撰写的新一代开源框架&#xff0c;融合了提示工程、检索增…

作者头像 李华
网站建设 2026/6/5 4:59:09

如何计算数据立方体中聚合单元的个数?

课件例题这个题如此抽象&#xff0c;先看一下基本概念&#xff1a;有两种情况&#xff1a;count>1,count>2理解一下&#xff0c;这里count&#xff1e;&#xff1d;1时&#xff0c;相当于把每个基础单元格看作集合&#xff0c;然后求出这个集合的所有真子集&#xff0c;然…

作者头像 李华
网站建设 2026/6/6 1:22:04

在线笔记分享平台的设计与实现任务书

毕业设计&#xff08;论文&#xff09;课题论证书 学院&#xff1a;化工过程自动化学院 申报人&#xff1a; 杨中兴 职称&#xff1a;副教授 题目名称&#xff1a; 课 题 性 质 课 题 来 源 是否联系实际 是 纵向课题 横向课题 自选课题 √ 毕业设计&…

作者头像 李华