2. TouchGFX基础回顾
在进入资源与内存优化之前,必须先建立一张清晰的“全局地图”:TouchGFX 到底由哪些部分组成、一帧画面是怎样被渲染出来的、工程里有哪些资源类型、它们分别被放在哪块内存区域。只有把这条链路看清楚,后续的裁剪、压缩、缓存与搬运策略才有依据。
一、TouchGFX 的整体架构
TouchGFX 采用典型的“分层 + 生成代码”架构,自上而下大致分为四层:
- 应用层(Application):由你在 TouchGFX Designer 中搭建的 Screen、View、Presenter 以及自定义 Widget 组成,负责界面逻辑与交互。
- 框架层(Framework):包含 Model-View-Presenter(MVP)机制、Screen 管理器、事件分发、动画与过渡(Transition)、Timer、Easing 等。
- 渲染层(Renderer):负责把 Widget 树绘制成像素,包含 Canvas Widget Renderer(CWR)、LCD24bpp、LCD16bpp 等不同渲染器,以及位图缓存(Bitmap Cache)与脏矩形(Dirty Rectangle)机制。
- 硬件抽象层(HAL / OSAL):包括 LCD 驱动、DMA2D/GPU 加速接口、触摸驱动、OS 抽象层(FreeRTOS 或裸机),由开发者按板级实现。
关键点在于:TouchGFX Designer 生成的代码与手写代码是分离的。Designer 生成 generated/ 目录下的 Screen 基类、资源描述与字体/图片索引,而你的业务逻辑写在 gui/ 目录中。理解这一点,才能判断哪些资源是“自动生成、可被优化”的,哪些是“手写、需自行管理”的。
二、一帧画面的渲染流程
TouchGFX 不是每帧全屏重绘,而是基于“脏矩形 + 位图缓存”的增量渲染。典型流程如下:
- 主循环调用
handleTickEvent(),推进动画与定时器。 - 框架收集本帧需要重绘的区域,合并为脏矩形列表。
- 对每个脏矩形,遍历覆盖它的 Widget 树,按 Z 序从底到顶绘制。
- 若 Widget 是图片或文字,优先从位图缓存中取已渲染好的位图;未命中则调用渲染器绘制并写回缓存。
- 渲染结果写入帧缓冲区(Frame Buffer),再由 LCD 驱动或 DMA2D 搬运到屏幕。
因此,位图缓存大小、脏矩形数量、是否启用 DMA2D 会直接决定 CPU 占用与帧率。缓存越大,重复绘制越少,但 RAM 消耗越高——这正是后续优化的核心矛盾之一。
三、资源类型与内存区域划分
TouchGFX 工程中的资源大致可分为四类,它们对内存的诉求完全不同:
| 资源类型 | 典型内容 | 默认存放区域 | 优化关注点 |
|---|---|---|---|
| 图片资源 | PNG/JPG 转换后的位图、Alpha 通道 | Flash(常量)→ 运行时可能解压到 RAM | 色深、压缩格式、是否常驻缓存 |
| 字体资源 | 点阵字库、字符映射表 | Flash | 字符集裁剪、字号数量、是否启用压缩 |
| 文本资源 | 多语言字符串表 | Flash | 语言数量、是否按需加载 |
| 运行时缓冲 | 帧缓冲、位图缓存、Widget 对象 | 内部 SRAM / 外部 SDRAM | 缓存大小、双缓冲策略、对象生命周期 |
需要特别注意的是:“资源放在 Flash”并不等于“不占 RAM”。很多图片在绘制时需要先解压到 RAM 中的位图缓存,字体在渲染时也要生成字形位图。因此,Flash 占用与 RAM 占用必须分开评估。
四、典型应用场景与常见误区
TouchGFX 常见于以下场景:
- 家电 HMI:屏幕小、资源少,重点在 Flash 裁剪与低 RAM 占用。
- 工业控制面板:多语言、多状态图标,重点在字体与图片的按需加载。
- 智能穿戴:分辨率高但 RAM 极小,必须依赖外部 Flash 与压缩资源。
- 汽车仪表:高帧率、多图层,重点在 DMA2D 加速与双帧缓冲。
常见误区:很多开发者只关注 Flash 占用,却忽略了位图缓存和帧缓冲对 RAM 的消耗。实际项目中,RAM 往往比 Flash 更早成为瓶颈。另一个误区是盲目增大位图缓存,导致内部 SRAM 被占满,反而引发栈溢出或分配失败。
复盘建议:在优化前,先用 TouchGFX Designer 的“资源报告”和链接脚本的
.map 文件,分别统计 Flash 与 RAM 的实际占用。把资源按“常驻 / 按需 / 可压缩”三类标记,再决定哪些放内部 SRAM、哪些放外部 SDRAM、哪些保留在 Flash 中按需解压。
// 典型链接脚本片段:把 TouchGFX 资源与缓冲分区
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
SDRAM (rwx) : ORIGIN = 0xC0000000, LENGTH = 8M
}
SECTIONS
{
.touchgfx_res : { *(.touchgfx_res*) } > FLASH
.framebuffer : { *(.framebuffer*) } > SDRAM
.bitmap_cache : { *(.bitmap_cache*) } > SDRAM
}
理解架构、渲染流程、资源类型与内存区域划分,是后续所有优化动作的起点。只有先明确“谁在什么时候、从哪块内存、取什么资源、画到哪里”,才能有针对性地做减法与搬运。