本文记录在 STM32H7S78-DK(STM32H7S7L8,Cortex-M7 @600MHz,800×480 RGB 屏)上,用 Zephyr 4.5 + LVGL 9.6 把板载三种图形硬件全部用起来,并设计了一个可运行时按键切换渲染路径的对比实验,用日志定量给出三者的差异。环境基于《STM32H7S78-DK 上跑通 Zephyr:从 Blinky 到外部 Flash 启动》。
1. 三种硬件在 Zephyr 中的使用方式
| LTDC | Zephyr 原生显示驱动,帧缓冲放 PSRAM,直渲染双缓冲 | 板级 defconfig 自动配置 |
| DMA2D(Chrom-ART) | LVGL 9.6 内置 draw unit,自带 STM32H7RS 时钟使能 | CONFIG_LV_USE_DRAW_DMA2D=y |
| NeoChrom GPU2D | LVGL NemaGFX renderer + Zephyr 官方 glue(含 Cortex-M7 预编译库) | CONFIG_LV_USE_NEMA_GFX=y |
LVGL 9 把每个渲染器注册为全局 draw unit,按链表优先级分发绘制任务(NEMA > DMA2D > SW),处理不了的原语自动回落,因此三者可以共存。
2. 运行时切换渲染器(本实验的核心技巧)
LVGL 没有公开的 draw unit 开关 API,但开启 CONFIG_LV_USE_PRIVATE_API=y 后可以拿到内部链表,按键时重建它——把对应硬件单元摘掉或挂回:
/* 重建全局 draw unit 链表:优先级 NEMA_GFX -> DMA2D -> SW */
static void apply_render_mode(enum render_mode m)
{
lv_draw_global_info_t *info = &LV_GLOBAL_DEFAULT()->draw_info;
lv_draw_unit_t *head = sw_unit;
if (m == MODE_NEMA && nema_unit != NULL && dma2d_unit != NULL) {
nema_unit->next = dma2d_unit;
dma2d_unit->next = sw_unit;
head = nema_unit;
} else if (m >= MODE_DMA2D && dma2d_unit != NULL) {
dma2d_unit->next = sw_unit;
head = dma2d_unit;
}
sw_unit->next = NULL;
info->unit_head = head;
}3. 实验设计
PSRAM 全部用起来(256Mbit Hexadeca-SPI,映射在 0x90000000):
# LVGL 堆内存池搬到 PSRAM 并加大到 2MB CONFIG_LV_Z_MEMORY_POOL_ZEPHYR_REGION=y CONFIG_LV_Z_MEMORY_POOL_ZEPHYR_REGION_NAME="PSRAM" CONFIG_LV_Z_MEM_POOL_SIZE=2097152
加上板级默认的双全屏帧缓冲(2×800×480×3 ≈ 2.3MB),PSRAM 共占用约 3.5MB。2MB 的堆让大图像缓冲成为可能:实验中从 PSRAM 分配一块 200×150 旋转画布(SW 渲染时放不下也转不动的场景)。
测量方法:LVGL sysmon 日志模式,每 300ms 输出一行官方统计,render(纯绘制)与 flush(刷屏)分开:
CONFIG_LV_USE_SYSMON=y CONFIG_LV_USE_PERF_MONITOR=y CONFIG_LV_USE_PERF_MONITOR_LOG_MODE=y CONFIG_LV_USE_LOG=y # sysmon 日志依赖 LVGL 日志总开关 CONFIG_LV_DEF_REFR_PERIOD=5 # 默认 33ms 会把 FPS 顶死在 30 CONFIG_CPU_LOAD=y # Zephyr CPU 负载统计,不开 CPU% 恒为 100
输出示例:
sysmon: 21 FPS (refr_cnt: 7 | redraw_cnt: 7), refr 46ms (render 33ms | flush 11ms), CPU 52%
4. 实测数据与曲线
压力场景:阴影渐变弹跳小球 + 旋转圆弧 + 进度条 + 缩放文字 + 半透明移动面板 + 200×150 PSRAM 画布持续旋转。

| CPU (SW) | ~48ms | ~7ms | 84~94% | 14~21 |
| DMA2D | ~39ms | ~6ms | 78~100% | 16~21 |
| NeoChrom GPU | ~38ms | ~5ms | 50~65% | 19~21 |
5. 结论
NeoChrom 是最优路径:绘制快约 20%,关键优势在 CPU 占用——从 ~90% 降到 ~55%,同样帧率下省出三分之一 CPU 给应用逻辑,这就是独显的意义。
DMA2D 绘制同样快约 20%,但 CPU 没降:Zephyr 的 LVGL 以 LV_OS_NONE 运行(应用手动调 lv_timer_handler),DMA2D 的中断异步模式在 OS_NONE 下无效(源码里有对应 #warning),传输完成靠轮询死等,硬件干活 CPU 干等。
flush 仅 ~7ms,不是瓶颈:垂直同步等待远小于想象,时间主要花在真实绘制。
GPU 差距没有拉得更开的原因:文字、圆弧、渐变等原语 NemaGFX 不接管(回落 SW),且帧缓冲在 PSRAM,带宽是共享天花板。属 LVGL 混合渲染的常态。
PSRAM 的正确用法:帧缓冲(2.3MB)+ LVGL 堆(2MB)都放 PSRAM 后,片内 SRAM 留给 NemaGFX 池(330KB)和系统,大图像/双缓冲/GPU 同时工作的基础。
6. 踩坑记录
FPS 全卡 30:LV_DEF_REFR_PERIOD 默认 33ms,任何模式都撞天花板,改成 5ms 才解开。
CPU 恒 100%:Zephyr 版 LVGL 的 CPU% 取自 cpu_load_get(),必须开 CONFIG_CPU_LOAD=y,否则 idle 恒为 0。
sysmon 日志不出:LV_USE_PERF_MONITOR_LOG_MODE 依赖 CONFIG_LV_USE_LOG=y(默认关,LV_LOG 是空操作);之前看到的 lvgl 警告来自 Zephyr glue 自己的日志,容易误导。
主循环空转:固定 5ms 调 lv_timer_handler 会推高 CPU 占用读数,应用其返回值睡眠。
*LVZ 符号不可直接赋值**:LV_Z_USE_NEMA_GFX 等无 prompt 符号由 LV_USE_NEMA_GFX 自动带出,在 prj.conf 里直接赋值会报 Kconfig 错误。
NemaGFX 内存池放不下:默认 800×600(~490KB)超过 456KB SRAM,需调小(实验用 800×400)。
我要赚赏金
