3、TouchGFX校准框架解析:HAL层中的触摸接口、校准数据结构与回调机制
做TouchGFX移植,最绕不开的就是校准这块。很多朋友拿着官方例程一顿复制,结果触摸点要么飘到天边,要么点下去没反应。说白了,就是没搞懂HAL层里那几个接口到底在干什么。
这一章,我带你拆开TouchGFX的校准框架。咱们从三个角度入手:触摸接口、校准数据结构、回调机制。搞懂这三样,校准对你来说就是透明的。
3.1 HAL层触摸接口:谁在跟硬件打交道?
TouchGFX本身不直接操作硬件。它通过HAL层抽象出一组接口,让上层UI代码跟底层驱动解耦。触摸相关的接口,核心就两个:
sampleTouch()—— 获取原始触摸坐标setTouchSampleRate()—— 设置采样频率
这两个函数定义在 TouchController.hpp 里,你需要自己实现。我习惯把驱动层的读取逻辑封装在 sampleTouch() 里,这样上层只管拿坐标,不用管I2C还是SPI。
关键点: sampleTouch() 返回的是 原始ADC值,不是像素坐标。校准就是要把这个原始值映射到屏幕分辨率上。
来看看典型的实现长什么样:
// TouchController.cpp
#include "TouchController.hpp"
static uint16_t touchX, touchY;
static bool touchPressed;
bool TouchController::sampleTouch(int32_t& x, int32_t& y) {
// 读取触摸控制器(例如FT5x06)
if (touchPressed) {
x = touchX; // 原始ADC值
y = touchY;
return true;
}
return false;
}
void TouchController::setTouchSampleRate(uint16_t rate) {
// 设置定时器采样周期
// 通常用100Hz,即10ms一次
}
嗯,这里要注意:sampleTouch() 返回的坐标范围取决于触摸控制器的分辨率。比如FT5x06是12位ADC,范围0~4095。而你的屏幕可能是480x272。不校准直接映射,点不准是必然的。
3.2 校准数据结构:坐标怎么存?
TouchGFX用 TouchCalibrationData 结构体来保存校准参数。这个结构体定义在 platform/hal/inc/touch/TouchCalibration.hpp 里:
struct TouchCalibrationData {
int32_t xMin; // 触摸X轴最小值
int32_t xMax; // 触摸X轴最大值
int32_t yMin; // 触摸Y轴最小值
int32_t yMax; // 触摸Y轴最大值
int32_t xCenter; // 触摸X轴中心值
int32_t yCenter; // 触摸Y轴中心值
};
说白了,这就是一个线性映射的边界参数。TouchGFX内部用这些值做缩放和平移:
// 伪代码:校准映射过程
screenX = (rawX - calib.xMin) * screenWidth / (calib.xMax - calib.xMin);
screenY = (rawY - calib.yMin) * screenHeight / (calib.yMax - calib.yMin);
我在项目里遇到过一个问题:有些触摸屏的ADC值不是从0开始的,比如最小值是50,最大值是4000。如果不校准,直接除以4096再乘分辨率,边缘区域根本点不到。所以校准数据的准确性,直接决定了触摸手感。
我的习惯: 校准数据不要硬编码。我会在首次启动时运行校准程序,把结果保存到EEPROM或Flash的固定地址。下次启动直接读取,省去每次校准的麻烦。
3.3 回调机制:坐标怎么送到UI层?
TouchGFX的触摸事件传递,依赖一个回调链。核心是 HAL::setTouchSampleRate() 和 HAL::touchHandler()。流程是这样的:
- 定时器中断触发 → 调用
sampleTouch()获取原始坐标 - HAL层内部调用校准函数,将原始坐标映射为屏幕坐标
- 通过回调函数,将屏幕坐标传递给
Application对象 - Application 分发触摸事件到当前活动的Screen
这个回调函数在 HAL 类中定义:
// HAL.hpp 中相关声明
typedef void (*TouchEventCallback)(int16_t x, int16_t y, uint8_t event);
void HAL::setTouchEventCallback(TouchEventCallback callback) {
touchEventCallback = callback;
}
你想想看,这个回调是谁注册的?是 Application 在初始化时注册的。所以只要你的 sampleTouch() 返回了正确的原始值,剩下的映射和分发,TouchGFX框架全帮你做了。
我曾经踩过的坑: 回调函数是在中断上下文里调用的。如果你的触摸驱动里做了延时操作(比如I2C等待应答),会导致整个UI卡顿。解决办法是把触摸读取放在一个高优先级任务里,用信号量通知HAL层去采样。
3.4 校准框架整体流程
为了让你看得更清楚,我画了一张流程图。这张图展示了从触摸按下到UI响应的完整链路:
3.5 校准数据存储与加载
校准数据不能每次开机都重新校准。我一般把它存在外部EEPROM或Flash的最后几个扇区。结构体大小只有24字节,随便找个地方就能存。
存储格式建议:
| 偏移地址 | 内容 | 大小 | 说明 |
|---|---|---|---|
| 0x00 | Magic Number | 4字节 | 0xCA11B9A5,用于校验数据有效性 |
| 0x04 | xMin | 4字节 | 触摸X轴最小值 |
| 0x08 | xMax | 4字节 | 触摸X轴最大值 |
| 0x0C | yMin | 4字节 | 触摸Y轴最小值 |
| 0x10 | yMax | 4字节 | 触摸Y轴最大值 |
| 0x14 | xCenter | 4字节 | 触摸X轴中心值 |
| 0x18 | yCenter | 4字节 | 触摸Y轴中心值 |
| 0x1C | Checksum | 4字节 | CRC32校验 |
加载时先读Magic Number,不对就直接跑校准程序。Checksum用来防止数据损坏。我在一个量产项目里遇到过Flash老化导致校准数据出错,加了CRC校验后,至少能检测到异常,不会让用户点不准。
3.6 总结一下
TouchGFX的校准框架,说白了就是三层:
- 驱动层:
sampleTouch()提供原始ADC值 - 校准层:
TouchCalibrationData保存映射参数,HAL内部做线性变换 - 回调层:
TouchEventCallback把校准后的坐标传给Application