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()。流程是这样的:

  1. 定时器中断触发 → 调用 sampleTouch() 获取原始坐标
  2. HAL层内部调用校准函数,将原始坐标映射为屏幕坐标
  3. 通过回调函数,将屏幕坐标传递给 Application 对象
  4. 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响应的完整链路:

TouchGFX 触摸校准与事件传递流程 触摸硬件(FT5x06等) sampleTouch() 获取原始ADC 校准映射(ADC → 像素坐标) TouchEventCallback 回调 Application 分发到当前Screen UI控件响应触摸事件 说明: 校准数据存储在 TouchCalibrationData结构体中 由HAL层在初始化时加载 注意: 回调函数在中断上下文中执行 不要在里面做耗时操作

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