2. 嵌入式GUI基础
从 TouchGFX 迁移到 LVGL,表面上是 API 替换,实质上是两套 GUI 框架在架构假设上的重新对齐。TouchGFX 面向 STM32 + Chrom-ART/DMA2D 的紧耦合设计,把渲染、资源、事件都围绕"硬件加速优先"来组织;LVGL 则是跨平台、可裁剪、以软件渲染为基线、按需接入加速器的通用框架。理解这一层差异,才能判断哪些代码可以平移、哪些必须重写。
一、图形库架构:从"框架托管"到"内核 + 驱动分层"
TouchGFX 的架构是"应用层—Presenter/View—框架内核—HAL/驱动"的垂直整合,控件、动画、资源、渲染器由框架统一调度,开发者主要通过 Model-Presenter-View 模式接入。LVGL 则是典型的"内核 + 对象树 + 显示/输入驱动"分层结构:lv_obj 构成父子对象树,样式与事件挂在对象上,lv_disp 与 lv_indev 分别抽象显示与输入设备,渲染由内核统一驱动。
这意味着迁移时,TouchGFX 中"框架帮你做的事"在 LVGL 中往往需要显式配置:屏幕刷新周期、缓冲区大小、颜色格式、输入设备读取回调,都要在初始化阶段注册。
二、渲染管线:脏矩形、缓冲区与刷新路径
两套框架都采用"脏矩形 + 局部刷新"策略,但缓冲策略差异明显。TouchGFX 常用双缓冲(Double Buffer)或部分帧缓冲,配合 DMA2D 做块传输与混合;LVGL 提供三种典型模式:
- 单缓冲:
draw_buf一块,渲染完直接 flush,内存最省但可能撕裂。 - 双缓冲:两块交替,渲染与传输可并行,推荐用于有 DMA 的平台。
- 全屏缓冲:整屏一块,适合有外部 SDRAM 或 RGB 屏的场景。
LVGL 的刷新链路为:对象标记脏区 → 内核合并脏矩形 → 渲染到 draw_buf → 调用 flush_cb → 驱动搬运到显存。迁移时最关键的是把 TouchGFX 的 DMA2D 加速逻辑,改写为 LVGL 的 flush_cb 或 GPU/DMA2D 绘制回调。
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[LV_HOR_RES_MAX * 40];
static lv_color_t buf2[LV_HOR_RES_MAX * 40];
lv_disp_draw_buf_init(&draw_buf, buf1, buf2,
LV_HOR_RES_MAX * 40);
static void disp_flush(lv_disp_drv_t *drv,
const lv_area_t *area,
lv_color_t *px_map)
{
/* 此处可接入 DMA2D 或 LTDC 局部刷新 */
LCD_CopyBuffer(area->x1, area->y1, area->x2, area->y2, px_map);
lv_disp_flush_ready(drv);
}
三、事件模型:回调注册 vs 消息驱动
TouchGFX 通过 handleTickEvent、handleClickEvent 等虚函数在 View 中响应,事件由框架分发到当前活动 Screen。LVGL 采用"对象 + 事件回调"模型:每个 lv_obj 可注册 LV_EVENT_CLICKED、LV_EVENT_VALUE_CHANGED、LV_EVENT_PRESSED 等回调,事件沿对象树冒泡,也可用 lv_event_get_target 获取触发者。
迁移要点:TouchGFX 的 Presenter 逻辑通常要拆成"控件回调 + 业务状态更新"两部分;原来在 handleTickEvent 中做的周期任务,应改为 lv_timer_create 定时器,避免阻塞 lv_timer_handler。
四、资源管理与内存约束
TouchGFX 使用 TouchGFX Designer 生成资源,图片常以压缩格式存入 Flash,运行时按需解码。LVGL 则通过 lv_img_dsc_t 描述图片,支持内置 C 数组、文件系统、外部 Flash 等多种来源,并可配置 LV_IMG_CACHE_DEF_SIZE 做解码缓存。
嵌入式场景下内存是最硬的约束。下表对比两套框架在关键资源维度上的差异:
| 维度 | TouchGFX | LVGL | 迁移注意 |
|---|---|---|---|
| 帧缓冲 | 双缓冲 / 部分缓冲 | 单/双/全屏可选 | 按 RAM 重新计算 buf 行数 |
| 图片资源 | Designer 生成,压缩存储 | C 数组 / 文件系统 / 外部 Flash | 需重做资源转换流程 |
| 字体 | 位图字体,按需生成 | FreeType 或内置位图字体 | 注意字符集裁剪 |
| 动态内存 | 框架内部管理 | LV_MEM_SIZE 静态池 |
对象多时需调大内存池 |
| 硬件加速 | DMA2D 深度集成 | 通过 flush_cb / draw_ctx 接入 | 需自行实现加速回调 |
LV_MEM_SIZE,对象树一复杂就出现分配失败。
五、典型迁移场景复盘
以 STM32F429 + 480×272 RGB 屏为例:TouchGFX 原方案使用 2 块全屏缓冲(约 520KB),依赖 DMA2D 做混合。迁移到 LVGL 后,改为 2 块 40 行缓冲(约 76KB),flush_cb 中调用 DMA2D 做块拷贝,lv_timer_handler 放在主循环每 5ms 调用一次。结果 RAM 占用下降约 85%,刷新率仍保持 30fps 以上。
- 确认
LV_COLOR_DEPTH与屏幕像素格式一致(RGB565 / ARGB8888)。 - 确认
draw_buf大小与 DMA 传输粒度对齐。 - 把 TouchGFX 的 tick 逻辑迁移为
lv_timer。 - 重新生成图片与字体资源,优先使用索引色或压缩格式。
- 评估是否需要接入 DMA2D / GPU 加速,避免纯软件渲染成为瓶颈。