简介:野火STM32F429开发板适配 TouchGFX 的完整工程资源,融合 STM32CubeMX 初始化代码与 TouchGFX Designer 生成的界面框架,面向希望为 MCU 增加图形交互的嵌入式开发者,解决在 STM32F4 平台运行带 3D 旋转效果的 dome 示例问题,也适用于智能家电、工业控制、物联网设备的界面原型开发与教学演示。压缩包以 rar 格式封装,共 759 个文件,约 12.83MB;其中 h/hpp/c/cpp 源文件对应 TouchGFX 框架及应用逻辑,lib/a 提供预编译库,png 为 UI 素材,另有 Keil 工程配置、链接脚本、批处理脚本及字体/调试配置文件,便于直接导入编译。已有 4307 人学习/下载,特别适合有一定 STM32 基础、希望进阶图形界面开发的工程师。资源内含完整的 LCD 与触摸屏驱动适配、SDL2 仿真辅助库(如 libSDL2.a、touchgfx_core.a)以及工程清理脚本,省去繁琐的手动移植步骤;通过对照工程结构,可理解 TouchGFX 的渲染与事件处理机制,也可直接作为基础模板,快速开启自己的嵌入式图形 UI 项目,进而缩短界面功能开发周期。 手里这块野火STM32F4开发板,到底能不能跑TouchGFX?这个问题的答案,比你想的复杂一些,但动手折腾下来,结果是能跑,而且跑起来之后的效果,比自己写控件绘制的UI要强太多了。这篇文章就把我在野火指南者(STM32F407VET6)上完整适配TouchGFX的全过程记录下来,包括环境搭建、屏幕驱动接入、帧缓冲策略、中文字体、字体图标以及各种坑,希望能给正在折腾F407+TouchGFX的朋友省点时间。
先说清楚这篇文章适合谁:手里已经有野火F4开发板(指南者、挑战者都行),不想再花钱买带LTDC屏幕的开发板,但又想用TouchGFX做界面的人。如果你还没买板子,而且主要是为了做UI,那直接上带RGB屏幕和SDRAM的版本会更省事,但如果你跟我一样手上正好有这块板子,那就接着往下看。
1. 项目整体思路:为什么选择TouchGFX,以及能做什么
1.1 TouchGFX的定位和核心优势
TouchGFX是一个面向嵌入式的图形界面框架,它跟emWin、LVGL这类GUI库最大的区别在于:它提供了一个PC端的设计器(TouchGFX Designer),界面布局、文字、图片、动画都可以在Designer里拖拽完成,生成的代码直接跟STM32的HAL库对接。说白了,它的核心优势不在运行时那一层,而在“UI开发效率”上。
以前做嵌入式UI,最痛苦的就是画控件,一个按钮从按下到弹起的高光效果,纯手写SSD1306这种点阵屏还行,但到了彩色TFT屏,控件的阴影、渐变、抗锯齿字体,手写代码量就很吓人了。TouchGFX把这些全做成现成的组件,一个按钮的原生动画效果都给你配好了,你要做的是关心界面逻辑,而不是关心这个像素该填什么颜色。
当然,它也有明显代价:资源占用比裸机GUI高,对硬件有一定要求。STM32F407这个级别,512KB Flash、192KB RAM,理论上是TouchGFX支持范围内的入门配置,但前提是你会合理规划内存,尤其是在没有外部SDRAM的板子上,帧缓冲很紧张。这一点我在后面“帧缓冲策略”里会详细讲,这是能不能跑起来的关键。
1.2 野火STM32F4平台的选型差异
先说硬件的家庭背景。野火的F407开发板主要分两个系列:指南者和挑战者。指南者常见的型号是STM32F407VET6,512KB Flash、192KB RAM,板载的屏幕一般是SPI接口的2.8寸/3.2寸TFT屏,或者FSMC并口屏;挑战者则分为V2/V3等版本,有的板载SDRAM(比如W9825G6KH),这在与TouchGFX适配时差别非常大。
这里必须明确一个关键的硬件事实:STM32F407没有LTDC液晶控制器,也没有FMC的SDRAM控制器(F429、F439这几款才有)。这意味着你不能像F429那样直接接RGB888/RGB565接口的裸屏,也不能把SDRAM挂在FMC上直接当显存用。所以你在F407上跑TouchGFX,本质上要靠“帧缓冲 + 软件刷新”的思路,把渲染结果主动推到屏幕控制器(如ILI9341)的GRAM里。
我整理了一张表,方便你判断自己手上的板子适合哪种方案:
| 开发板类型 | 典型型号 | Flash/RAM | SDRAM | 推荐屏幕接入方式 | 帧缓冲方案 |
|---|---|---|---|---|---|
| 野火指南者 | STM32F407VET6 | 512KB/192KB | 无 | SPI TFT屏 或 FSMC并口屏 | 局部缓冲(Partial Buffer) |
| 野火挑战者V2 | STM32F407ZGT6 | 1MB/192KB | 部分有 | FSMC并口屏 或 外接RGB转接模块 | 可考虑双缓冲(配合SDRAM) |
| 任意自画板 | STM32F407ZGT6 | 1MB/192KB | 可外接SDRAM | FSMC + 并口屏 | 建议局部缓冲或外部帧缓冲 |
如果你是指南者,没有SDRAM,别慌,后面我会给出能在192KB RAM里跑起来的方案。如果你的板子有SDRAM,恭喜你,你会省掉很多内存纠结的环节,直接用双缓冲就行。
2. 环境搭建与CubeMX工程配置
2.1 工具链版本搭配
TouchGFX这玩意儿的工具链版本匹配非常让人头疼,版本不匹配,CubeMX里不会出现TouchGFX Generator插件,或者生成的代码跟你工程里的HAL版本对不上,导致各种奇怪的编译错误。
我实际使用的组合如下:
- STM32CubeMX 6.9.x
- TouchGFX Designer 4.22.x
- Keil MDK 5.38
- STM32CubeF4 HAL库 1.28.x(CubeMX里选的版本)
这里有两个要点。第一,TouchGFX Designer和CubeMX的插件版本必须匹配,现在CubeMX在Middleware选项里出现TouchGFX,实际上是调用了你电脑上已安装的TouchGFX Designer。如果版本太旧,可能无法与当前CubeMX交互,建议把两个软件都升级到相对较新的版本。第二,如果代码运行正常但Designer预览对不上字体效果,优先检查字体生成配置,而不是怀疑版本问题。
注意:TouchGFX Generator在CubeMX里配置时,不要乱改“Application Monitor”和“Trace”等调试选项,我最初为了保证刷新率开了Trace,结果HAL层多出一堆调试代码,导致编译增肥和调试串口被占用,后面全关了。
2.2 时钟与基础外设配置
在你打开TouchGFX之前,先得把基础的CubeMX工程配置好。以野火指南者为例,板载外部晶振为8MHz(注意:不是16MHz,也不是25MHz,具体看你的板子原理图,挑战者可能是25MHz)。时钟树最高配置到168MHz:APB1为42MHz,APB2为84MHz。
如果这一块配置不对,最直观的现象是屏幕刷新超时、DMA2D时序错乱甚至系统跑飞。我的建议是先在CubeMX里把LED闪烁的裸机工程跑起来,确认串口、时钟稳定,再进行屏幕和TouchGFX的集成。
关键外设配置建议:
- RCC:外部高速晶振打开
- SYS:Debug设置为Serial Wire,时基源选择TIM6或TIM7(避免SysTick和FreeRTOS冲突)
- GPIO:屏幕的复位、背光、片选、数据命令脚按你的原理图分配,输出模式设置为推挽输出,速度选High
- 如果是SPI屏,把SPI配置为硬件NSS关闭、软件控制片选,时钟极性/相位按屏的规格书选(I使用SPI Mode 0或Mode 3,实测ILI9341两种都行,但Mode 0兼容性更稳)
- 如果是FSMC并口屏,直接配置FSMC Bank1,复用NE1/A18等引脚,BusTurnAroundDuration设为0或1,具体时序参数参考屏幕数据手册
2.3 TouchGFX Generator与工程生成
在CubeMX里配置好以上硬件后,一定要先“Generate Code”一次,确认基础工程能编译通过,然后再返回CubeMX,在“Middleware and Software Packs”下找到TouchGFX,选好Display接口设置:
- 分辨率:我这边设的是480x272(屏是3.2寸480x272),如果你的屏是320x240,那就填320x240
- Color Depth:RGB565,这是默认选项,每个像素2字节,兼顾画面和内存占用
- Frame Buffer策略:先选Partial Buffer,后面我会说为什么
- 勾选Enable DMA2D Acceleration
保存后再次生成代码,CubeMX会自动调用TouchGFX Designer生成一个基础UI工程,并且TouchGFX的HAL层代码会直接嵌入你的工程目录。这时你打开Keil工程,编译一次,理论上会通过,但屏幕还没画面,因为底层的LCD刷新回调还没人写。这正是下一节我们要搞定的核心环节。
3. 屏驱与帧缓冲实战:让第一个界面亮起来
3.1 F407没有LTDC,屏幕驱动方案怎么选
之前提过硬件的限制,F407没有LTDC,所以屏幕驱动是个绕不过去的环节。TouchGFX只负责把图像渲染到帧缓冲里,它不管屏幕是通过什么接口显示的。你需要在TouchGFX的BSP层实现“把帧缓冲区域的数据刷新到屏幕”这个动作,也就是BSP_Display_Flush回调函数。
我实测下来,适合F407的屏幕接入方案主要有两类:
方案一:SPI接口TFT屏(如ILI9341)。这种方式接线最简单,只需要MOSI、SCK、CS、DC、RST、BLK几根线。缺点是SPI时钟再高也就几十MHz,全屏刷新一次要传输的数据量是:480x272x2字节,约261KB,按10MHz SPI算,全屏刷新至少也要200ms级别,肉眼明显能看到刷屏过程。解决办法是TouchGFX的局部刷新机制,它每次只刷新有变化的矩形区域,而不是全屏,所以简单界面的刷新压力还能接受。
方案二:FSMC并口屏(如ILI9341 16bit并口模式)。这比SPI快得多,16位并口一次传2字节,速度可以达到几十MB/s,全屏刷新性能可以接受。但不好的地方是引脚占用非常多,大约20多个GPIO,指南者这类板子一般都预置了FSMC接口,所以直接用就行。
我在这个项目里用的是FSMC并口屏,因为指南者板子里正好有这个屏接口。如果你用的是SPI屏,也能跑,但最好把TouchGFX的局部缓冲块调小一点(比如每次只刷新8或16行),这样刷屏延迟不会太感人。
还有一点,屏幕的横竖屏设置是在LCD初始化序列里调整的。ILI9341的0x36寄存器控制扫描方向,修改MX、MY、MV位就可以旋转屏幕。比如要用横屏480x272,通常设置0x36为0x28(MV=1,MX=0,MY=1,BGR=1),具体数值根据你的屏硬件接线来试。TouchGFX这边只要把分辨率跟屏幕当前的实际显示方向对齐就行。
3.2 帧缓冲策略与内存规划:没有SDRAM怎么跑
帧缓冲是TouchGFX最吃内存的地方。RGB565一个像素2字节,一个480x272的完整帧缓冲需要261KB,这比F407的192KB RAM还大,所以不分情况就上全帧缓冲,肯定是死路一条。
解决思路是用Partial Buffer(局部缓冲)。TouchGFX里的局部缓冲并不要求一整帧显存,而是开辟一块较小的缓冲区(比如480x16x2=15KB),TouchGFX渲染引擎每次只渲染当前需要更新的那一部分界面,渲染完一个矩形块后,立刻通过刷屏回调送到屏幕GRAM,然后再渲染下一块。
这块缓冲区的最终大小由TouchGFX生成代码里的FrameBufferSize决定,同时也受到你剩余RAM的限制。我在指南者上分配给TouchGFX的局部缓冲是480x12x2=11.5KB,再加上TouchGFX内部的一些文本缓冲、存储,总占用量大约30KB左右,剩下的RAM留给系统栈和业务代码完全够用。
如果你的板子有SDRAM,情况就不一样了。你可以直接把整帧缓冲放在SDRAM里(分配2个480x272的缓冲甚至更多),使用Double Buffering(双缓冲)模式,TouchGFX渲染到一个缓冲的同时,另一个缓冲可以异步刷新到屏幕,画面不会出现撕裂和明显的“画一半”现象。这也是我从指南者换成带SDRAM板子后最直观的感受。
内存规划粗略表:
| 方案 | 显存需求(480x272 RGB565) | 是否可双缓冲 | 适用硬件 |
|---|---|---|---|
| 全帧缓冲+内部RAM | 261KB | 否(内存不够) | 无SDRAM也不适合 |
| 局部缓冲(16行) | 15KB | 否(天然不撕裂) | 指南者、无SDRAM板 |
| 全帧缓冲+SDRAM | 522KB | 是 | 带SDRAM的挑战者板 |
提示:如果使用局部缓冲,
BSP_Display_Flush里只需要把传入的矩形区域写到屏幕GRAM即可,千万别每次全屏刷新,否则局部缓冲的意义就没了。
3.3 中文字体与字体图标:解决显示“方块”问题
TouchGFX默认生成的字体是英文的,如果你直接显示中文或图标,屏幕就会出现方块。原因很简单:字体文件里压根没有对应字符的glyph数据。
中文字体方面,我在TouchGFX Designer的Texts界面里,点击Fonts,添加一个中文字体,比如系统的“Microsoft YaHei”或开源“思源黑体”,然后在Unicode区域手动添加需要的中文Unicode范围。最省事的是直接勾选CJK Unified Ideographs(0x4E00~0x9FFF),这样就能覆盖全部常用汉字,但生成的字体bin文件会非常大,一个十几MB都是正常的。F407的512KB Flash根本放不下,所以必须只挑选你用到的字。
实操做法:先写个脚本或直接列一个你自己界面中出现的中文字符串,把里面每个汉字的Unicode码收集起来,在Designer里一个一个或一段一段手动添加。比如你界面上有“温度”和“湿度”两个字,那就只加温、度、湿这三个字的Unicode码(0x6E29、0x5EA6、0x6E7F)。这样生成的字体文件很小,几百KB就到头了。
字体图标也是热门问题,很多人问TouchGFX能不能用FontAwesome这样的图标字体。答案是可以,而且方法跟中文字体类似。把FontAwesome的.ttf文件导入Designer的Fonts,然后在Unicode范围里添加0xF000~0xF2FF这个区间(FontAwesome的私有区),这样你在文本框里输入对应Unicode字符,就能显示图标。
但用代码设置图标时要注意,TouchGFX的文本框是通过生成的缓冲字符串存储文字的,不能直接赋值一个UTF-8字符串就完事。我一般是在代码里用Unicode::strncpy把图标Unicode字符写入文本缓冲:
// 在touchgfx中设置文本区域缓冲 // 假设T_FA_ICON是Designer里配好的字体,缓冲为textArea1Buffer Unicode::strncpy(textArea1Buffer, "\uF2DB", 10); // F2DB是FontAwesome中的蓝牙图标 textArea1.setWideTextAction(WideTextAction::WIDE_TEXT_CHARWRAP); textArea1.invalidate();注意:源文件必须保存为UTF-8编码,否则\uF2DB这种转义在MDK里可能解析不对,我就在这上面吃过亏——图标显示成了乱码。另外,图标字体建议使用“灰度”渲染而不是“二值”渲染,因为彩色图标的边缘需要灰度过渡,否则显示出来毛毛躁躁的。
4. 常见问题与排查技巧实录
4.1 黑屏、白屏、花屏的排查顺序
屏幕不亮,是最打击人的问题。我一开始也折腾了很久,后来总结了一套排查顺序,按照这个顺序排查,基本都能解决:
- 先确认屏幕本身的背光控制引脚电平是否正确,很多屏背光默认是高电平点亮,如果MCU引脚配置成低电平,屏幕就是“黑屏”状态
- 再用逻辑分析仪或示波器看RESET复位引脚的脉冲时序,屏幕控制器对上电时序有要求,RESET需要拉低再拉高,并且延时至少10ms
- 如果已经有数据信号,但屏幕白屏,大概率是初始化序列没跑对。ILI9341这类屏的初始化序列不能随便网上抄,必须看你的屏幕模块厂家提供的资料,代码版本不同,指令细节也有差异
- 花屏的常见原因是帧缓冲地址和TouchGFX给HAL层设置的地址不一致。你可以在TouchGFX的
HAL_Init或BSP_Display_FrameBufferAddress里打印出帧缓冲首地址,再用调试器查看这个地址里是否有实际渲染好的像素数据
我遇到过最隐蔽的一次是FSMC总线时序问题。屏幕初始化函数跑完之后,写了一点字符测试代码能显示,但TouchGFX一刷新就花屏。最后发现是FSMC的地址建立时间、数据建立时间配置太短,导致LCD控制器读取数据不稳定。调整为默认值+一个时钟周期之后,问题消失。
4.2 编译错误与Flash不足问题
编译错误里最典型的是“stm32f4xx_hal_conf.hnot found”,或者TouchGFX生成的代码里找不到某个HAL函数。这种情况九成是CubeMX生成的代码和当前工程里包含的HAL库版本不一致,最简单的办法是在CubeMX里重新生成一次代码,让HAL库配置和TouchGFX版本对齐。
Flash不足则在用了中文字体后集中爆发。我的处理思路是这样的:优先把中文字体控制在50个汉字以内,如果实在有多语言需求,可以换用外部SPI Flash(如W25Q64)来存储字体资源,TouchGFX支持通过自定义读取器从外部Flash加载字体,具体实现会稍微复杂一些,但能彻底解决F407内置Flash太小的问题。
另外,如果用的是野火指南者这种512KB Flash的板子,编译优化等级别设成-O0,否则代码段和数据段会膨胀到你怀疑人生。我在调试阶段用-O0,生成的固件超过512KB,改成-O2之后,瞬间降到400KB以内。
4.3 性能卡顿与刷新率优化
TouchGFX跑在F407上,性能不会像F429那样飞起,但只要不上特别复杂的动画,体感还是流畅的。如果你的界面明显卡顿,可以按这几个方向排查:
- 检查局部缓冲区块是不是太小。太小会导致TouchGFX反复进行“渲染+刷新”的循环,增加CPU开销;太大又占用内存,需要平衡。我的经验是480宽度的屏幕,局部缓冲12~20行比较合适
- 开启DMA2D加速。TouchGFX的图像搬运、填充、混合大量使用DMA2D,如果HAL层里没有启用DMA2D,CPU会被像素操作拖死
- 降低动画复杂度。TouchGFX默认的Button Transition效果很华丽,但效果越复杂需要重绘的区域越多。在F407这种定位的芯片上,建议关闭阴影、粒子等特效,保留简单移动和淡入淡出即可
- SPI屏的话,把SPI时钟调到屏幕支持的极限,比如ILI9341 SPI最大可以到40MHz左右,我实际使用24MHz,稳定且刷新延迟可接受
我实测在480x272、RGB565、局部缓冲16行的配置下,一个带有3个按钮、1个仪表盘、1个滑块的界面,TouchGFX的渲染帧率大约在18~25 FPS,虽然没有60FPS那么丝滑,但作为人机交互界面已经完全可用。如果你追求更高帧率,就得换F429/F746或者降低分辨率了。
最后再分享一个小技巧:如果你想在野火指南者上做UI原型验证,先别急着把所有页面都搭起来,用TouchGFX Designer搭一个带常用控件的空页面,编译下载到板子,确认画面能刷出来后再加业务逻辑。这样一旦后面出现黑屏或者花屏,你至少能确定是UI代码的问题还是底层驱动的问题。我在实际项目中,所有开发板适配都是从这个“最小可显示系统”开始的,它能帮你把环境问题与业务问题快速隔离。
本文还有配套的精品资源,点击获取