第3章 TouchGFX基础框架移植:从安装到驱动集成
TouchGFX的移植,说白了就是让这个GUI框架在你的硬件上跑起来。很多初学者一上来就盯着代码看,结果越看越懵。我个人的习惯是——先搞清楚工具链怎么用,再动手改代码。
这一章,我们就从TouchGFX Designer的安装开始,一步步走到触摸驱动的集成。嗯,内容不少,但每一步我都会把坑点讲清楚。
3.1 TouchGFX Designer安装与项目生成
TouchGFX Designer是ST官方提供的图形化配置工具。它不仅能帮你生成UI代码,还能直接输出一个完整的工程框架。
3.1.1 安装步骤
- 下载安装包:从ST官网或GitHub Release页面获取最新版本。注意区分Windows和Linux版本。
- 安装依赖:Windows下基本一路Next;Linux需要确保有Java Runtime Environment(JRE 11+)。
- 验证安装:打开命令行,输入
TouchGFXDesigner --version,能看到版本号就对了。
3.1.2 生成第一个项目
打开Designer后,你会看到一个模板选择界面。这里我建议选“Empty Application”,别选那些带例程的模板——那些模板绑定了特定的硬件配置,反而容易干扰你的移植思路。
项目生成后,Designer会输出一个完整的文件夹结构:
MyProject/
├── Application/ # UI代码(你主要改这里)
├── Target/ # 硬件抽象层(HAL)
│ ├── BoardConfiguration.cpp
│ ├── TouchController.cpp
│ └── ...
├── generated/ # 自动生成的代码(别手动改)
└── TouchGFX/ # 核心库文件
这里有个关键点:Target/ 目录下的文件,才是我们移植工作的主战场。
3.2 HAL层配置:让GUI认识你的硬件
HAL(硬件抽象层)是TouchGFX和MCU之间的桥梁。说白了,就是告诉TouchGFX:你的屏幕分辨率是多少、像素格式是什么、帧缓冲放在哪里。
3.2.1 核心配置项
打开 BoardConfiguration.cpp,你会看到类似这样的代码:
void TouchGFXHAL::initialize()
{
// 设置帧缓冲地址
setFrameBufferStartAddress((uint16_t*)0xC0000000);
// 配置显示参数
hal->setDisplayWidth(480);
hal->setDisplayHeight(272);
hal->setBitsPerPixel(16); // RGB565
// 配置帧缓冲策略
hal->setFrameBufferStrategy(FB_STRATEGY_MMU);
}
这里我踩过一个坑:帧缓冲地址必须对齐到32字节边界,否则DMA传输会出问题。我当时用的是SDRAM,地址0xC0000000没问题,但如果你用内部SRAM,记得检查对齐。
| 配置项 | 说明 | 常见值 |
|---|---|---|
| 显示宽度/高度 | 物理像素尺寸 | 480x272, 800x480 |
| 像素格式 | 每个像素的位数和排列 | RGB565, RGB888 |
| 帧缓冲策略 | 单缓冲/双缓冲/MMU | FB_STRATEGY_MMU |
| VSync信号 | 是否使用垂直同步 | true/false |
3.3 帧缓冲驱动:屏幕刷新的核心
帧缓冲驱动负责把TouchGFX渲染好的图像数据,传输到LCD屏幕上。这里有两种常见方案:
- CPU直接传输:简单,但占用CPU资源高,适合小屏幕。
- DMA2D传输:硬件加速,不占CPU,适合大屏幕或高帧率场景。
我个人强烈建议用DMA2D。为什么?因为TouchGFX的渲染引擎本身就很吃性能,你再让CPU去搬运数据,帧率直接掉一半。
3.3.1 实现DMA2D传输
在 BoardConfiguration.cpp 中,你需要重写 flushFrameBuffer() 方法:
void TouchGFXHAL::flushFrameBuffer(const Rect& rect)
{
// 配置DMA2D源地址(帧缓冲)和目标地址(LCD GRAM)
DMA2D->CR = DMA2D_CR_M2M;
DMA2D->FGMAR = (uint32_t)getFrameBufferStartAddress();
DMA2D->OMAR = (uint32_t)LCD_GRAM_ADDR;
DMA2D->FGPFCCR = DMA2D_FGPFCCR_CM_RGB565;
DMA2D->NLR = (rect.height << 16) | rect.width;
// 启动传输
DMA2D->CR |= DMA2D_CR_START;
// 等待完成
while (DMA2D->CR & DMA2D_CR_START);
}
这里有个细节:rect 参数表示需要刷新的区域。TouchGFX只会刷新变化的部分,而不是全屏刷新。这能大幅提升性能。
rect 参数。否则每次都全屏刷新,DMA2D的优势就浪费了。
3.4 触摸驱动集成:让交互活起来
触摸驱动是用户交互的入口。TouchGFX的触摸框架设计得很巧妙——它通过一个 TouchController 抽象类来解耦硬件。
3.4.1 实现TouchController
在 TouchController.cpp 中,你需要实现三个核心方法:
bool TouchController::init()
{
// 初始化I2C或SPI接口
// 复位触摸芯片
// 读取设备ID确认通信正常
return true;
}
bool TouchController::sampleTouch(int32_t& x, int32_t& y)
{
uint8_t data[5];
// 读取触摸坐标数据
if (readTouchData(data)) {
x = (data[1] << 8) | data[2];
y = (data[3] << 8) | data[4];
return true;
}
return false;
}
这里有个容易忽略的点:sampleTouch() 的返回值。如果返回 false,TouchGFX会认为没有触摸事件,不会触发任何回调。所以一定要确保在无触摸时返回 false,而不是返回上一次的坐标。
3.4.2 校准与坐标映射
触摸屏的物理坐标和屏幕像素坐标往往不是一一对应的。你需要做校准。我常用的方法是:
- 在屏幕四个角显示校准点。
- 记录用户点击时的触摸坐标。
- 用线性插值计算出映射系数。
不过在实际项目中,很多触摸芯片(如FT5x06、GT911)内部已经做了校准,你只需要读取坐标即可。但如果你用的是电阻屏,那就必须自己做校准了。
sampleTouch() 里做一次坐标翻转:x = displayWidth - x。
3.5 整体移植流程总结
为了让你更直观地理解整个移植流程,我画了一张图:
从图中你可以看到,整个流程是线性的,但HAL配置之后会分叉成两个并行任务——帧缓冲和触摸驱动。我建议先搞定帧缓冲,因为屏幕能亮了,你才能验证触摸坐标对不对。
3.6 常见问题与调试技巧
- 屏幕不亮:检查帧缓冲地址是否正确,LCD初始化时序是否完整。用逻辑分析仪抓一下LCD的时钟和数据线。
- 触摸无响应:先确认I2C/SPI通信正常。可以在
sampleTouch()里加个串口打印,看看坐标数据是否在变化。 - 显示花屏:大概率是像素格式不匹配。检查
setBitsPerPixel()和LCD控制器的配置是否一致。
好了,这一章的内容就到这里。TouchGFX的移植其实不复杂,关键是把每个环节的配置搞清楚。你只要按照我上面说的步骤来,基本不会出大问题。