1. 触摸屏基础:从物理原理到驱动接口
做触摸驱动这些年,我见过不少工程师一上来就翻 datasheet 调寄存器,结果连触摸屏是电阻还是电容都没搞清楚。嗯,这其实是个坑。今天咱们先把基础打牢,后面写代码时才不会跑偏。
1.1 电阻屏 vs 电容屏:原理与实战差异
先说电阻屏。它的结构很简单——两层透明导电薄膜中间夹着微小的隔离点。你用手指或者任何硬物按下去,上层薄膜就会和下层接触,形成一个分压电路。控制器检测到电压变化,就能算出触摸点的位置。
核心要点:电阻屏只认「压力」,不认「材质」。戴手套、用笔尖、甚至用信用卡都能操作。这在工业设备和医疗仪器上很常见。
我在一个工控项目里遇到过这种情况:现场环境有油污,操作员必须戴厚手套。电容屏根本没法用,最后只能上电阻屏。所以别觉得电阻屏过时了,它有自己的生存空间。
电容屏就完全不一样了。它利用的是人体电场和屏幕表面电极之间的耦合效应。你的手指一靠近,就会吸走一小部分电流,控制器通过检测四个角落的电流变化来定位触摸点。
| 对比项 | 电阻屏 | 电容屏 |
|---|---|---|
| 检测原理 | 压力导致物理接触 | 电场耦合改变电容 |
| 触控介质 | 任何硬物 | 导电物体(手指、电容笔) |
| 多点触摸 | 基本不支持(单点为主) | 原生支持(2~10点) |
| 透光率 | 较低(约80%) | 较高(约90%以上) |
| 耐用性 | 易磨损(薄膜结构) | 坚固(玻璃面板) |
| 典型场景 | 工控、医疗、POS机 | 手机、平板、车载 |
说白了,现在消费电子里几乎全是电容屏。但做驱动适配时,你还是要先确认硬件类型——我见过有人把电阻屏的驱动硬套在电容屏上,结果触摸数据完全乱套。
1.2 多点触摸协议:MT Protocol A 与 B
Linux 内核里,多点触摸协议分两种:Protocol A 和 Protocol B。这个区分很关键,选错了协议,上层应用可能收不到正确的触摸事件。
Protocol A:无状态上报
Protocol A 是早期的方案。每次扫描完所有触摸点后,驱动会一次性上报所有点的坐标。它不区分「这个点是新出现的」还是「之前那个点移动了」。每次上报都是一份完整的快照。
// Protocol A 上报示例
ABS_MT_POSITION_X x1
ABS_MT_POSITION_Y y1
SYN_MT_REPORT
ABS_MT_POSITION_X x2
ABS_MT_POSITION_Y y2
SYN_MT_REPORT
SYN_REPORT
这种方式的优点是实现简单,缺点是数据量较大,而且上层需要自己做「点跟踪」——也就是判断哪个点是哪个。我早期做的一个单点触摸项目用的就是 Protocol A,当时觉得够用,后来升级到多点才发现坑。
Protocol B:带 ID 的跟踪上报
Protocol B 引入了 TRACKING_ID。每个触摸点都有一个唯一的 ID,驱动只上报变化的部分。比如手指没动,就不重复上报坐标;手指抬起了,就上报一个 TRACKING_ID = -1 表示离开。
// Protocol B 上报示例
ABS_MT_TRACKING_ID 45
ABS_MT_POSITION_X x1
ABS_MT_POSITION_Y y1
SYN_MT_REPORT
ABS_MT_TRACKING_ID 46
ABS_MT_POSITION_X x2
ABS_MT_POSITION_Y y2
SYN_MT_REPORT
SYN_REPORT
// 手指抬起时
ABS_MT_TRACKING_ID -1
SYN_MT_REPORT
SYN_REPORT
我的建议:新项目一律用 Protocol B。它更省带宽,上层处理也更方便。除非你的触摸控制器实在太老,不支持硬件 ID 分配,否则别碰 Protocol A。
我曾经在一个平板项目里用了 Protocol A,结果上层手势识别库(比如 libinput)处理多点缩放时总是卡顿。换成 Protocol B 后,问题立刻消失。原因就是 Protocol A 缺少 ID,上层需要花大量 CPU 去做点匹配。
1.3 I2C 与 SPI 接口:选哪个?
触摸屏和主控之间的通信接口,主流就两种:I2C 和 SPI。选哪个,取决于你的场景。
I2C:引脚少,速度够用
I2C 只需要两根线(SCL、SDA),支持多设备挂载。标准模式 100kHz,快速模式 400kHz,高速模式能到 3.4MHz。对于大多数触摸屏(尤其是电容屏),I2C 完全够用。
// I2C 设备树节点示例
touchscreen@38 {
compatible = "goodix,gt911";
reg = <0x38>;
interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
touchscreen-size-x = <1024>;
touchscreen-size-y = <600>;
};
SPI:速度快,适合高刷新率
SPI 需要 4 根线(MOSI、MISO、SCLK、CS),全双工通信,速度轻松上几十 MHz。如果你的触摸屏需要高报点率(比如 200Hz 以上),或者分辨率特别高(比如 4K 屏),SPI 是更好的选择。
// SPI 设备树节点示例
touchscreen@0 {
compatible = "ilitek,ili2511";
reg = <0>;
spi-max-frequency = <10000000>;
interrupt-parent = <&gpio2>;
interrupts = <10 IRQ_TYPE_EDGE_RISING>;
touchscreen-size-x = <1920>;
touchscreen-size-y = <1080>;
};
| 接口 | 引脚数 | 速度 | 典型场景 |
|---|---|---|---|
| I2C | 2 | 100kHz~3.4MHz | 手机、平板、车载 |
| SPI | 4 | 10MHz~50MHz | 高刷触摸屏、大屏 |
注意:I2C 的地址冲突是个常见坑。多个 I2C 设备挂在同一总线上时,地址不能重复。我遇到过一块板子上触摸屏和传感器用了同一个地址,结果两个设备都工作异常。排查了整整一天才找到原因。
1.4 本章知识体系总览
下面这张图把本章的核心知识点串起来了。你可以把它当作一个快速索引,后面写驱动时随时回来对照。
1.5 避坑指南:我踩过的几个坑
最后分享几个实战中容易忽略的点:
- 中断引脚配置错误:触摸屏的中断触发方式(上升沿、下降沿、电平触发)必须和控制器匹配。我曾经把下降沿触发配成了上升沿,结果触摸屏完全没反应。查了两天才发现是设备树里
IRQ_TYPE_EDGE_FALLING写成了IRQ_TYPE_EDGE_RISING。 - I2C 时序不匹配:有些触摸控制器对 I2C 时钟频率很敏感。我遇到过一块屏在 400kHz 下工作正常,换到 100kHz 反而丢数据。后来发现是控制器的内部 FIFO 在低速下超时了。
- Protocol B 的 ID 管理:如果触摸控制器不支持硬件 ID 分配,驱动里要自己维护 ID 池。我见过一个驱动直接把
TRACKING_ID设成固定值,结果多点触摸时所有点都认为是同一个手指。
做驱动适配时,先看芯片手册里的「Protocol」章节。如果手册提到 TRACKING_ID 或 CONTACT_ID,那大概率支持 Protocol B。如果只提坐标上报,那多半是 Protocol A。
好了,这一章的基础知识就到这里。记住:触摸屏驱动适配的核心不是写代码,而是理解硬件怎么工作、协议怎么通信。后面几章我们会一步步把这些知识落地到实际的驱动代码里。