1. 课程导入

做TouchGFX开发,最怕什么?

我最怕的是:界面做完了,一烧录,黑屏。或者跑起来卡顿,点一下按钮等两秒才响应。这种问题十有八九是内存惹的祸。

我入行那会儿,第一次用TouchGFX做个温控器界面,就栽了个大跟头。界面设计得漂漂亮亮,动画流畅,结果一上真机,直接内存溢出。从那以后,我养成了一个习惯——先摸清内存家底,再动手写代码。

1.1 内存危机的根源:谁在吃我的RAM?

TouchGFX本质上是一个帧缓冲驱动的UI框架。它不像传统嵌入式GUI那样逐像素绘制,而是先把整个画面渲染到内存里,再一次性刷到屏幕。这种方式画面漂亮,但代价就是——内存消耗巨大。

我总结了一下,内存占用主要有四大“吃货”:

内存消耗源 典型占用 说明
帧缓冲(Frame Buffer) 宽度 × 高度 × 像素深度 最核心的消耗,至少占1-2个整屏
图像资源(Images) 每张图解压后的大小 尤其是全屏背景图,一张就能吃掉几百KB
字体缓存(Font Cache) 字符数 × 字形大小 中文字体尤其严重,一个汉字字形可能占几十字节
动态对象(Widgets & Containers) 每个对象几十到几百字节 屏幕上的按钮、文本框、图片框,积少成多

核心结论:TouchGFX的内存瓶颈,90%出在帧缓冲和图像资源上。别急着优化代码,先看看这两块有没有冗余。

1.2 诊断方法:如何精准定位内存问题?

光知道谁在吃内存还不够,你得有工具去“抓现行”。我个人习惯用以下三种方法,基本能覆盖90%的场景。

方法一:编译器输出分析(最基础)

编译完成后,IDE的控制台会输出内存占用信息。以IAR为例,你会看到类似这样的内容:

  968 bytes of readonly  code memory
  124 bytes of readonly  data memory
 64 212 bytes of readwrite data memory

注意看readwrite data memory,这就是你的RAM占用。如果这个值接近或超过芯片的RAM上限,那基本可以断定内存不够用了。

小技巧:我一般会在编译后看一眼这个数字。如果它超过了芯片RAM的70%,我就开始警惕了——因为运行时还有栈、堆、中断向量等开销,实际可用内存比这个数字要少。

方法二:TouchGFX Designer的Memory Report(最直观)

TouchGFX Designer自带一个内存报告功能。在Designer里点击View → Memory Report,它会列出每个屏幕、每个资源占用的内存。

我曾经在一个项目里发现,一个全屏背景图(800×480,24位色)竟然占了1.15MB。而我的芯片总共才2MB RAM,光这一张图就吃掉了一半多。这就是典型的“资源超载”。

资源类型 数量 单个大小 总占用
全屏背景图 3张 1.15 MB 3.45 MB
按钮图标 12个 32 KB 384 KB
中文字体 1套 256 KB 256 KB
帧缓冲 2个 1.15 MB 2.30 MB
总计 — — 6.39 MB

看到这个表,你应该明白为什么黑屏了吧?芯片RAM才2MB,你硬要塞6.39MB的东西进去,不崩才怪。

方法三:运行时动态监控(最精准)

编译时分析只能看到静态占用,运行时内存使用是动态变化的。我推荐用HAL::getInstance()->getMCU()来获取运行时信息。

在代码里加个调试接口:

// 在某个屏幕或定时器里调用
uint32_t freeMem = HAL::getInstance()->getMCU().getFreeMemory();
uint32_t totalMem = HAL::getInstance()->getMCU().getTotalMemory();
uint32_t usedPercent = (totalMem - freeMem) * 100 / totalMem;

// 通过串口打印
printf("Memory: %d%% used (%d/%d bytes)\r\n", 
       usedPercent, totalMem - freeMem, totalMem);

我曾经在一个项目里发现,切换屏幕时内存占用会瞬间飙升20%。原因是有个动画在加载时申请了临时缓冲区,用完没释放。这种问题,静态分析根本看不出来,必须靠运行时监控。

避坑指南:我曾经在调试时发现内存占用忽高忽低,查了半天,结果是某个图片在多个屏幕间反复加载/卸载导致的碎片化。后来我改用常量图像(ConstImage),一次性加载到固定地址,问题就解决了。

1.3 内存危机的典型症状

如果你遇到以下情况,基本可以断定是内存问题:

  • 黑屏或花屏:帧缓冲分配失败,或者被其他数据覆盖了。
  • 界面卡顿:内存不足导致频繁的页面置换,像电脑硬盘一直在转。
  • 随机崩溃:某些操作偶尔触发内存越界,表现就是“时好时坏”。
  • 动画撕裂:双缓冲切换时,其中一个缓冲区被意外修改了。

嗯,这里要注意:不要一上来就怀疑代码逻辑。我见过太多人花几天时间查bug,最后发现是内存不够。先看内存,再看代码,这是铁律。

1.4 本章知识体系

下面这张图,是我自己总结的TouchGFX内存问题诊断流程。你照着这个思路走,基本不会漏掉关键点。

TouchGFX内存问题诊断流程 TouchGFX内存问题诊断流程 出现内存问题 第一步:查看编译器内存报告 是否超过 RAM的70%? 是 立即优化资源 否 第二步:Memory Report逐项排查 第三步:运行时动态监控 定位根因,制定优化方案 先诊断,后优化 不要跳过任何一步

这张图的核心逻辑很简单:先看静态占用,再看动态行为。很多新手一上来就改代码,结果发现改了半天,问题出在图片太大。你想想看,是不是很浪费时间?

1.5 本章小结

说白了,内存危机不是洪水猛兽。你只要搞清楚三件事:

  1. 谁在吃内存——帧缓冲、图像、字体、控件,四大源头。
  2. 怎么查——编译器报告、Designer Memory Report、运行时监控,三管齐下。
  3. 什么时候该警惕——静态占用超过RAM的70%,就要开始动手了。

我个人习惯在项目启动的第一天,就把这些诊断工具搭好。别等到界面做完了再回头查内存,那时候改起来成本就高了。嗯,这一章的内容就到这里。记住:诊断是优化的前提,别跳过这一步。