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 与 LVGL 架构对照 TouchGFX(垂直整合) Application / MVP 层 TouchGFX Framework 内核 Renderer / Canvas / 动画调度 HAL:DMA2D / LTDC / 触摸 STM32 硬件平台 LVGL(内核 + 驱动分层) 用户应用 / 自定义控件 lv_obj 对象树 + 样式 + 事件 lv_disp / lv_indev 抽象层 flush_cb / read_cb 驱动回调 任意 MCU / MPU / 模拟器 迁移

二、渲染管线:脏矩形、缓冲区与刷新路径

两套框架都采用"脏矩形 + 局部刷新"策略,但缓冲策略差异明显。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 接入 需自行实现加速回调
常见误区:直接把 TouchGFX 的整屏双缓冲配置照搬到 LVGL,结果 RAM 溢出。LVGL 的部分缓冲(如 40 行)配合 DMA 局部刷新,往往能在小 RAM 上获得更平滑的体验。另一个误区是忽略 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 加速,避免纯软件渲染成为瓶颈。