3. 原始数据采集:读取触摸控制器寄存器、X/Y坐标原始值、压力值(Z轴)获取
好,咱们进入第三章。上一章我们把硬件接口调通了,I2C 或者 SPI 能正常通信了。但光能通信没用,你得把数据读回来。这一章,我们就来干这件事——读取触摸控制器的原始数据。
说白了,就是去翻触摸芯片的寄存器,把 X 坐标、Y 坐标,还有压力值(也就是 Z 轴)给掏出来。这些原始值,是所有后续校准、滤波、手势识别的基础。你想想看,如果原始数据都是错的,后面再怎么折腾也是白搭。
3.1 触摸控制器的寄存器地图
每个触摸控制器芯片,内部都有一张“寄存器地图”。你得拿着这张地图,才知道去哪里找数据。我习惯先把数据手册里的寄存器表扒下来,整理成下面这样的表格,方便查阅。
以市面上最常见的 FT5x06 系列(比如 FT5206、FT5316)为例,它的寄存器布局大致如下:
| 寄存器地址 (Hex) | 寄存器名称 | 描述 | 有效位 |
|---|---|---|---|
| 0x00 | DEVIDE_MODE | 设备模式(工作/休眠) | 8 bit |
| 0x01 | GESTURE_ID | 手势识别结果(如滑动、双击) | 8 bit |
| 0x02 | TD_STATUS | 触摸点状态(触摸点数) | 8 bit |
| 0x03 | P1_XH | 触摸点1 X坐标高字节 + 事件标志 | 高2位: 事件, 低6位: X[11:8] |
| 0x04 | P1_XL | 触摸点1 X坐标低字节 | 8 bit (X[7:0]) |
| 0x05 | P1_YH | 触摸点1 Y坐标高字节 + 事件标志 | 高2位: 事件, 低6位: Y[11:8] |
| 0x06 | P1_YL | 触摸点1 Y坐标低字节 | 8 bit (Y[7:0]) |
| 0x07 | P1_WEIGHT | 触摸点1 压力值(Z轴) | 8 bit |
| ... | ... | ... | ... |
嗯,这里要注意。很多新手会直接去读 0x03 和 0x04,以为拿到的就是完整的 X 坐标。其实不是。你看 P1_XH 这个寄存器,它的高两位是事件标志,低六位才是 X 坐标的高位数据。你得做位运算,把它们拼起来。
P1_XH 寄存器的值当 X 坐标的高 8 位用,结果触摸点满屏乱跳。查了半天才发现,高两位是事件标志,不是数据。从那以后,我每次读寄存器前,都会把数据手册的位定义再确认一遍。
3.2 读取流程:三步走
读取原始数据的流程,其实不复杂。我总结了一个“三步走”的策略:
- 第一步:查询状态 —— 先读
TD_STATUS寄存器,看看当前有几个触摸点。如果值为 0,说明没人摸屏幕,后面的数据就不用读了。 - 第二步:读取坐标 —— 根据触摸点数,循环读取对应点的
XH/XL/YH/YL寄存器,并拼装成完整的 12 位或 16 位坐标值。 - 第三步:读取压力 —— 读取
WEIGHT寄存器,获取 Z 轴压力值。有些芯片还提供AREA寄存器,表示触摸面积,也可以一并读取。
下面这段代码,是我在实际项目中用过的 FT5x06 读取函数。你可以直接参考:
// 假设 i2c_read(dev_addr, reg_addr, *buffer, len) 已经实现
#define FT5x06_ADDR 0x38
#define REG_TD_STATUS 0x02
#define REG_P1_XH 0x03
typedef struct {
uint16_t x;
uint16_t y;
uint8_t pressure;
uint8_t event; // 0: 按下, 1: 抬起, 2: 持续接触
} TouchPoint_t;
uint8_t ft5x06_read_touch(TouchPoint_t *points, uint8_t max_points) {
uint8_t status;
uint8_t buf[6];
uint8_t touch_count = 0;
// 第一步:读取触摸状态
i2c_read(FT5x06_ADDR, REG_TD_STATUS, &status, 1);
touch_count = status & 0x0F; // 低4位表示触摸点数
if (touch_count == 0 || touch_count > max_points) {
return 0;
}
// 第二步:循环读取每个触摸点
for (uint8_t i = 0; i < touch_count; i++) {
// 每个点占6个寄存器:XH, XL, YH, YL, WEIGHT, AREA
uint8_t reg_base = REG_P1_XH + (i * 6);
i2c_read(FT5x06_ADDR, reg_base, buf, 6);
// 解析事件标志(在XH的高两位)
points[i].event = (buf[0] >> 6) & 0x03;
// 拼装X坐标:高6位 + 低8位
points[i].x = ((uint16_t)(buf[0] & 0x3F) << 8) | buf[1];
// 拼装Y坐标:高6位 + 低8位
points[i].y = ((uint16_t)(buf[2] & 0x3F) << 8) | buf[3];
// 读取压力值(Z轴)
points[i].pressure = buf[4];
}
return touch_count;
}
TD_STATUS 做校验。如果两次读取的触摸点数不一致,说明数据可能被中断污染了,最好丢弃这一帧数据。这在高速触摸场景下尤其重要。
3.3 压力值(Z轴)的获取与意义
压力值,也就是 Z 轴数据,很多人会忽略它。其实它很有用。比如,你可以根据压力大小来判断用户是“轻点”还是“重按”,从而实现不同级别的交互反馈。
不同芯片的压力值读取方式不太一样:
- FT5x06 系列: 直接读
WEIGHT寄存器,值范围 0x00 ~ 0xFF。值越大,表示按压力度越大。 - GT911 系列: 压力值在
Px_WEIGHT寄存器,但它的值受触摸面积影响,不完全是力度。 - resistive 电阻屏: Z 轴压力是通过测量接触电阻计算出来的,需要读取 X+、Y+ 和 X-、Y- 的电压值,再做一次除法运算。
我个人习惯,在调试阶段会把压力值也打印出来。有一次,我发现某个批次的屏幕,压力值始终偏小,导致触摸灵敏度不够。排查下来,是触摸屏的贴合胶层太厚了。你看,一个小小的压力值,能帮你发现硬件问题。
3.4 数据采集的完整流程
把上面这些知识点串起来,整个原始数据采集的流程就清晰了。我画了一张流程图,帮你理清思路:
3.5 常见问题与调试技巧
在实际调试中,你可能会遇到一些“怪现象”。我把自己踩过的坑整理了一下:
- 坐标值始终为 0: 先检查 I2C 通信是否正常。我习惯在初始化后,先读一下芯片的
DEVIDE_MODE或CHIP_ID寄存器,如果能读到预期的值,说明通信没问题。 - 坐标值跳变、不连续: 大概率是位拼装错了。检查一下
XH寄存器的高两位是不是被当成了数据位。另外,注意字节序,有些芯片是大端,有些是小端。 - 压力值始终为 0xFF 或 0x00: 可能是寄存器地址不对。有些芯片的压力值寄存器不在连续地址上,需要单独读取。
- 触摸点数量不对: 检查
TD_STATUS的位定义。有些芯片的低 4 位是触摸点数,但高 4 位可能表示其他状态,记得做掩码操作。
好了,这一章的内容就到这里。原始数据采集是触摸校准的第一步,也是最重要的一步。把这一步做扎实了,后面的校准和滤波才能有的放矢。记住,数据不会骗人,但你的代码可能会。多读、多验、多打印,这是调试的不二法门。