4. 内存类型与分配机制
在 TouchGFX 应用中,内存并不是一块“越大越好”的模糊资源,而是由多种物理介质组成的层级体系。不同介质在容量、速度、成本、可写性和访问方式上差异巨大,如果分配策略与介质特性不匹配,轻则帧率下降,重则界面卡顿、撕裂甚至无法启动。理解每种介质的“性格”,是资源与内存优化的第一步。
一、核心原理:四种介质的特性对比
TouchGFX 运行时需要同时满足“代码执行”“资源读取”“帧缓冲读写”三类需求,而没有任何一种介质能同时最优地满足全部需求。因此,合理的做法是让每种数据流向最适合它的介质。
| 介质 | 典型容量 | 访问速度 | 可写性 | TouchGFX 典型用途 |
|---|---|---|---|---|
| 内部 Flash | 512KB~2MB | 中(XIP) | 只读,需擦除 | 应用代码、常量、小字库 |
| 内部 RAM | 128KB~1MB | 最快 | 可读写 | 栈、堆、高频变量、小帧缓冲 |
| 外部 SDRAM | 8MB~64MB | 较快,有刷新 | 可读写 | 主帧缓冲、大图、动画缓存 |
| QSPI Flash | 4MB~128MB | 较慢 | 只读,需擦除 | 外部字库、大图资源、音频 |
二、方法流程:分配策略的决策顺序
面对一个具体项目,建议按以下顺序做内存分配决策:
- 先确定帧缓冲位置。帧缓冲是 TouchGFX 最大的连续内存消耗者。若内部 RAM 不足以容纳双缓冲,应优先将帧缓冲放入外部 SDRAM,并确认 LTDC/FMC 带宽满足刷新率。
- 再划分只读资源。图片、字库、音频等只读数据优先放入 QSPI Flash 或内部 Flash,通过 XIP 或按需加载方式访问,避免占用 RAM。
- 最后分配运行时内存。栈、堆、TouchGFX 内部对象、模型数据等放在内部 RAM,保证访问速度与确定性。
- 用链接脚本固化布局。通过分散加载文件或链接脚本,把不同段明确映射到不同介质,避免编译器随意放置。
关键原则:速度敏感的数据放内部 RAM,容量敏感的数据放外部 SDRAM,只读资源放 Flash,帧缓冲优先考虑 SDRAM。
三、案例场景:480×272 屏的分配方案
以 STM32F429 + 480×272 RGB565 屏幕为例,单帧缓冲约 480×272×2 = 261KB,双缓冲约 522KB。若内部 RAM 仅 256KB,显然无法容纳双缓冲,必须外扩 SDRAM。
/* 链接脚本片段:将帧缓冲与资源分段放置 */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2M
RAM (rwx): ORIGIN = 0x20000000, LENGTH = 256K
SDRAM (rwx): ORIGIN = 0xC0000000, LENGTH = 8M
QSPI (rx) : ORIGIN = 0x90000000, LENGTH = 16M
}
SECTIONS
{
.framebuffer : { *(.framebuffer) } > SDRAM
.text : { *(.text*) } > FLASH
.data : { *(.data*) } > RAM
.bss : { *(.bss*) } > RAM
.qspi_assets : { *(.qspi_assets) } > QSPI
}
对应到 TouchGFX 配置中,需要把 TouchGFX_Framebuffer 指向 SDRAM 地址,把图片资源通过 Bitmap::setCache 或外部加载器指向 QSPI 区域。这样内部 RAM 只保留栈、堆和模型数据,通常可控制在 100KB 以内。
四、误区与复盘
常见误区一:把帧缓冲放在内部 RAM。小屏低分辨率时可能可行,但一旦分辨率提升或启用双缓冲,内部 RAM 会迅速耗尽,导致栈溢出或分配失败。应尽早评估帧缓冲大小。
常见误区二:所有资源都塞进内部 Flash。内部 Flash 容量有限,且 XIP 读取大图时可能拖慢 CPU。大尺寸图片和字库应迁移到 QSPI Flash 或 SDRAM。
复盘建议:在项目早期就画出“介质—数据”映射表,明确每类数据的归属;在链接脚本中固化布局;用 TouchGFX Designer 的资源统计和 map 文件核对实际占用。这样可以在开发初期就避免后期因内存不足而大规模重构。
总结来说,内部 Flash、内部 RAM、外部 SDRAM 和 QSPI Flash 各有不可替代的位置。优化的本质不是“省内存”,而是“把正确的数据放到正确的介质上”。理解这一点,后续的帧缓冲优化、资源压缩和缓存策略才有稳固的基础。