3. 工程结构解析:TouchGFX 生成的目录到底长什么样?

好,上一章我们把 TouchGFX Designer 跑通了,生成了第一个工程。你双击打开那个 .ioc 或者 .uvprojx 文件的时候,有没有一瞬间觉得「哇,这么多文件夹,我该改哪个?」

别急,我当年第一次看到这个目录结构,也愣了几秒。后来做过的项目多了,发现这个结构其实非常清晰,只是它把「框架代码」和「用户代码」分得很开。说白了,TouchGFX 帮你搭好了舞台,你只管在上面演戏就行。

这一章,我们就来拆解这个舞台。我会带着你,把 App、Gui、Target 这几个核心文件夹一个一个看明白。

3.1 顶层目录:一眼看穿骨架

打开你的工程根目录,正常情况下你会看到类似这样的结构(我用的是 STM32CubeIDE 生成的,Keil 或 IAR 也大同小异):

MyProject/
├── Core/
├── Drivers/
├── Middlewares/
├── TouchGFX/
├── .cproject
├── .project
├── *.ioc
└── Src/  (部分IDE会有)

这里面,Core 和 Drivers 是 STM32CubeMX 生成的,跟 TouchGFX 没有直接关系。真正的主角是 TouchGFX 这个文件夹。

我个人习惯,拿到一个新工程,第一件事就是打开 TouchGFX 目录。因为所有跟界面相关的代码,都藏在这里面。

核心原则:TouchGFX 生成的代码,你 不要手动修改。你改的代码,都应该放在 App 和 Target 里。这是避免升级冲突的关键。

3.2 TouchGFX 目录:三层架构

展开 TouchGFX 文件夹,你会看到三个子目录:

TouchGFX/
├── App/
├── Gui/
└── Target/

这三个文件夹,对应了 TouchGFX 框架的「三层架构」:

  • App:应用层。你写业务逻辑的地方。
  • Gui:界面层。Designer 生成的 UI 代码全在这里。
  • Target:硬件层。板级支持包,跟硬件打交道的代码。

嗯,这里要注意:Gui 文件夹里的东西,你几乎不需要碰。但 App 和 Target 是你频繁出入的地方。

3.3 App 文件夹:你的主战场

先看 App 文件夹。它里面通常只有两个文件:

TouchGFX/App/
├── app_touchgfx.c
└── app_touchgfx.h

这两个文件,是 TouchGFX 跟你的主循环(main loop)之间的桥梁。说白了,它负责启动 TouchGFX 的调度器。

打开 app_touchgfx.c,你会看到类似这样的代码:

#include "app_touchgfx.h"
#include "touchgfx.h"

void MX_TouchGFX_Init(void)
{
    // 初始化 TouchGFX 框架
    touchgfx_init();
}

void MX_TouchGFX_Process(void)
{
    // 主循环里不断调用这个函数
    touchgfx_taskEntry();
}

我在项目中遇到过一个问题:有人把 MX_TouchGFX_Process 放在了一个低优先级的任务里,结果界面卡顿。后来发现,这个函数必须在主循环或者高优先级任务里频繁调用,否则 UI 刷新会跟不上。

小技巧:如果你用 FreeRTOS,建议把 touchgfx_taskEntry() 放在一个独立任务里,优先级设高一点。我曾经因为优先级设低了,导致触摸响应延迟了 200ms,用户体验很差。

3.4 Gui 文件夹:Designer 的「自留地」

接下来是 Gui 文件夹。这个目录结构,完全由 TouchGFX Designer 生成和维护。你想想看,你在 Designer 里拖了一个按钮,它就会在这里生成对应的 C++ 代码。

典型的 Gui 结构如下:

TouchGFX/Gui/
├── include/
│   └── gui/
│       ├── common/
│       ├── screen1_screen/
│       └── screen2_screen/
└── src/
    └── gui/
        ├── common/
        ├── screen1_screen/
        └── screen2_screen/

每个屏幕(Screen)对应一个文件夹。比如 screen1_screen 里,会有:

  • Screen1View.hpp / Screen1View.cpp:视图层,负责显示。
  • Screen1Presenter.hpp / Screen1Presenter.cpp:主持人层,负责逻辑。

这是 TouchGFX 的 MVP(Model-View-Presenter)架构。View 只管画界面,Presenter 管业务逻辑。我刚开始学的时候,总觉得这两个东西分不清,后来发现一个简单的判断方法:凡是跟 UI 控件直接打交道的,放 View;凡是跟数据、通信、计算有关的,放 Presenter。

