STM32CubeMX+CLion打造TouchGFX界面开发实战(附源码)
1. 环境准备与工具链配置
在开始STM32CubeMX+CLion+TouchGFX的开发之前,我们需要先准备好整个工具链。我实际测试用的环境是STM32CubeMX 6.11.1、TouchGFX 4.24.0和CLion 2023.3.4,芯片选的是STM32F407ZG。这里有个小提醒,不同版本的工具在配置上可能会有细微差别,如果你用的版本和我不一样,遇到问题时要灵活调整。
先安装STM32CubeMX,这个没什么好说的,去ST官网下载安装包直接安装就行。安装完成后,记得在Help -> Manage Embedded Software Packages里面找到并安装TouchGFX软件包。这个步骤很关键,没有这个包,后续在CubeMX里就看不到TouchGFX的配置选项。
CLion的安装也比较 straightforward,JetBrains官网下载专业版,记得申请教育许可证或者购买正版授权。安装好后,需要安装两个插件:Embedded Tools和STM32CubeMX插件。这两个插件是CLion支持STM32开发的核心,没有它们就无法直接生成和调试嵌入式项目。
硬件方面,我用的是一块STM32F407ZG开发板,搭配一块SPI接口的LCD屏幕。SPI屏的选择很重要,一定要确认你的屏幕控制器是否被TouchGFX官方支持。我用的是一块ILI9341控制的屏幕,这是比较常见的型号,社区支持也比较好。
提示:如果你使用的是其他型号的STM32芯片,确保它有足够的Flash和RAM来运行TouchGFX。F407系列通常没问题,但如果是F103之类资源紧张的芯片,可能需要做很多优化才能跑起来。
2. STM32CubeMX工程配置
打开STM32CubeMX,点击New Project,选择STM32F407ZG芯片。这一步要注意选择正确的芯片型号,不同封装的引脚定义可能不一样。选好芯片后,我们先配置时钟树。F407的最高主频可以到168MHz,我一般会配置到最大频率,这样TouchGFX的运行会更流畅。
在Pinout & Configuration页面,需要配置几个关键外设。首先是SPI,因为我们的LCD屏幕是通过SPI接口通信的。我用了SPI2,配置为全双工主模式,硬件NSS信号禁用,数据大小8位,时钟极性低,相位第一边沿,波特率预分频器设为2,这样SPI时钟频率就是42MHz。
接下来要开启CRC计算单元,这个在TouchGFX中用于图形数据的校验,是必须开启的。在System Core目录下找到CRC,勾选激活即可。
然后配置FreeRTOS,在Middleware and Software Packs中选择FREERTOS,Interface选择CMSIS_V2。这是新版FreeRTOS的API,比传统的V1接口更好用。创建两个任务:一个叫TouchGFX_Task,选择As Weak方式,这个函数后面会被TouchGFX自动实现;另一个叫TimeTask,用于提供刷新同步信号。
在Project Manager页面,Toolchain / IDE要选择STM32CubeIDE,这个选项生成的工程结构CLion才能正确识别。一定要勾选Generate Under Root,这样生成的代码才会放在项目根目录下,而不是嵌套在多层目录里。
最后点击Generate Code生成工程代码。第一次生成可能会比较慢,因为要下载相关的HAL库和中间件。生成完成后,你会看到一个完整的工程目录结构,包含了所有的驱动文件和配置文件。
3. TouchGFX Designer界面设计
在工程目录下找到.touchgfx文件,用TouchGFX Designer打开。第一次打开时会让你选择模板,我建议选择Blank UI,从头开始创建界面,这样能更好地理解整个框架的工作方式。
Designer的界面分为几个主要区域:左边是控件库,中间是画布,右边是属性面板。我从控件库拖了一个Box组件到画布上作为背景,设置颜色为深灰色。然后添加了一个TextArea组件,设置文字为"Hello TouchGFX",字体选择默认的Roboto 20px。
接着我添加了一个Button组件,设置文本为"Click Me",并为其添加了点击事件。在交互部分,我设置点击按钮后改变TextArea的文字内容。这个简单的交互能帮助我们理解TouchGFX的事件处理机制。
注意:在设计界面时要注意屏幕的分辨率,我的屏幕是320x240,所以画布大小也设置为这个尺寸。如果你用的屏幕分辨率不同,记得相应调整。
设计完成后,点击右上角的Generate Code按钮生成UI代码。这个过程会将图形界面转换为C++代码,并集成到STM32工程中。生成完成后,你会在工程目录下看到新增了很多TouchGFX相关的源文件和头文件。
4. CLion工程配置与编译
用CLion打开刚才生成的工程,第一次打开时会自动进行CMake配置。但是直接编译肯定会报错,因为自动生成的CMake模板有些问题,需要手动修改。
首先修改CMakeLists_template.txt文件,这个文件在项目根目录下。需要添加硬件浮点数支持,因为F407带有FPU单元。在add_compile_options中添加-mfloat-abi=hard -mfpu=fpv4-sp-d16这两个选项。
然后添加头文件搜索路径,主要是TouchGFX生成的文件路径和HAL库的路径。在include_directories中添加以下路径:
${CMAKE_SOURCE_DIR}/TouchGFX/generated/gui_generated/include
${CMAKE_SOURCE_DIR}/TouchGFX/generated/fonts/include
${CMAKE_SOURCE_DIR}/TouchGFX/generated/texts/include
${CMAKE_SOURCE_DIR}/TouchGFX/generated/images/include
接下来修改可执行文件的链接选项,添加链接脚本。在target_link_options中添加-T${CMAKE_SOURCE_DIR}/STM32F407ZGTx_FLASH.ld,这个链接脚本是CubeMX自动生成的,确保了代码和数据在Flash和RAM中的正确布局。
还需要链接TouchGFX的库文件,在target_link_libraries中添加touchgfx-${MCU_FAMILY},这个库包含了TouchGFX引擎的核心功能。
修改完成后,右键点击CMakeLists.txt,选择Reload CMake Project。这时候CLion会重新解析CMake配置,如果配置正确,应该能看到所有的源文件都被正确识别了。
5. 显示驱动移植与适配
现在开始最关键的驱动移植部分。打开TouchGFXGeneratedHAL.cpp文件,这个文件是TouchGFX与硬件之间的桥梁。我们需要实现三个关键函数:touchgfxDisplayDriverTransmitActive、touchgfxDisplayDriverShouldTransferBlock和touchgfxDisplayDriverTransmitBlock。
首先在文件顶部添加全局变量:
volatile bool spi_trans_comp = false;
LcdDriver lcdd;
然后实现三个传输函数:
extern "C" int touchgfxDisplayDriverTransmitActive() {
return spi_trans_comp;
}
extern "C" int touchgfxDisplayDriverShouldTransferBlock(uint16_t bottom) {
return 1;
}
extern "C" void touchgfxDisplayDriverTransmitBlock(const uint8_t* pixels, uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
spi_trans_comp = true;
lcdd.FlushColor(x, x+w, y, y+h, (uint16_t*)pixels);
}
这些函数负责将图形数据通过SPI发送到LCD屏幕。spi_trans_comp变量用于同步传输状态,确保不会在传输过程中被中断。
接下来需要实现DMA传输完成回调函数,这个函数在HAL库中定义:
extern "C" void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
if (hspi->Instance == SPI2) {
spi_trans_comp = false;
DisplayDriver_TransferCompleteCallback();
}
}
这个回调函数会在DMA传输完成时被调用,更新传输状态变量,并通知TouchGFX引擎可以开始下一次传输。
最后在TouchGFXConfiguration.cpp文件中添加LCD初始化代码:
extern LcdDriver lcd;
void touchgfx_components_init() {
lcd.Init();
}
这个初始化函数会在TouchGFX启动时被调用,确保LCD屏幕在图形渲染前已经正确初始化。
6. FreeRTOS任务调度与同步
在CubeMX中我们已经创建了两个FreeRTOS任务,现在需要实现它们的具体功能。TimeTask任务负责提供垂直同步信号:
extern "C" void TimeTask(void const * argument) {
while(1) {
touchgfxSignalVSync();
vTaskDelay(pdMS_TO_TICKS(10));
}
}
这个任务每10毫秒发送一次VSync信号,告诉TouchGFX引擎可以开始新一帧的渲染。这个时间间隔可以根据你的屏幕刷新率调整,通常60Hz的屏幕需要16.67ms的间隔。
TouchGFX_Task任务由TouchGFX自动生成,主要负责处理用户输入和界面更新。我们不需要手动实现这个函数,但需要确保它在FreeRTOS中具有足够的堆栈空间。我一般设置为2048字节,因为TouchGFX需要较多的栈空间来处理图形渲染。
在FreeRTOS配置中,还需要调整几个重要参数。configTOTAL_HEAP_SIZE设置为足够大的值,我一般用20KB,因为TouchGFX和HAL库都需要动态内存。configMAX_PRIORITIES设置为7,给任务调度足够的优先级空间。
注意:FreeRTOS任务的优先级要合理设置,TouchGFX_Task应该具有较高的优先级,确保界面响应及时,但不要高于系统关键任务。
7. 调试与问题排查
编译通过后,连接ST-Link调试器,点击CLion中的Debug按钮开始调试。第一次运行可能会遇到各种问题,我总结了一些常见的坑和解决方法。
如果屏幕没有显示,首先检查SPI引脚配置是否正确。用逻辑分析仪或者示波器测量SPI时钟线和数据线,看是否有信号输出。如果没有信号,可能是SPI初始化有问题,检查CubeMX中的SPI配置。
如果屏幕有显示但花屏,可能是颜色格式不匹配。TouchGFX默认使用RGB565格式,但有些屏幕需要RGB888或者其他格式。在LCD驱动器的初始化代码中调整颜色格式设置。
还有可能遇到内存不足的问题,表现为程序运行不稳定或者随机重启。这时需要优化内存使用,可以减小帧缓冲区大小,或者使用TouchGFX的部分帧缓冲功能。
我在实际项目中还遇到过DMA传输不稳定的情况,表现为屏幕闪烁或者部分区域刷新不正常。这个问题通常是因为DMA缓冲区太小或者地址不对齐,调整缓冲区大小和确保32位地址对齐可以解决。
如果遇到触摸屏不响应,首先检查触摸IC的驱动是否正确初始化。然后用调试器读取触摸原始数据,看是否有变化。如果没有变化,可能是硬件连接问题或者初始化序列不正确。
调试时充分利用CLion的调试功能,设置断点观察变量值,使用实时表达式监控关键变量。还可以启用FreeRTOS的调试视图,观察任务状态和堆栈使用情况。
8. 性能优化与进阶技巧
当基本功能都调通后,我们可以开始考虑性能优化。首先关注帧率,使用GPIO翻转和逻辑分析仪测量实际帧率。如果帧率低于30fps,可能需要优化渲染流程。
启用TouchGFX的硬件加速功能可以显著提升性能。F407有Chrom-ART加速器,可以在CubeMX中开启DMA2D,然后在TouchGFX配置中启用硬件加速。这样图形拷贝和混合操作都会由硬件完成,大大减轻CPU负担。
内存使用也是优化的重点。使用TouchGFX提供的Bitmap Database工具分析位图内存占用,将不常用的图片放到外部Flash中,需要时再加载到内存。还可以使用L8颜色格式的图片减少内存占用。
对于动态界面,合理使用Invalidate和Redraw机制。只重绘需要更新的区域,而不是整个屏幕,可以显著减少渲染时间。TouchGFX的脏矩形机制就是为此设计的,要确保正确使用。
电源管理也是实际项目中的重要考虑。在不需要更新界面时,可以降低刷新率或者进入低功耗模式。TouchGFX支持部分刷新和睡眠模式,合理使用可以显著降低功耗。
最后不要忘记代码维护性。将硬件相关代码和界面逻辑分离,使用MVP或者MVVM模式组织代码。这样后期更换硬件或者修改界面时,互相不会产生影响。
更多推荐


所有评论(0)