前阵子有同行问我:刚拿到 STM32H7S78-DK,板子出厂跑的那个 TouchGFX 演示效果确实惊艳,触摸滑动、转盘动画、实时波形全都非常流畅。但 ST 官网的板卡页面里只给了固件下载和入门文档,想要拿这份出厂 TouchGFX Demo 的源码却翻遍页面也找不到入口。这个问题看似简单,实际上第一次接触这块板子的人几乎都会卡在这里。我把当时的排查过程、拿源码的三条官方路径、以及本地重建工程时踩过的坑完整梳理了一遍,希望对后面玩这块板子的人有点帮助。
1. 为什么出厂TouchGFX Demo的源码不跟固件放在一起:先理解“出厂固件”和“示例工程”的关系
1.1 STM32H7S78-DK 出厂演示跑的是什么
STM32H7S78-DK 是 ST 面向图形显示应用推出的评估套件,核心芯片是 STM32H7S7 系列——Cortex-M7 内核,主频可以跑到 600 MHz,片上集成了 Neochrom 图形加速器、Chrom-ART DMA2D 控制器,还带 MIPI DSI 显示控制器。板载一块 4.3 英寸左右的高清触摸屏,外面再接了 64MB 外部 SDRAM 做显存和帧缓冲,整个硬件就是为 TouchGFX 这类 GUI 框架准备的。
出厂 Demo 展示的并非单纯一张静态图片,而是一整套交互式界面:转盘滚动的图片列表、带阴影和半透明效果的卡片、动态曲线刷新、文字动画等。这套演示最主要的目的,是让客户第一眼就能感受到基于 Neochrom GPU 和 TouchGFX 渲染引擎在 H7S7 系列上的性能上限。也正因为它跑起来非常流畅,很多人才会想拿到源码看看官方到底做了哪些优化。
实际上这块板子出厂时 Flash 里烧录的是经过编译优化后的二进制固件。我们在板卡上看到的画面,就是这个固件跑起来的效果。原厂烧录环节只会关心“固件能不能正常启动、触摸灵不灵敏”,而不会把一堆源码工程一起烧进去。源码作为“开发支持材料”,走的是另外一条分发路线。
1.2 固件、软件包、示例工程:同一份 Demo 的三种形态
很多嵌入式老手刚到 ST 的生态里也会被绕晕,因为同一个演示程序在 ST 官方体系里至少有三种形态:
- 出厂固件(Pre-programmed firmware):就是板卡上电后直接运行的二进制文件。它的存在是为了让你“先看到效果”,通常不提供源码,或者源码被拆到别的包里。
- 软件包(STM32Cube MCU Package):针对某个 MCU 系列发布的完整固件库集合,里面包含 HAL 驱动、BSP 板级驱动、中间件以及大量示例工程。官方 Demo 源码通常以“Example / Demonstration”工程的形式放在这个包里。
- TouchGFX 工程示例(Example/Board Farm):TouchGFX Designer 这个 GUI 设计工具里内置了大量示例工程,可以直接按板卡型号筛选。很多板的出厂演示就是从这些示例工程衍生出来的。
明白这三种形态之后,问题就变成了:STM32H7S78-DK 的出厂 TouchGFX Demo 源码到底隐藏在哪个包里。我当初正是把这三个方向挨个试过,才把完整的工程挖出来。
1.3 为什么 ST 不直接把源码挂在板卡下载页
这或许是很多人的第一反应:板卡页面下直接放一个“Factory Demo Source Code.zip”不好吗?但仔细想一下,这种分发的复杂度比表面看起来高得多。
TouchGFX 工程不是一套孤立代码就能跑起来的。它依赖的 HAL 驱动、BSP 代码、以及由 TouchGFX Designer 自动生成的 GUI 框架,都需要和具体的 MCU 固件库版本、TouchGFX 工具链版本严格匹配。如果直接放一个压缩包,里面用了 A 版本的 HAL,而用户本地装的是 B 版本的 CubeMX,打开工程时就会冒出一堆莫名其妙的警告。把源码放进统一的 Cube 固件包和 TouchGFX 工具链里,通过版本管理来保证一致性,反而更符合真实开发流程。
另外,TouchGFX Designer 生成的代码本身是动态的。你在 Designer 里改一个颜色、改一个坐标,重新点一次 Generate Code,生成的 .cpp/.hpp 就会变。官方如果直接发布固定源码,用户反而失去了“改完再生成”的灵活性。最佳做法是给你一个带 GUI 配置文件的工程骨架,而不是一份一次性快照。
2. 找源码的官方路径:从 Cube 固件包到 TouchGFX Designer 的 Board Farm
2.1 路径一:STM32CubeH7 固件包中的 Demonstrations 目录
我最推荐的第一条路径,是去下载 STM32CubeH7 固件包。这个包可以从 ST 官网的 STM32CubeH7 软件页面获取,下载体积不小,但内容非常全。解压之后找到:
STM32Cube_FW_H7_Vx.y.z/ └── Projects/ └── STM32H7S78-DK/ ├── Applications/ └── Demonstrations/Demonstrations目录下通常就是官方出厂演示的完整工程,里面包含了 .ioc 工程配置、TouchGFX 相关源文件、BSP 驱动、启动文件、链接脚本等。找到工程后,用 STM32CubeIDE 打开即可。如果你的目的只是想要这份代码回去完整编译一次,这条路最直接。
需要注意一点,STM32H7S7 系列对固件包版本有要求。并不是任意一个 STM32CubeH7 老版本都能看到 STM32H7S78-DK 的文件夹。我自己的经验是直接下载当时的最新版本,再对照 Release Notes 确认是否已经包含该板卡的支持。如果版本太老,Projects目录下根本没有这个板子的名字,白折腾。
2.2 路径二:TouchGFX Designer 的 Example 列表和 Board Farm
第二条路是在 TouchGFX Designer 里直接生成。安装 TouchGFX Designer 后,新建工程时有一个 Example 列表,里面按 STM32 系列分类,可以筛选出 STM32H7S78-DK 相关的演示工程。有些出厂 Demo 还会以“Board Farm”的方式提供,连接板子之后可以一键生成。
用这种方式拿源码的好处是,你会拿到一份与本地安装的 TouchGFX Designer 版本严格匹配的工程,避开了“代码是别人用老版本生成”的兼容性问题。TouchGFX Designer 会按照你本机的工具链配置自动调整生成代码。
有一点要提醒:并非所有出厂 Demo 都会出现在 Example 列表里。比如说,有些针对特定面板做过的显示初始化代码,或依赖特定硬件版本的演示,未必适合直接给所有用户生成。这时候回到路径一,去 Cube 固件包里找,往往更靠谱。
2.3 路径三:GitHub 上的官方/社区仓库
ST 近年把不少示例代码都放到了 GitHub,搜索STM32H7S78-DK或者stm32h7s78-touchgfx可以看到官方仓库以及一些第三方维护的镜像。从社区仓库拿代码,好处是能看到历史上对工程文件的修改记录,某些 bug 修复过程会比官方固件包更透明。
不过从 GitHub 拉工程要特别留意一点:仓库里的代码往往不止对应一个工具链版本。你可能看到一个工程目录结构不错,但拉下来之后发现它依赖的 HAL 版本和你本地 CubeMX 自动生成的完全不同,打开 .ioc 时会提示缺少组件或版本不兼容。我的经验是,在 GitHub 上找源码最好先看 README 或最近的 commit 信息,确认它针对哪个固件包版本。否则后面编译报错时,你根本分不清是代码问题还是版本问题。
| 获取方式 | 适合场景 | 主要风险 |
|---|---|---|
| STM32CubeH7 固件包 | 想要完整、稳定、可对比的工程 | 包体积大,版本过老可能没有板卡支持 |
| TouchGFX Designer 生成 | 想直接拿到和本机工具链匹配的工程 | 个别出厂演示不会出现在 Example 列表 |
| GitHub 搜索 | 想看历史修改、找社区补丁 | 版本匹配风险高,需要仔细核对 |
3. 在本地重建工程:从 .ioc 到 TouchGFX Designer 再到 STM32CubeIDE
3.1 先看清工程目录里谁管外设、谁管界面
拿到源码后,如果直接用 STM32CubeIDE 打开整个工程目录,很可能会被一堆文件夹搞晕。TouchGFX 工程的目录结构看起来比普通 STM32 工程多了一层,它的核心分工是这样的:
.ioc文件:整个工程的硬件配置源头。打开它可以看到 STM32H7S78-DK 上所有引脚复用、时钟树、外设参数。CubeMX 就是靠它生成初始化代码。Core/目录:由 STM32CubeMX 生成的处理器初始化代码,里面包含main.c、stm32h7xx_it.c等。TouchGFX/目录:由 TouchGFX Designer 生成和管理的 GUI 相关代码,里面又分为generated(自动生成,不要手改)和gui(存放 Model、View、Presenter 等框架代码)以及target(平台相关移植代码)。Drivers/和Middlewares/:HAL 驱动库和 TouchGFX 运行时库。
理解这个结构非常关键。很多新手遇到编译错误后第一反应是去改generated目录里的代码,结果下次生成时改动全部被覆盖。正确的做法是先搞清楚报错落在哪个域里——是外设初始化的问题,就去管 .ioc 和 Core;是界面显示逻辑的问题,就去管 TouchGFX/gui;是平台适配的问题,才去看 TouchGFX/target。
3.2 用 CubeMX 打开 .ioc 并校准工具链
拿到官方工程后,我习惯先双击 .ioc 文件,让 STM32CubeIDE 里的 CubeMX 把它打开。打开后先不要急着点 GENERATE,检查几个关键配置:
- 工具链是否已经选为 STM32CubeIDE。如果 .ioc 是从别的工具链导出的,这里可能出现空配置。
- 项目名称和路径是否正确。官方工程默认路径是解压后的目录,目录名如果带空格,趁早改掉。
- 是否有组件版本不满足提醒。常见的是 X-CUBE-TOUCHGFX 组件版本和当前工程所需版本不一致。
CubeMX 的“Software Packs”管理面板里可以安装或者切换 X-CUBE-TOUCHGFX 的版本。这个组件就是 CubeMX 和 TouchGFX Designer 之间的桥梁。如果你打开 .ioc 时看到中间件区域有触发的“组件缺失”提示,说明本机没有安装对应版本的 TouchGFX 软件包,需要先在 CubeMX 的软件包管理器里补上。
确认这些之后,再点 GENERATE。CubeMX 会按 .ioc 配置重新生成 Core、Drivers 等部分。这一步会刷新掉之前由 CubeMX 生成的代码,但不会动 TouchGFX 目录里的 GUI 内容。所以如果你的目标是阅读官方 Demo,而不是重新配置硬件,直接生成即可。
3.3 交给 TouchGFX Designer 生成 GUI 代码
CubeMX 生成完成后,工程里已经有一套可用于编译的骨架了,但真正的画面资源、字体资源、图片资源以及触摸交互逻辑,还需要 TouchGFX Designer 来生成。
在工程目录里找到TouchGFX/下后缀为.part的文件,或者直接用 TouchGFX Designer 打开工程根目录下的.touchgfx工程文件。打开后你能看到官方 Demo 的完整界面结构:每个 Screen 对应的 View、每个控件的位置和属性,以及图片/字体资源列表。
此时只需要点一下右上角的 Generate Code,TouchGFX Designer 就会按照当前配置重新生成TouchGFX/generated目录下的代码。如果你对 Demo 样式不满意,也可以在这里先改一点东西再生成,比如把转盘的图片换成自己的素材,或者修改启动页的文案——这正是使用 TouchGFX 的核心工作流。
需要留意的是,TouchGFX Designer 的版本会影响生成代码的 API。官方 Demo 如果在设计时用了某个版本的新特性,而你的 Designer 是旧版,打开 .touchgfx 工程时可能直接提示不兼容,甚至打不开。这时候最直接的办法是升级 TouchGFX Designer,而不是硬去改工程配置文件。
3.4 回到 CubeIDE 编译烧录:首次点亮的几个关键点
当 CubeMX 和 TouchGFX Designer 两边都完成代码生成后,回到 STM32CubeIDE,刷新工程,执行一次完整构建。这里有几个第一次编译时容易出问题的点:
- 浮点 ABI 选项:TouchGFX 通常启用 VFPv5 或 VFPv4 硬件浮点,工程里的 FPU 选项如果被 CubeMX 重置成了软件浮点,编译会报一堆寄存器相关错误。
- 优化选项:官方 Demo 为了展示流畅度,一般使用
-O2或更高优化。如果把优化级别调成-O0,即使能编译通过,触摸和动画的体验也会明显变差。 - 错误告警等级:某些 STM32CubeIDE 版本默认把警告当错误处理,导致官方工程因为一两个 deprecated 接口直接编译失败。项目属性里可以单独调整。
烧录方面,STM32H7S78-DK 板载 ST-LINK,USB 线连上后,CubeIDE 里可以直接识别。按下 RUN,稍等一会儿,屏幕亮起,TouchGFX 出厂画面出现,整个重建流程就算闭环了。
4. 重建过程中最容易踩的坑:版本、路径与工具链的连锁反应
4.1 TouchGFX Designer 版本不够,板子直接消失
第一个坑出现在最开始。我一开始用的 TouchGFX Designer 是几年前的老版本,打开 Example 列表后翻遍了 H7 系列都没看到 STM32H7S78-DK 这个名字。当时第一反应是“这块板子太新了,老的 Designer 不支持”,升级后问题立刻消失。
所以如果你在 Example 列表里找不到对应板卡,先不要怀疑官方资料缺失,大概率是工具版本太旧。ST 的 TouchGFX 工具链更新速度很快,越新的 MCU 越依赖新版 Designer 中的 Board 支持。类似的问题也体现在 CubeMX 的软件包安装上,安装完新版 Designer 后,记得重新启动,让组件注册表刷新一次。
4.2 STM32CubeH7 固件包版本和 HAL 层不匹配
第二个坑更隐蔽。我在一个较旧的 STM32CubeH7 固件包中找到了 STM32H7S78-DK 的 Demonstrations 目录,但打开 .ioc 编译的时候,HAL 库报出一堆类似未定义宏、缺少函数原型的错误。核对之后发现,工程里的 HAL 驱动目录被固件包覆盖成了旧版本,而这个 Demo 工程依赖的新 HAL 接口在旧包里根本不存在。
这说明 STM32CubeH7 固件包本身也在持续迭代,板卡目录出现并不等于该版本的所有外设支持都是完整的。特别是 STM32H7S7 这种较新的系列,某些图形相关外设(如 MIPI DSI 控制器、Neochrom GPU 内部寄存器封装)的 HAL 更新比较频繁。解决方法是去 ST 官网下载最新版固件包,或者在 CubeMX 的软件包管理器里直接更新到最新版本,确保 .ioc 引用的 HAL 和实际编译用的 HAL 是同一套。
4.3 工程路径里的中文和空格,比想象中坑
第三个坑听起来像是老生常谈,但在 TouchGFX 工程里它会被放大。TouchGFX 生成的资源文件包含大量绝对路径写入的元信息,如果工程放在带中文或者空格的目录下,生成环节可能不报错,但编译时的链接过程会冒出奇怪的 “file not found”,而且错误位置指向的源文件看起来完全正常。
我当时的工程路径是D:\stm32\factory demo\,Creator 和 Designer 都工作正常,但编译时链接器就是找不到一个字体资源文件。花了不少时间把路径改成纯英文的D:\stm32\factory_demo\后一切恢复。这种问题排查起来非常恼火,所以拿到官方源码的第一步,先把路径规范化。
4.4 编译通过但屏幕灰屏:SDRAM 和显示控制器的初始化顺序
这是我在第 3 章提到“编译通过”之后还会遇到的另一类麻烦:编译完全正常,烧录后串口打印也正常,但屏幕只有背光亮,画面一片灰。
经过排查,问题出在初始化顺序上。TouchGFX 的显示链路需要经过 SDRAM 初始化、MIPI DSI 面板初始化、显示控制器配置这几步,而且严格的先后顺序是外部存储先就绪,再启动显示链路。因为 TouchGFX 的帧缓冲画在 SDRAM 里,显示控制器要不断从这个地址取数据,如果 SDRAM 还没就绪就开始刷屏,屏幕上自然没有有效内容。
官方源码里这些初始化函数通常以固定顺序调用。但如果你在对工程做裁剪,比如把某个外设初始化延迟到后面,一定要保证 SDRAM 的初始化在显示控制器启动之前完成。这个坑不太好排查,因为它不会立刻报错,只会表现成“开机黑屏/灰屏”。
| 现象 | 可能原因 | 解决思路 |
|---|---|---|
| Example 列表没有板卡 | Designer/CubeMX 版本过旧 | 升级工具链并重启 |
| 编译报 HAL 缺函数 | 固件包版本和工程不匹配 | 更新 STM32CubeH7 固件包 |
| 链接阶段文件找不到 | 路径含中文/空格 | 工程复制到纯英文路径 |
| 烧录后屏幕灰屏 | SDRAM 与显示控制器初始化顺序错 | 优先初始化 SDRAM 再启显示链路 |
4.5 不要试图从板子里的固件反推源码
还有一个踩坑思路值得一提。当时我在官网找不到源码的时候,一度想过用 STM32CubeProgrammer 把 MCU Flash 里的出厂固件读出来,再反汇编去逆向工程。后来发现这个做法基本不可行,原因有两个:
一是 ST 出厂固件普遍使能了读保护(RDP Level 1),正常连接调试器读不出内容;二是即使把二进制 dump 出来,反汇编得到的代码和 TouchGFX 生成的 C++ 源码完全不是一回事,里面还有大量资源表、字体二进制数据,从中还原工程结构毫无意义。相比之下,老老实实找官方固件包里的 Demonstrations 工程,效率高得多。
5. 拿到源码后怎么读、怎么改成自己的项目
5.1 读代码的先后顺序:先搞清楚 Model、Presenter、View 的调用链
TouchGFX 工程的代码组织方式和普通嵌入式 C 工程差别挺大。刚打开源码时,很多人习惯先去看main.c,然后顺着初始化逻辑往里翻。但 TouchGFX 运行时是在main.c里被启动的,真正的业务逻辑却在TouchGFX/gui目录里。
读这份 Demo 源码,我的建议是先看TouchGFX/gui/include/gui/下面的目录结构。你会看到每个 Screen 都有三个关键类:
- Model:负责与后台逻辑通信,比如读取传感器数据、更新状态;它不直接操作界面。
- Presenter:相当于 View 和 Model 之间的中间层,键盘焦点、界面跳转逻辑都在这里。
- View:直接管理界面控件,处理触摸事件、刷新动画。
官方 Demo 里,转盘切换和卡片元素的交互逻辑基本都是 Model/Presenter/View 协作完成的。先顺着这个调用链走一遍,比一开始就抠某个控件绘图代码要有用得多。
5.2 把官方 Demo 改成自己 UI 项目的边界划分
如果你想基于这份出厂 Demo 改成自己的产品界面,强烈建议保留官方工程里Board相关代码和TouchGFX/target下的平台相关代码不变,只替换TouchGFX/gui目录下的界面逻辑。这个边界划分能省掉大量移植工作。
具体操作是:在 TouchGFX Designer 里新建一个 Screen,或者在现有 Screen 上删除不需要的控件,然后添加自己的控件和逻辑。TouchGFX Designer 负责处理界面资源和行为绑定,生成代码时会自动更新gui目录。这样,硬件驱动、时钟配置、显示接口都沿用出厂 Demo,你只需要关心自己的产品到底想展示什么。
当然,如果产品需求不是重绘一个全新 UI,而是基于官方 Demo 的样子做微调,那更简单,直接在 Designer 里改图片素材、改文案即可。出厂 Demo 里很多特效控件和渲染思路本身就能直接复用。
5.3 从 Demo 里抄性能优化手法:Neochrom GPU 的典型用法
这份出厂 DEMO 之所以流畅,并不是因为代码写得特别玄妙,而是很多关键性能点都用上了硬件加速。读源码时值得留意几个具体的优化点:
- 帧缓冲配置:Demo 使用了一块完整的 SDRAM 区域作为帧缓冲,部分区域还可能开启了双缓冲或者局部缓冲模式,避免整屏刷新带来的带宽压力。
- 图像缓存:部分频繁使用的图片资源被预先转换成了适合 GPU 读取的格式,省去了每次渲染时的像素格式转换开销。
- 硬件加速渲染:TouchGFX 在运行时会把绘制任务卸载给 Neochrom 或 DMA2D,而不是靠 CPU 逐像素搬运。在源码里可以看到对图形加速器缓存状态的判断逻辑。
这些优化策略并不是只能在 STM32H7S7 上使用,我后来在做另一个基于 H7 系列的产品界面时,也沿用了同样的思路:优先用Cache、Buffer,优先使用 GPU 加速的绘制 API,避免在onDraw回调里做大量 CPU 侧绘图。
如果只是想快速跑起来看效果,按第 2、3 章的路径拿工程编译一次就够了;但如果你想真正掌握 TouchGFX 在 H7S7 上的最佳实践,我强烈建议花一个下午把 Demo 的 GUI 框架代码逐行过一遍,尤其是 Model 层和 target 层的代码。很多细节性能调优手法都藏在里面。
最后再分享一个小经验:拿到源码之后,最好把工程整个复制一份,一份保持官方原样用于对照,一份当作自己的工作副本。这样你在里面改坏了某个配置,随时可以切回原版对比差异。我第一次重建之后就在工作副本里改了时钟配置,结果整块板子死机了,对着官方代码逐项对比才找到是哪一行动了手。这个习惯一直沿用到今天,省下过不少时间。