警告:千万不要手动修改 Gui 文件夹里的文件!如果你改了,下次在 Designer 里重新生成代码,你的修改会被覆盖。正确的做法是:在 App 或 Target 里写你的逻辑,然后通过回调或接口跟 Gui 交互。

3.5 Target 文件夹:跟硬件打交道的地方

最后是 Target 文件夹。这个文件夹里,放的是跟具体硬件平台相关的代码。

TouchGFX/Target/
├── BoardConfiguration.cpp
├── BoardConfiguration.hpp
├── TouchGFXGPIO.cpp
├── TouchGFXGPIO.hpp
├── TouchGFXHAL.cpp
├── TouchGFXHAL.hpp
├── STM32DMA.cpp
├── STM32DMA.hpp
├── TouchGFXDataReader.cpp
├── TouchGFXDataReader.hpp
└── ... (其他硬件相关文件)

这里的关键文件是 TouchGFXHAL.cpp。HAL 是 Hardware Abstraction Layer 的缩写,它负责把 TouchGFX 的绘制指令,转换成实际硬件能理解的信号。

举个例子,你的屏幕是 RGB 接口还是 SPI 接口?帧缓冲(Frame Buffer)放在内部 SRAM 还是外部 SDRAM?这些配置,都在 TouchGFXHAL.cpp 里设置。

我记得有一次移植到一款国产 MCU 上,屏幕一直花屏。查了两天,最后发现是 TouchGFXHAL.cpp 里的帧缓冲地址配置错了。嗯,从那以后,我每次移植都会先检查这个文件。

3.6 依赖关系:谁依赖谁?

搞清楚了三个文件夹的作用,我们来看看它们之间的依赖关系。我画了一张图,帮你理清思路:

TouchGFX 三层架构依赖关系 App 应用层 app_touchgfx.c Gui 界面层 View + Presenter Target 硬件层 HAL + 驱动 调用 调用 关键规则 1. App 调用 Gui 的 Presenter 2. Gui 调用 Target 的 HAL 3. Target 不依赖 App 和 Gui 4. Gui 不依赖 App 用户代码位置: - 业务逻辑 → App - 硬件适配 → Target - UI 布局 → Gui (自动生成) 总结:App 依赖 Gui,Gui 依赖 Target,Target 独立。用户代码集中在 App 和 Target。

从这张图可以看得很清楚:

  • App 依赖 Gui:App 里的主循环调用 Gui 的 Presenter 来驱动界面更新。
  • Gui 依赖 Target:Gui 里的 View 需要调用 Target 的 HAL 来操作屏幕硬件。
  • Target 是独立的:它不依赖 App 和 Gui,只依赖 MCU 的底层驱动。

这种分层设计的好处是:如果你想换一个屏幕,只需要修改 Target 文件夹里的代码,Gui 和 App 完全不用动。反之,如果你想改界面布局,只需要在 Designer 里改,然后重新生成 Gui 文件夹,App 和 Target 不受影响。

3.7 实战建议:移植时该动哪些文件?

好了,理论讲完了,我们来点实际的。假设你要把 TouchGFX 从一个开发板移植到自己的板子上,你应该关注哪些文件?

文件夹 需要修改的文件 修改原因
App app_touchgfx.c 调整初始化顺序、任务优先级
Gui 通常不改 除非你要手动调整布局(不推荐)
Target TouchGFXHAL.cpp 配置帧缓冲地址、屏幕接口、时钟
Target BoardConfiguration.cpp 配置 GPIO、LCD 控制引脚
Target STM32DMA.cpp 配置 DMA 传输(如果使用)

我曾经在移植到 STM32F429 时,发现屏幕刷新特别慢。查了半天,发现是 TouchGFXHAL.cpp 里没有启用 DMA2D 硬件加速。打开后,帧率从 15fps 直接飙到了 60fps。所以,移植时一定要检查硬件加速是否开启。

避坑指南:如果你用的是外部 SDRAM 作为帧缓冲,记得在 TouchGFXHAL.cpp 里把帧缓冲地址改成 SDRAM 的地址。我曾经忘了改,结果画面一直显示乱码,还以为是屏幕坏了。

3.8 小结

这一章我们拆解了 TouchGFX 的工程结构。说白了,你只需要记住三句话:

  • App 是你的业务逻辑入口,放主循环和初始化。
  • Gui 是 Designer 的领地,别去动它。
  • Target 是硬件适配层,移植时主要改这里。

搞清楚了这些,后面我们讲具体的移植步骤时,你就知道该往哪个文件夹里塞代码了。