RK3568 + LVGL + GUI Guider:嵌入式GUI开发实战指南
发布时间:2026/9/24 2:10:57 锦皓数字建站

拿到 RK3568 这块板子的那天我其实没想太多就是想把 GUI Guider 拖出来的界面真正跑在 Linux 下的显示屏幕上。折腾了两三天踩了一堆坑之后我把整个过程重新整理了一遍。写这篇文章的时候我特意把自己代回“第一次接触 LVGL 移植”的状态确保你照着走就能跑通而不是又一篇教你“抄完代码怎么编都编不过”的劝退文。这不是从零开始的泛泛而谈而是基于一系列明确选择的具体实操硬件用 RK3568图形库用 LVGL界面用 GUI Guider 9.2 画。先说结论这套方案每一步都有讲究RK3568 是瑞芯微里做 HMI 的“万金油”四核 A55 处理器加上 Mali-G52 GPU跑 LVGL 这种轻量级 UI 绰绰有余LVGL 是目前嵌入式 GUI 生态里最活跃的开源库之一渲染开销低、控件丰富、中文字体和主题都很成熟GUI Guider 9.2 又刚好是 NXP 官方持续更新的可视化拖拽工具生成了可直接编译的项目骨架。三者组合起来的性价比远高于在 STM32 上死磕或者抬出 Qt 这类重框架。我默认你已经有一块能跑 Linux 系统的 RK3568 开发板比如某宝常见的 RK3568 核心板底板或者香橙派 5 这类评估板并且知道怎么给它烧录系统、用串口或者 ADB 登录。如果这些基础操作还不太熟文中的命令也可以直接照抄只要你板子的系统没改过大概率差不了太多。1. 项目整体思路为什么是 RK3568 加 LVGL 加 GUI Guider1.1 核心需求解析这个项目的本质是在一个带屏幕的嵌入式设备上用最短时间做一个能看、能点的图形界面。对大多数做产品的团队来说硬件选型通常早于界面开发界面只是一个“能在上面跑”的目标。但选型如果太拉胯界面流畅度、内存占用、配套生态都会让你后续痛苦。先说 RK3568。它是一颗四核 Cortex-A55 的 SoC主频最高 2.0 GHz集成了 Mali-G52 GPU、VPU 编解码单元还有很完整的显示控制器。比起在 STM32 上移植 LVGLRK3568 跑 LVGL 完全是“杀鸡用牛刀”但正因为性能余量大你可以在界面里放心塞图片、动画、圆角阴影甚至同时跑几个页面切换不用担心帧率崩盘。再说 LVGL。LVGL 目前版本已经到 9.xAPI 变化比 v8 时代又大了一截。LVGL v9 的渲染架构做了重构不再绑定特定的显示驱动接口而是引入了lv_display、lv_event这一套对象模型代码结构更干净。GUI Guider 9.2 正是围绕这个新版本设计的所以你如果还拿 v8 时代的旧教程硬套会发现接口对不上。然后是 GUI Guider。这工具说白了就是“LVGL 的拖拽式界面设计器”左边拖控件右边调属性底下写事件回调最终自动生成一堆 C 代码。它最大的价值是让人把精力放在“界面长什么样”而不是“代码怎么组织”对产品原型和中期开发都非常友好。9.2 版本生成的代码已经相当工程化可以直接和我们的手动移植代码合并不需要像老版本那样改到吐。1.2 技术选型背后的考量有人可能会问RK3568 都上 Linux 了为什么不直接用 Qt这就得看使用场景了。Qt 的优势是复杂业务逻辑和桌面级交互缺点是内存占用大、交叉编译麻烦、对图形驱动的要求高而且商业授权在部分场景下不友好。LVGL 的定位是嵌入式专用资源占用低一个量级没有那么多运行时依赖非常适合 IoT、智能面板、健身器材、家用电器这类“界面不需要太复杂但要稳”的产品。也有朋友纠结“直接用官方 BSP 里现成的 buildroot能不能省事”这当然可以。但如果你要做的是自己的终端产品用 GUI Guider 生成界面代码、手动交叉编译反而更容易把控整个软件栈。这句话得展开讲一讲官方 SDK 常年把 LVGL 作为示例应用编译进 rootfs可它往往是固定版本、固定分辨率、固定触摸节点你改一个小地方就得整个镜像重编。独立工程的方式则灵活得多界面代码和业务逻辑分离后续改 UI 也不用重刷系统。还有一个很多人前期不重视、后期会后悔的点分辨率。RK3568 的显示控制器支持 LVDS、MIPI DSI、eDP、HDMI 多种输出。在做界面之前必须先想清楚你的屏幕接口和分辨率是多少。LVGL 是用户态程序它不会自己初始化显示控制器而是通过文件接口去写屏所以你的屏幕能不能点亮、用什么驱动直接决定了跑 LVGL 的方式。我建议不管最终产品用哪种屏幕前期先用带 HDMI 的显示器或者一个已知驱动正常的 MIPI 屏幕做验证别一上来就调新屏。1.3 RK3568 与 RK3566 的差别标题写的是 RK3568但很多人手上可能是 RK3566也经常有人问这俩到底啥区别。这里先花一小段讲清楚避免你照着文章结果板子不同、配置差异大到怀疑人生。项目RK3568RK3566CPU四核 Cortex-A55最高 2.0GHz四核 Cortex-A55最高 1.8GHzGPUMali-G52 2EEMali-G52 1EEPCIe支持 PCIe 3.0不支持 PCIe显示支持 4K 显示主要面向 2K 以下典型场景中高端 HMI、边缘计算、NAS平板、低端盒子、简单面板如果你的板子是 RK3566这篇文章的 LVGL 移植思路完全通用只是性能上限和显示能力略有差别。界面本身对小核数不敏感LVGL 主要是单线程渲染A55 的 IPC 足够。所以别纠结太多先跑通才是王道。2. 搭建开发环境交叉编译工具链和板端系统准备2.1 开发机上需要准备的东西这里假设你用的开发机是 Ubuntu我实测 18.04 和 20.04、22.04 都没啥问题Windows 下也可以用 WSL 或者装交叉编译工具后配合 IDE但我建议还是 Linux 省心。首先要确认目标系统架构。RK3568 是 64 位的板子上的 Linux rootfs 一般是aarch64架构所以我们需要用 aarch64 交叉编译器。如果你的开发板厂商提供了完整的 SDK 和工具链优先用厂商的因为它的 glibc 版本和板端系统是匹配的。以瑞芯微官方 SDK 为例你通常会拿到一个包含 buildroot 的环境里面已经装好了工具链。但我这里讲的是不依赖整套 SDK 的裸交叉编译方式所以需要自己装sudo apt update sudo apt install -y gcc-aarch64-linux-gnu g-aarch64-linux-gnu make cmake装完验证一下aarch64-linux-gnu-gcc --version如果输出中能看到gcc version 9.x或10.x之类说明交叉编译器可用。后面我们写 Makefile 时会用到CROSS_COMPILE ? aarch64-linux-gnu-这样的前缀。另外还需要保证板子上能方便地接收你编译好的可执行文件。最省事的方式是 ADB 或者 SSH。瑞芯微的开发板一般默认开启 ADBUSB 连上之后用adb devices能看到设备。如果你习惯用网络也可以ifconfig查到板子的 IP然后ssh rootIP登录默认密码通常是rockchip或者你自己设置的。2.2 板端系统与显示设备确认在跑 LVGL 之前必须先确认两件事系统能启动屏幕能点亮。如果你用 HDMI 连接显示器插上后进入系统命令行用如下命令可以看到 DRM 相关的输出信息dmesg | grep -i drm cat /sys/class/drm/card0-*/status cat /sys/class/drm/card0-*/modes正常情况下你会看到某个 connector 的状态是connected并且有具体分辨率模式比如1920x1080。如果你的屏幕是 MIPI DSI 或者 RGB 屏那么应该是对应的 connector 显示为 connected前提是设备树已经正确配置。LVGL 跑在 Linux 用户态不需要真正的显卡加速也能工作它把显存看作一块普通的内存区域刷屏时直接往这块内存写像素。所以我们的第一个任务就是找到一个“能写像素的显示设备节点”。RK3568 的 Linux 内核通常会提供两种路径/dev/fb0传统 framebuffer 设备DRM/KMS 设备/dev/dri/card0LVGL 官方维护的驱动代码里两种都支持。我个人建议优先使用 framebuffer 方式因为代码路径短、好排查。虽然 DRM 是最终推荐方案但在“先跑通”阶段framebuffer 的报错信息友好得多。如果ls /dev/fb0看不到任何 fb 设备也不用急去检查内核配置CONFIG_FB_ROCKCHIP。有些官方内核默认没开 framebuffer只开了 DRM。这种情况下有两个选择一是重新配置内核打开CONFIG_FB_ROCKCHIP并编译烧录二是直接用 DRM/KMS 驱动。为了减少前期折腾我强烈建议你打开 framebuffer。瑞芯微原厂内核的 framebuffer 和 DRM 是可以共存的后者不冲突。触摸方面确认一下系统里 event 设备cat /proc/bus/input/devices会看到event0、event1之类的输入设备。如果你的屏幕是触摸屏通常会出现一个名字类似Goodix、GslX680、eGalax的设备。先用evtest工具验证有没有触摸中断上报sudo evtest /dev/input/event1手指点屏幕如果终端有输出说明触摸设备是好的。这一步很重要否则你界面能显示但不能点会让人怀疑人生。2.3 获取 LVGL 及显示驱动源码接下来要把 LVGL 源码和官方驱动拉到本地。这里有一个非常容易踩坑的点GUI Guider 9.2 生成代码依赖的 LVGL 版本是 9.x如果你直接从网上下一个最新 master接口可能和 GUI Guider 生成的代码不匹配。最好的做法是下载官方 release 分支版本选择 9.2 附近的稳定版。我的建议是git clone --branch v9.2.0 --depth 1 https://github.com/lvgl/lvgl.git git clone --branch v9.2.0 --depth 1 https://github.com/lvgl/lv_drivers.git这里单独解释一下lv_drivers是什么。LVGL 本身不直接操作硬件它只管把控件画到内存里然后把更新区域交给显示驱动函数去刷。lv_drivers是 LVGL 官方仓库里面提供了常见的 Linux 显示驱动、输入设备驱动比如 framebuffer、DRM、evdev代码小而清晰我们只需要把它编译进去然后做少量配置就行。如果你在编译时发现 LVGL 源码中有lv_display.h但缺少某些定义可以检查一下仓库的版本标签。v9.2 是 GUI Guider 9.2 的原配版本别乱升也千万别用 v8 去凑。3. GUI Guider 9.2设计你的第一个界面3.1 软件安装与新建工程GUI Guider 9.2 是跨平台软件Windows、Ubuntu、macOS 都有安装包。直接去 NXP 官网下载页面找对应版本安装很简单一路下一步就可以。装完打开会出现创建工程的引导页面。新建工程时几个关键点Target 选择里面有很多 NXP 的评估板型号可能没有 RK3568。没关系选一个通用的Generic或者自带模拟器的空模板即可。GUI Guider 最终输出的是纯 C 代码不绑定具体硬件这里的选择只决定初始模板内容。屏幕分辨率这一步必须和你实际屏幕一致。比如我用的是 1024x600 的屏幕就填1024和600。分辨率不对的话后面生成的代码坐标全是错的调起来很痛苦。颜色深度LVGL 支持 1/8/16/24/32 位颜色。RK3568 的 framebuffer 常见配置是 RGB56516位或 ARGB888832位。如果你不确定先用 16 位 RGB565兼容性最好。改成 32 位也行但注意显存占用翻倍。新建完成后GUI Guider 会给你一个空白页面类似普通设计器。左侧是控件库中间是画布右侧是属性面板底部有图层。这个界面熟悉一下即可操作逻辑和 PS、Figma 有点类似。3.2 从空白页到一个能看的仪表盘现在我们来做点实际的东西。假设你的产品要做一个简单的温湿度监控面板那界面可以长这样顶部一张图片或者一个圆角矩形标题栏中间一个大数字显示温度底下两个按钮用来切换页面。在左侧控件库里依次拖入一个Arc圆弧控件用来模拟仪表盘指针一个Label标签用来显示温度数字一个Button按钮用来触发事件把这些控件摆放好后右侧属性面板可以设置位置、大小、背景颜色、圆角、字体、文字颜色。GUI Guider 支持实时预览你可以直接在当前工程里运行模拟器看看效果虽然模拟器上的观感和真机略有差别但布局基本是一致的。写了这些之后我想强调一个新手最容易漏掉的操作给每个控件改一个有意义的名字。双击控件名把screen_1_btn_1改成temp_up_btn把screen_1_label_1改成temp_label。看似不起眼但等你生成代码后搜索和回调处理会省下一大把时间。3.3 事件绑定与代码生成在 GUI Guider 里选中按钮右侧有一个“事件”标签点击添加事件下拉框里有各种各样的事件类型比如点击、按下、释放。添加之后右侧会有一个代码编辑窗口。这里可以直接写回调函数体。GUI Guider 会自动帮你把事件注册好你只需要写按钮点击之后要干什么。例如给按钮添加一个点击事件在回调里把温度标签的数字加一void temp_up_btn_event_handler(lv_event_t *e) { static int temp 25; temp; lv_label_set_text_fmt(temp_label, %d°C, temp); }注意这里temp_label变量是生成的全局变量名它在gui_guider.h或者对应的setup_scr_screen_1.c里被声明。GUI Guider 生成的多屏代码中每个控件通常是一个全局指针变量跨文件可直接访问。事件写好之后点击右上角的 “Generate Code”。这时会在你指定的工程输出目录下生成一堆文件通常包括gui_guider.hgui_guider.csetup_scr_screen_1.csetup_scr_screen_1.hevents_init.cevents_init.hcustom.ccustom.h打开gui_guider.c能看到初始化函数setup_ui()和setup_scr_screen_1()这类入口。我们的核心目标就是把setup_ui()调用到 LVGL 初始化之后然后让它进入事件循环。GUI Guider 生成的代码默认是为 “MCU 环境下自行编译” 准备的它不包含main函数也不包含 LVGL 的初始化。这些需要我们自己写这正是下一章的核心内容。3.4 中文字体与图片资源的处理很多人在这一步栽了跟头GUI Guider 里明明写了中文生成代码后烧到板子上显示出来全是方框。原因是 LVGL 默认字体只包含 ASCII 字符不含中文点阵必须额外加入中文字体并把要用到的汉字子集编译进字库。GUI Guider 的字体管理里可以点击“文本”设置选择“添加字体”然后从本机导入一个 TTF 字体文件。这里有个小细节不要整个字体全量嵌入LVGL 是按 Unicode 码位裁剪子集的。在 GUI Guider 中你可以在“字体属性”里设置要包含的字符范围比如添加0x4E00-0x9FA5范围这样基本覆盖常用汉字。同时可以勾选“自动收集用到的汉字”让它只打包你在界面里写过的那些汉字这样生成的 bin/fnt 文件体积会小很多。图片资源同理。GUI Guider 支持 PNG、JPG、SVG但最终会转成 C 数组或者 LVGL 的 bin 格式。一般建议图标和素材直接用 PNG颜色不要太复杂这样输出体积可控。如果你做的是大尺寸背景图建议先压缩到和目标分辨率完全一致不要在运行时缩放否则内存开销翻倍性能也难看。4. LVGL 移植到 RK3568从源码到可执行文件4.1 整理工程目录结构现在我们把所有文件组织成一个真正能编译的 Linux 用户态程序。我习惯的目录结构如下lvgl_rk3568_demo/ ├── lvgl/ ├── lv_drivers/ ├── gui_guider/ │ ├── gui_guider.h │ ├── gui_guider.c │ ├── setup_scr_screen_1.c │ ├── setup_scr_screen_1.h │ ├── events_init.c │ ├── events_init.h │ ├── custom.c │ ├── custom.h │ └── images/ (存放生成的图片资源) ├── src/ │ └── main.c ├── Makefile └── lv_conf.h将 GUI Guider 生成的generated目录整体拷贝到工程里改名为gui_guider。然后把下载好的lvgl和lv_drivers仓库放进来。lv_conf.h是 LVGL 的配置文件在 LVGL 仓库的lv_conf_template.h基础上修改复制一份到工程根目录即可。在lv_conf.h里有一些必须设置的关键项#define LV_COLOR_DEPTH 16 #define LV_USE_LOG 1 #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN #define LV_MEM_SIZE (64U * 1024U)LV_MEM_SIZE是 LVGL 的动态内存池大小。RK3568 有充足内存但 LVGL 的默认内存池是内部静态数组和系统内存没关系所以分配多少它就用多少。先设 64KB 够跑简单界面如果后面发现控件多到内存不足会报lv_mem_alloc失败再往上调。4.2 编写 main.c显示初始化与事件循环现在写核心的main.c。这段代码要做四件事初始化 framebuffer 显示设备、初始化触摸输入设备、初始化 LVGL 库、进入无限事件循环。完整代码如下#include stdio.h #include unistd.h #include lvgl.h #include lv_drivers/display/fbdev.h #include lv_drivers/indev/evdev.h #include gui_guider.h #define HOR_RES 1024 #define VER_RES 600 int main(void) { lv_init(); fbdev_init(); fbdev_set_resolution(HOR_RES, VER_RES); evdev_init(); // 设置触摸设备节点默认是 /dev/input/event1根据实际调试 evdev_set_file(/dev/input/event1); lv_display_t *disp lv_display_create(HOR_RES, VER_RES); lv_display_set_flush_cb(disp, fbdev_flush); lv_display_flush_ready(disp); lv_indev_t *indev lv_indev_create(); lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); lv_indev_set_read_cb(indev, evdev_read); setup_ui(guider_ui); while (1) { lv_timer_handler(); usleep(5000); } return 0; }这里解释一下关键点。fbdev_init()会打开/dev/fb0并做mmap把显存映射到用户态地址。lv_display_create创建了一个与屏幕尺寸一致的显示对象lv_display_set_flush_cb把 LVGL 的 flush 回调指向fbdev_flush——这个函数在lv_drivers/display/fbdev.c里实现负责把渲染好的像素拷贝到 framebuffer。evdev_init和evdev_read同理读取触摸事件传给 LVGL。补充一句很多旧教程里会看到lv_disp_drv_register和lv_indev_drv_register这些 API 在 LVGL 9 中已经改名了改成lv_display_create加回调函数的方式。如果你照抄老代码编译器会直接报implicit declaration of function之类所以一定要确认 API 版本。4.3 Makefile 编写与交叉编译整个工程的编译依赖比较多最简单的方式是手写一个 Makefile把 LVGL 和 LV_Drivers 的源码都编进来。CROSS_COMPILE ? aarch64-linux-gnu- CC : $(CROSS_COMPILE)gcc LVGL_DIR : lvgl LVDRV_DIR : lv_drivers CFLAGS : -O2 -Wall -I$(LVGL_DIR) -I$(LVGL_DIR)/src -I$(LVDRV_DIR) -I. CFLAGS -DLV_CONF_INCLUDE_SIMPLE CFLAGS -D_GNU_SOURCE # 收集 LVGL 源码 LVGL_SRC : $(wildcard $(LVGL_DIR)/src/*.c) LVGL_SRC $(wildcard $(LVGL_DIR)/src/**/*.c) LVGL_SRC $(wildcard $(LVGL_DIR)/src/**/**/*.c) # 收集 lv_drivers 源码 LVDRV_SRC : $(wildcard $(LVDRV_DIR)/display/*.c) LVDRV_SRC $(wildcard $(LVDRV_DIR)/indev/*.c) # GUI Guider 生成代码 GUI_SRC : $(wildcard gui_guider/*.c) SRCS : src/main.c $(LVGL_SRC) $(LVDRV_SRC) $(GUI_SRC) OBJS : $(SRCS:.c.o) TARGET : lvgl_demo all: $(TARGET) $(TARGET): $(OBJS) $(CC) -o $ $^ -lm -lpthread %.o: %.c $(CC) $(CFLAGS) -c -o $ $ clean: rm -rf $(OBJS) $(TARGET)这里有个坑LVGL v9 的源码组织比较深wildcard src/**/*.c这种写法在 Make 的默认 glob 模式下不会递归匹配所有子目录所以保险起见我写了好几层 wildcard 来覆盖多层子目录。如果你的源码树更深再补一层即可。更省心的办法是用find命令动态生成但为了新手好理解这里先用显式多层。编译一行命令make如果一切正常几分钟后会生成一个名为lvgl_demo的 aarch64 可执行文件。用file lvgl_demo查看应该能看到ELF 64-bit LSB executable, ARM aarch64字样。4.4 显存与绘制性能的初步调优跑通是一回事跑得流畅又是另一回事。LVGL 在 Linux framebuffer 上默认是全量刷新或者脏矩形刷新如果你发现界面卡顿可以调整两个地方。第一是LV_DISP_ROT和屏幕方向的配置。如果屏幕物理方向不对不要用 LVGL 做旋转最好在内核的设备树里把 DRM 的 rotation 属性配好或者调整 fb 的 pan。软件旋转会显著增加 CPU 负载。第二是脏矩形刷新。LVGL 内置的刷新机制本身只会重绘变化区域但 framebuffer 驱动层的fbdev_flush函数默认是把整个区域拷贝过去。如果你的屏幕分辨率是 1080p每帧全屏刷新一次 RGB565 数据大约是 4MBCPU 拷贝开销不小。可以把 framebuffer 的fbdev_set_resolution分辨率设小一点匹配实际屏幕或者在后期的性能优化中使用LV_USE_PERF_MONITOR来观察 FPS#define LV_USE_PERF_MONITOR 1打开这个宏之后界面上会有一个小的 CPU/帧率显示条方便量化优化效果。5. 部署运行、踩坑记录与后续扩展5.1 拷贝可执行文件到开发板编译完成后把lvgl_demo文件拷贝到 RK3568 的 Linux 系统里。最简单的办法adb push lvgl_demo /userdata/ adb shell chmod x /userdata/lvgl_demo adb shell /userdata/lvgl_demo如果你的系统用 SSH也可以scp发送。运行之后屏幕上应该会出现你刚才在 GUI Guider 里画的界面。如果运行后屏幕黑屏或者花屏不要慌最可能有问题的不是 LVGL 代码而是 framebuffer 的颜色格式不匹配。lv_conf.h里LV_COLOR_DEPTH如果是 16而 fb 设备是 32 位深就会显示花屏。先确认一下fbset -s /dev/fb0看到rgba 8/8/8/8或者rgb 5/6/5之类的信息后再去改LV_COLOR_DEPTH重新编译。一般来说常见 RGB 屏是 RGB565HDMI 显示器多数能接收 RGB565但也有些显示器强制要求 32 位具体以实际画面为准。5.2 常见问题与排查思路实录这一节我把实际操作中遇到的高频问题列成速查表按现象、原因、解决办法三列组织现象可能原因解决办法运行时报错Failed to open /dev/fb0内核没开 framebuffer检查内核配置CONFIG_FB_ROCKCHIP重新编译烧录屏幕黑屏但不报错显示驱动没刷新或 LVGL 没进入 timer 循环检查 main.c 是否在 while 里调用lv_timer_handler()花屏、颜色偏色LV_COLOR_DEPTH与 fb 格式不一致用fbset -s /dev/fb0查格式匹配修改触摸没反应evdev 设备节点不对用cat /proc/bus/input/devices查触摸设备改evdev_set_file中文显示方框字体没嵌入中文字库GUI Guider 里导入 TTF 并裁剪字符集界面卡顿CPU 渲染过重或刷新区域过大打开LV_USE_PERF_MONITOR观察缩小控件阴影/圆角使用编译时lv_conf.h找不到编译宏没指对头文件Makefile 增加-DLV_CONF_INCLUDE_SIMPLE确认 lv_conf.h 在根目录有一个问题非常隐蔽我特意单独拎出来说。如果你在 GUI Guider 里使用了动态图片或 GIF 资源生成代码里可能会引用lv_gif.h或lv_png.h这些外部库。这些不是 LVGL 核心源码需要额外编译对应的 libpng、libgif 库或者在你下载 LVGL 时把lv_lib_png等仓库一同拉进来。编译报错如果看到undefined reference to lv_png_init别怀疑 LVGL 的问题就是缺依赖库。5.3 性能优化思路从 framebuffer 到 DRM/Mali GPU 渲染前面几节都是基于 framebuffer 的软渲染方案优点是简单直接缺点是 GPU 没有真正参与渲染。RK3568 的 Mali-G52 本来可以加速部分渲染但如果你用 fbdev 作为后端GPU 加速效率会受到限制。跑通第一个界面之后如果你要把它做成真正的产品建议走 DRM/KMS 后端。LVGL 官方的lv_drivers里有一个drm驱动它通过 DRM 的 dumb buffer 创建显存然后编程填充到内存。用 DRM 的好处是可以利用 KMS 的自动刷新机制避免手动 memcpy而且在一些场景下还能配合 Mali 的 GPU 做合成。DRM 驱动的初始化比 fbdev 复杂一些代码示例大概是lv_display_t *disp lv_drm_display_create(CONNECTOR_ID, HOR_RES, VER_RES);其中CONNECTOR_ID要自己通过drmModeGetConnector去遍历。这部分代码我已经在另一个项目里写过但放进来会把这篇文章拖太长这里只点明方向先把软件渲染跑顺再研究 DRM。还有一个方向是使用 LVGL 的lv_draw_sw高性能渲染功能。LVGL 9 默认使用软件渲染器但在编译时可以开启LV_DRAW_SW_SUPPORT下的 NEON 优化。A55 支持 ARM NEON 指令把优化开关打开之后颜色填充和位块搬运性能可以提升 30% 左右代价只是编译指令集打开几乎零成本。#define LV_DRAW_SW_NEON 1记得在编译参数加-mfpuneon -mfloat-abisoftfp或者干脆用-mcpucortex-a55让编译器自动优化。实测下来简单界面 FPS 从 40 多帧能到 60 帧效果明显。5.4 从单屏到多页面与业务代码的对接GUI Guider 可以创建多个 screen然后通过事件切换屏幕。它生成的代码里有lv_scr_load_anim这类动画切换函数你可以直接调用。例如在按钮事件里void next_screen_btn_event_handler(lv_event_t *e) { lv_scr_load_anim(guider_ui.screen_2, LV_SCR_LOAD_ANIM_MOVE_LEFT, 300, 0, false); }这里的guider_ui是全局结构体GUI Guider 在gui_guider.c里已经初始化好所有屏幕指针。多屏切换的内存分配和释放要特别注意lv_scr_load_anim会自动卸载旧屏幕但如果你用到自定义的临时对象要手动删除避免内存泄漏。业务代码对接方面建议把 LVGL 事件回调当成一个薄薄的消息入口不要在回调里执行耗时操作。比如按钮触发网络请求正确做法是在回调里置一个标志位然后在while(1)循环中或者单独线程处理真正的业务逻辑。LVGL 的回调是在lv_timer_handler()上下文中执行的如果你在回调里卡 200ms整个 UI 就卡 200ms体验非常糟糕。5.5 触摸设备封装与实际项目中的输入兼容最后补一点关于触摸的小经验。不同屏厂的触摸芯片不一样有的叫Goodix有的叫GslX680有的叫Hynitron。LVGL 的evdev_read只读取标准的struct input_event结构所以理论上所有 evdev 设备都行但触摸坐标上报的方式有差异。大多数触摸屏在 evdev 里报ABS_X、ABS_Y和BTN_TOUCH这没问题。但也有舞台投影触摸框或者红外触摸框只报BTN_LEFT不报BTN_TOUCH这种情况下 LVGL 可能识别不到按压。解决方式是在evdev_read里稍微改动一小段解析逻辑把BTN_LEFT映射成LV_INDEV_STATE_PRESSED。调试触摸时可以用evtest查看原始事件如果发现坐标范围是 0~4095 而你的屏幕是 1024x600LVGL 会默认按绝对坐标直接映射这个一般是对的。如果系统里的触摸驱动校准过坐标比如上报范围已经是屏幕像素范围那就不用额外做坐标变换。写在最后的一点经验这篇文章从方案选型、环境搭建、GUI 设计、代码生成、交叉编译到最终部署把一条从零到一的路完整走了一遍。我特意没有用厂商 SDK 里的现成 LVGL 示例而是采用“源码直接编译、手动适配驱动”的方式因为这种灵活度才符合真实做产品的需求。基于我个人的实操体会如果你第一次搞一定要按顺序来先点亮屏幕再验证触摸最后才和 GUI Guider 生成的代码合并。很多人一上来就急着把整个工程编好结果发现屏幕没亮后面所有调试都是空中楼阁。还有一个小技巧分享给大家在main.c最开始加一个lv_log_register_print_cb回调把 LVGL 的日志重定向到串口或文件调试时能救大命。LVGL 在内存不足、颜色格式不匹配、资源加载失败时都有明确的错误输出只是默认固定在 stdout有时候你没注意到。LVGL 的东西远不止一篇文章能讲完但跑通第一个界面的过程是相通的。后面无论是接真实的传感器数据、做多语言、还是上 DRM 硬件加速都是从这条基线出发。祝你们一次点亮。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。