4. 触摸设备驱动
触摸屏在Linux系统中并不是一个“单一设备”,而是由触摸控制器(硬件)→ 总线接口(I2C/SPI/USB)→ 控制器驱动 → 输入子系统(input subsystem)→ 事件节点(/dev/input/eventX)逐层向上抽象而成的完整链路。本章聚焦最贴近硬件的一层:触摸设备驱动。理解这一层,才能解释为什么“屏幕能亮但触摸没反应”“坐标偏移”“单点正常多点丢失”等典型问题。
一、核心原理:三类总线接口的驱动模型
触摸控制器与SoC之间的物理连接方式决定了驱动形态。I2C最常见,SPI用于高刷新率或大尺寸面板,USB则多见于外接触摸屏或一体机。
- I2C触摸驱动:控制器作为I2C从设备,驱动通过
i2c_driver注册,使用regmap或i2c_smbus_read_i2c_block_data读取坐标寄存器。优点是引脚少、协议简单,缺点是带宽有限,多点高帧率时容易成为瓶颈。 - SPI触摸驱动:通过
spi_driver注册,使用spi_sync传输。SPI没有地址概念,靠CS片选区分设备,适合需要高速上报的场合,但中断脚和复位脚必须正确配置。 - USB触摸驱动:通常遵循HID协议,由
usbhid或hid-multitouch统一处理,厂商私有协议则需编写usb_driver。USB设备支持热插拔,枚举信息可通过lsusb -v查看。
无论哪种总线,最终都要调用输入子系统的上报接口:input_report_abs()上报坐标,input_report_key()上报BTN_TOUCH,最后input_sync()提交一帧。缺少input_sync()会导致事件被缓存而不触发。
二、设备树配置:把硬件描述交给内核
现代Linux驱动要求硬件资源通过设备树(Device Tree)描述,而不是硬编码。一个典型的I2C触摸节点如下:
&i2c3 {
status = "okay";
clock-frequency = <400000>;
touchscreen@38 {
compatible = "edt,edt-ft5406";
reg = <0x38>;
interrupt-parent = <&gpio1>;
interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
vcc-supply = <&vdd_3v3_tp>;
touchscreen-size-x = <1024>;
touchscreen-size-y = <600>;
touchscreen-inverted-x;
};
};
关键属性包括:compatible用于匹配驱动,reg是I2C地址,interrupts描述中断触发方式,reset-gpios控制复位时序,touchscreen-size-x/y定义坐标范围。若这些属性缺失或写错,驱动可能加载成功但无法上报有效坐标。
dtc -I fs /proc/device-tree 反编译当前生效的设备树,确认内核实际看到的内容,而不是只看源码。三、中断处理与上报事件流程
触摸驱动通常采用“中断+工作队列”模型:中断上下文只做最少的事,把读取坐标的耗时操作放到线程化中断或工作队列中,避免阻塞系统。
static irqreturn_t ts_irq_handler(int irq, void *dev_id)
{
struct ts_data *ts = dev_id;
/* 中断上下文:仅标记并唤醒 */
disable_irq_nosync(irq);
schedule_work(&ts->work);
return IRQ_HANDLED;
}
static void ts_work_handler(struct work_struct *work)
{
struct ts_data *ts = container_of(work, struct ts_data, work);
ts_read_coordinates(ts); /* I2C/SPI 读取 */
input_report_abs(ts->input, ABS_MT_POSITION_X, ts->x);
input_report_abs(ts->input, ABS_MT_POSITION_Y, ts->y);
input_report_key(ts->input, BTN_TOUCH, 1);
input_sync(ts->input); /* 提交一帧 */
enable_irq(ts->irq);
}
上报多点触摸时,必须使用MT协议:先input_mt_slot()选择槽位,再上报ABS_MT_TRACKING_ID和坐标,最后input_mt_sync_frame()与input_sync()。漏掉TRACKING_ID会导致手指抬起后槽位无法释放。
四、调试技巧与典型案例
触摸驱动调试有一套成熟工具链,建议按“总线→中断→事件”顺序排查:
| 排查层次 | 工具/命令 | 判断依据 |
|---|---|---|
| I2C通信 | i2cdetect -y 3 |
地址0x38是否出现,无则查上拉/供电 |
| 驱动加载 | dmesg | grep -i touch |
probe是否成功,有无-EPROBE_DEFER |
| 中断触发 | cat /proc/interrupts |
触摸时中断计数是否增长 |
| 事件上报 | evtest /dev/input/event2 |
是否输出ABS_MT_POSITION_X/Y |
| 坐标校准 | libinput debug-events |
坐标范围与屏幕分辨率是否匹配 |
touchscreen-size-x/y而不检查驱动是否读取该属性;或中断触发方式写成IRQ_TYPE_LEVEL_HIGH而硬件实际是下降沿,导致中断风暴或完全无中断。典型案例:某平板触摸无反应,i2cdetect能看到0x38,但/proc/interrupts中触摸中断计数始终为0。最终发现设备树中interrupt-parent指向了错误的GPIO控制器,中断被路由到无效引脚。修正后中断正常,evtest立即输出坐标事件。
掌握“总线通信—中断触发—事件上报”这条主线,再配合设备树与调试工具,绝大多数触摸驱动问题都能被快速定位。驱动层的稳定,是上层图形栈与手势识别可靠工作的前提。