title: "Linux 多点电容触摸屏驱动" tags:
| 特性 | 电阻式触摸屏 | 电容式触摸屏 |
|---|---|---|
| 工作原理 | 压力导通两层电阻膜 | 手指感应电场变化 |
| 触摸介质 | 任何硬物(手指/笔) | 仅导体(手指/电容笔) |
| 多点触控 | 不支持(单点) | 支持(多点) |
| 透光率 | 较低(~75%) | 较高(>90%) |
| 使用寿命 | 约10万次 | 约5000万次 |
| 分辨率 | 中等 | 高 |
| 成本 | 低 | 中高 |
| 抗干扰 | 较好 | 易受电磁干扰 |
电阻式触摸屏通过按压使上下两层电阻膜接触产生电压信号,适合工业环境。电容式触摸屏通过检测手指触摸时引起的电容变化来定位坐标,是目前智能手机和平板电脑的主流方案。
触摸屏由专用触摸 IC(如 [[03-FT5426触摸驱动|FT5426]]、GT9147)控制,通过 I2C 接口与主控通信:
┌──────────────┐ I2C总线 ┌──────────────┐
│ 主控 SoC │◄──────────────►│ 触摸 IC │
│ (IMX6ULL) │ │ (FT5426) │
│ │ INT(中断) │ │
│ │◄───────────────│ │
│ │ RST(复位) │ │
│ │───────────────►│ │
└──────────────┘ └──────────────┘
关键信号:
Linux 内核支持两种多点触控协议,定义在 Documentation/input/multi-touch-protocol.txt:
Type A 协议:
SYN_MT_REPORT 分隔每个触摸点的数据Type B 协议(主流):
slot 机制标识每个触摸点ABS_MT_TRACKING_ID 添加/删除触摸点多点触控相关的事件定义在 include/uapi/linux/input.h:
#define ABS_MT_SLOT 0x2f /* 触摸槽位,Type B 使用 */
#define ABS_MT_TOUCH_MAJOR 0x30 /* 触摸椭圆长轴 */
#define ABS_MT_TOUCH_MINOR 0x31 /* 触摸椭圆短轴 */
#define ABS_MT_WIDTH_MAJOR 0x32 /* 接近椭圆长轴 */
#define ABS_MT_WIDTH_MINOR 0x33 /* 接近椭圆短轴 */
#define ABS_MT_ORIENTATION 0x34 /* 椭圆方向 */
#define ABS_MT_POSITION_X 0x35 /* 触摸点 X 坐标 */
#define ABS_MT_POSITION_Y 0x36 /* 触摸点 Y 坐标 */
#define ABS_MT_TOOL_TYPE 0x37 /* 触摸工具类型 */
#define ABS_MT_BLOB_ID 0x38 /* blob 分组 ID */
#define ABS_MT_TRACKING_ID 0x39 /* 触摸点唯一 ID */
#define ABS_MT_PRESSURE 0x3a /* 触摸压力 */
#define ABS_MT_DISTANCE 0x3b /* 悬浮距离 */
#define ABS_MT_TOOL_X 0x3c /* 工具 X 坐标 */
#define ABS_MT_TOOL_Y 0x3d /* 工具 Y 坐标 */
最常用的是 ABS_MT_SLOT、ABS_MT_POSITION_X、ABS_MT_POSITION_Y 和 ABS_MT_TRACKING_ID。
Linux 输入子系统(Input Subsystem)负责管理所有输入设备(键盘、鼠标、触摸屏等),架构分为三层:
┌─────────────────────────────────┐
│ 用户空间 │
│ /dev/input/eventX │
├─────────────────────────────────┤
│ 输入核心层 │
│ drivers/input/input.c │
├────────────┬────────────────────┤
│ 事件处理层 │ 驱动层 │
│ evdev/kbd │ touchscreen/ │
│ 等处理器 │ ft5x06.c │
└────────────┴────────────────────┘
触摸屏驱动处于驱动层,负责将硬件事件转换为输入子系统标准事件上报。
触摸屏坐标上报遵循标准流程:
触摸事件发生
│
▼
中断处理函数被调用
│
▼
通过 I2C 读取触摸坐标
│
▼
调用 input_report_abs() 上报坐标
│
▼
调用 input_sync() 同步数据
│
▼
用户空间从 /dev/input/eventX 读取
EV_SYN // 同步事件,标记一组完整数据
EV_KEY // 按键事件(BTN_TOUCH 表示触摸按下/抬起)
EV_ABS // 绝对坐标事件(触摸坐标)
每次触摸事件上报完毕后,必须调用 input_sync() 发送 SYN_REPORT 事件,告诉内核"这是一组完整的数据,请处理"。内核收到 SYN_REPORT 后才会将数据传递给用户空间。
触摸屏 IC 通常挂载在 I2C 总线下,设备树节点需要包含:
IMX6ULL 的触摸屏中断配置涉及:
interrupt-parent:中断所属 GPIO 控制器interrupts:中断号和触发方式(0 = 下降沿触发)interrupt-gpios:中断 GPIO 引脚描述/* 中断引脚配置 */
pinctrl_tsc: tscgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0xF080 /* TSC_INT */
>;
};
/* 复位引脚配置(位于 iomuxc_snvs 节点下) */
pinctrl_tsc_reset: tsc_reset {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x10B0 /* TSC_RST */
>;
};
&i2c2 {
clock_frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
/* FT5426 触摸屏 IC */
ft5426: ft5426@38 {
compatible = "edt,edt-ft5426";
reg = <0x38>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc &pinctrl_tsc_reset>;
interrupt-parent = <&gpio1>;
interrupts = <9 0>;
reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
};
};
| 属性 | 说明 |
|---|---|
compatible |
用于驱动匹配,需与驱动 of_match_table 一致 |
reg |
I2C 从机地址,FT5426 为 0x38 |
interrupt-parent |
中断引脚所属 GPIO 控制器 |
interrupts |
<GPIO引脚号 触发方式>,0 为下降沿 |
reset-gpios |
复位引脚 GPIO 描述 |
interrupt-gpios |
中断引脚 GPIO 描述 |
gt9147: gt9147@14 {
compatible = "goodix,gt9147", "goodix,gt9xx";
reg = <0x14>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc &pinctrl_tsc_reset>;
interrupt-parent = <&gpio1>;
interrupts = <9 0>;
reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
status = "okay";
};
struct input_dev *input_allocate_device(void);
struct input_dev *devm_input_allocate_device(struct device *dev);
申请 input 设备结构体。devm_ 前缀版本将资源绑定到设备,设备移除时自动释放。
void input_set_abs_params(struct input_dev *dev,
unsigned int code,
int min, int max,
int fuzz, int flat);
设置绝对坐标事件的参数范围。触摸屏需要设置:
ABS_MT_POSITION_X:X 坐标范围(0 ~ 屏幕宽度)ABS_MT_POSITION_Y:Y 坐标范围(0 ~ 屏幕高度)int input_mt_init_slots(struct input_dev *dev,
unsigned int num_slots,
unsigned int flags);
初始化 Type B 多点触控的 slot 数量。num_slots 为最大支持触摸点数,flags 常用值:
INPUT_MT_DIRECT:直接输入设备(触摸屏)INPUT_MT_DROP_UNUSED:丢弃未使用的触摸点void input_mt_slot(struct input_dev *dev, int slot);
Type B 协议专用,设置当前上报的触摸点 slot 编号。触发 ABS_MT_SLOT 事件。
void input_mt_report_slot_state(struct input_dev *dev,
unsigned int tool_type,
bool active);
Type B 协专用,上报 ABS_MT_TRACKING_ID 和 ABS_MT_TOOL_TYPE 事件:
active = true:新增触摸点,内核自动分配 tracking IDactive = false:触摸点抬起,内核自动分配 -1tool_type:通常为 MT_TOOL_FINGERvoid input_report_abs(struct input_dev *dev,
unsigned int code, int value);
上报绝对坐标值,如 ABS_MT_POSITION_X、ABS_MT_POSITION_Y。
void input_sync(struct input_dev *dev);
上报 SYN_REPORT 事件,标记一组完整触摸数据结束。
void input_mt_report_pointer_emulation(struct input_dev *dev,
bool use_count);
兼容单点触摸,上报 BTN_TOOL_TAP 事件供不支持 MT 协议的应用程序使用。
int devm_request_threaded_irq(struct device *dev, int irq,
irq_handler_t handler,
irq_handler_t thread_fn,
unsigned long irqflags,
const char *devname,
void *dev_id);
申请线程化中断,中断处理分为上半部(硬中断)和下半部(内核线程)。触摸屏常用:
IRQF_TRIGGER_FALLING:下降沿触发IRQF_ONESHOT:中断线程运行期间保持中断屏蔽本驱动基于 FT5426 触摸 IC,挂载在 I2C2 总线下,使用 Type B 多点触控协议,最大支持 5 点触摸。
#include <linux/module.h>
#include <linux/ratelimit.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#define MAX_SUPPORT_POINTS 5 /* 5点触摸 */
#define TOUCH_EVENT_DOWN 0x00 /* 按下 */
#define TOUCH_EVENT_UP 0x01 /* 抬起 */
#define TOUCH_EVENT_ON 0x02 /* 接触 */
#define TOUCH_EVENT_RESERVED 0x03 /* 保留 */
/* FT5X06 寄存器定义 */
#define FT5X06_TD_STATUS_REG 0x02 /* 状态寄存器 */
#define FT5x06_DEVICE_MODE_REG 0x00 /* 模式寄存器 */
#define FT5426_IDG_MODE_REG 0xA4 /* 中断模式 */
#define FT5X06_READLEN 29 /* 读取寄存器个数 */
struct ft5x06_dev {
struct device_node *nd; /* 设备节点 */
int irq_pin, reset_pin; /* 中断和复位IO */
int irqnum; /* 中断号 */
void *private_data; /* 私有数据 */
struct input_dev *input; /* input结构体 */
struct i2c_client *client; /* I2C客户端 */
};
static struct ft5x06_dev ft5x06; /* 全局设备实例 */
/* 连续读取多个寄存器 */
static int ft5x06_read_regs(struct ft5x06_dev *dev, u8 reg,
void *val, int len)
{
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = dev->client;
msg[0].addr = client->addr; /* FT5426地址 */
msg[0].flags = 0; /* 标记为写 */
msg[0].buf = ® /* 寄存器首地址 */
msg[0].len = 1;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD; /* 标记为读 */
msg[1].buf = val;
msg[1].len = len;
ret = i2c_transfer(client->adapter, msg, 2);
if (ret == 2)
return 0;
return -EREMOTEIO;
}
/* 连续写入多个寄存器 */
static s32 ft5x06_write_regs(struct ft5x06_dev *dev, u8 reg,
u8 *buf, u8 len)
{
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = dev->client;
b[0] = reg;
memcpy(&b[1], buf, len);
msg.addr = client->addr;
msg.flags = 0;
msg.buf = b;
msg.len = len + 1;
return i2c_transfer(client->adapter, &msg, 1);
}
/* 写单个寄存器 */
static void ft5x06_write_reg(struct ft5x06_dev *dev, u8 reg, u8 data)
{
u8 buf = data;
ft5x06_write_regs(dev, reg, &buf, 1);
}
static int ft5x06_ts_reset(struct i2c_client *client,
struct ft5x06_dev *dev)
{
int ret = 0;
if (gpio_is_valid(dev->reset_pin)) {
/* 申请复位IO,默认输出低电平(复位有效) */
ret = devm_gpio_request_one(&client->dev,
dev->reset_pin,
GPIOF_OUT_INIT_LOW,
"edt-ft5x06 reset");
if (ret)
return ret;
msleep(5); /* 保持复位5ms */
gpio_set_value(dev->reset_pin, 1); /* 释放复位 */
msleep(300); /* 等待芯片启动 */
}
return 0;
}
static irqreturn_t ft5x06_handler(int irq, void *dev_id)
{
struct ft5x06_dev *multidata = dev_id;
u8 rdbuf[29];
int i, type, x, y, id;
int offset, tplen;
int ret;
bool down;
offset = 1; /* 偏移量:0x02+1=0x03开始是触摸值 */
tplen = 6; /* 每个触摸点占6个寄存器 */
memset(rdbuf, 0, sizeof(rdbuf));
/* 从 0x02 寄存器开始连续读取 29 个字节 */
ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG,
rdbuf, FT5X06_READLEN);
if (ret)
goto fail;
/* 上报每一个触摸点坐标 */
for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
u8 *buf = &rdbuf[i * tplen + offset];
/*
* TOUCH1_XH (地址 0x03) 各位描述:
* bit7:6 Event flag 0:按下 1:释放 2:接触 3:无事件
* bit5:4 保留
* bit3:0 X轴触摸点的11~8位
*/
type = buf[0] >> 6; /* 获取触摸类型 */
if (type == TOUCH_EVENT_RESERVED)
continue;
/* 坐标解析(触摸屏与FT5X06方向相反) */
x = ((buf[2] << 8) | buf[3]) & 0x0fff;
y = ((buf[0] << 8) | buf[1]) & 0x0fff;
/*
* TOUCH1_YH (地址 0x05) 各位描述:
* bit7:4 Touch ID,触摸点ID
* bit3:0 Y轴触摸点的11~8位
*/
id = (buf[2] >> 4) & 0x0f;
down = type != TOUCH_EVENT_UP;
/* Type B 协议上报 */
input_mt_slot(multidata->input, id);
input_mt_report_slot_state(multidata->input,
MT_TOOL_FINGER, down);
if (!down)
continue;
input_report_abs(multidata->input, ABS_MT_POSITION_X, x);
input_report_abs(multidata->input, ABS_MT_POSITION_Y, y);
}
input_mt_report_pointer_emulation(multidata->input, true);
input_sync(multidata->input);
fail:
return IRQ_HANDLED;
}
坐标解析详解:
FT5426 每个触摸点占 6 个字节(寄存器 0x03~0x08),布局如下:
字节0: [type(2bit)][reserved(2bit)][X_h(4bit)]
字节1: [X_l(8bit)]
字节2: [id(4bit)][Y_h(4bit)]
字节3: [Y_l(8bit)]
字节4: [weight]
字节5: [misc]
((buf[2] << 8) | buf[3]) & 0x0fff:组合高低字节,取低12位((buf[0] << 8) | buf[1]) & 0x0fff:组合高低字节,取低12位(buf[2] >> 4) & 0x0f:从 YH 字节高4位提取static int ft5x06_ts_irq(struct i2c_client *client,
struct ft5x06_dev *dev)
{
int ret = 0;
/* 申请中断 GPIO */
if (gpio_is_valid(dev->irq_pin)) {
ret = devm_gpio_request_one(&client->dev, dev->irq_pin,
GPIOF_IN, "edt-ft5x06 irq");
if (ret) {
dev_err(&client->dev,
"Failed to request GPIO %d, error %d\n",
dev->irq_pin, ret);
return ret;
}
}
/* 申请线程化中断 */
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, ft5x06_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->name, &ft5x06);
if (ret) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
return ret;
}
return 0;
}
static int ft5x06_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = 0;
ft5x06.client = client;
/* 1. 获取设备树中的中断和复位引脚 */
ft5x06.irq_pin = of_get_named_gpio(client->dev.of_node,
"interrupt-gpios", 0);
ft5x06.reset_pin = of_get_named_gpio(client->dev.of_node,
"reset-gpios", 0);
/* 2. 复位 FT5x06 */
ret = ft5x06_ts_reset(client, &ft5x06);
if (ret < 0)
goto fail;
/* 3. 初始化中断 */
ret = ft5x06_ts_irq(client, &ft5x06);
if (ret < 0)
goto fail;
/* 4. 初始化 FT5X06 寄存器 */
ft5x06_write_reg(&ft5x06, FT5x06_DEVICE_MODE_REG, 0);
ft5x06_write_reg(&ft5x06, FT5426_IDG_MODE_REG, 1);
/* 5. 申请并初始化 input_dev */
ft5x06.input = devm_input_allocate_device(&client->dev);
if (!ft5x06.input) {
ret = -ENOMEM;
goto fail;
}
ft5x06.input->name = client->name;
ft5x06.input->id.bustype = BUS_I2C;
ft5x06.input->dev.parent = &client->dev;
/* 设置事件类型 */
__set_bit(EV_KEY, ft5x06.input->evbit);
__set_bit(EV_ABS, ft5x06.input->evbit);
__set_bit(BTN_TOUCH, ft5x06.input->keybit);
/* 设置坐标范围 */
input_set_abs_params(ft5x06.input, ABS_X, 0, 1024, 0, 0);
input_set_abs_params(ft5x06.input, ABS_Y, 0, 600, 0, 0);
input_set_abs_params(ft5x06.input, ABS_MT_POSITION_X, 0, 1024, 0, 0);
input_set_abs_params(ft5x06.input, ABS_MT_POSITION_Y, 0, 600, 0, 0);
/* 初始化 MT slots */
ret = input_mt_init_slots(ft5x06.input, MAX_SUPPORT_POINTS, 0);
if (ret)
goto fail;
/* 注册 input 设备 */
ret = input_register_device(ft5x06.input);
if (ret)
goto fail;
return 0;
fail:
return ret;
}
/* 传统匹配表 */
static const struct i2c_device_id ft5x06_ts_id[] = {
{ "edt-ft5206", 0 },
{ "edt-ft5426", 0 },
{ /* sentinel */ }
};
/* 设备树匹配表 */
static const struct of_device_id ft5x06_of_match[] = {
{ .compatible = "edt,edt-ft5206" },
{ .compatible = "edt,edt-ft5426" },
{ /* sentinel */ }
};
/* I2C 驱动结构体 */
static struct i2c_driver ft5x06_ts_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "edt_ft5x06",
.of_match_table = of_match_ptr(ft5x06_of_match),
},
.id_table = ft5x06_ts_id,
.probe = ft5x06_ts_probe,
.remove = ft5x06_ts_remove,
};
/* 驱动入口 */
static int __init ft5x06_init(void)
{
return i2c_add_driver(&ft5x06_ts_driver);
}
/* 驱动出口 */
static void __exit ft5x06_exit(void)
{
i2c_del_driver(&ft5x06_ts_driver);
}
module_init(ft5x06_init);
module_exit(ft5x06_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
按下时的上报时序:
ABS_MT_SLOT value=0 ← 选择触摸点0
ABS_MT_TRACKING_ID value=5 ← 分配跟踪ID
ABS_MT_POSITION_X value=1004 ← X坐标
ABS_MT_POSITION_Y value=23 ← Y坐标
BTN_TOUCH value=1 ← 按下
ABS_X value=1004 ← 兼容单点(指针模拟)
ABS_Y value=23 ← 兼容单点(指针模拟)
SYN_REPORT value=0 ← 同步
抬起时的上报时序:
ABS_MT_TRACKING_ID value=-1 ← 释放触摸点
BTN_TOUCH value=0 ← 抬起
SYN_REPORT value=0 ← 同步
五点校准选取屏幕四角和中心共 5 个校准点:
(0,0)─────────────(W,0)
│ │
│ (W/2,H/2) │
│ │
(0,H)─────────────(W,H)
算法步骤:
(rx_i, ry_i) 和期望的 LCD 坐标 (lx_i, ly_i)使用最小二乘法拟合变换矩阵:
lx = A * rx + B * ry + C
ly = D * rx + E * ry + F
求解 6 个系数 A~F
保存校准数据到文件
tslib 将校准数据保存在 /etc/pointercal 文件中:
<A> <B> <C> <D> <E> <F> <screen_width> <screen_height>
运行 ts_calibrate 命令可进行交互式校准,校准完成后自动生成此文件。
# 编译驱动模块
make -j32
# 复制模块到开发板
cp ft5x06.ko /nfs/rootfs/lib/modules/4.1.15/
# 开发板上加载
depmod # 首次加载需执行
modprobe ft5x06.ko # 加载驱动模块
加载成功后会生成 /dev/input/eventX 设备文件。
hexdump /dev/input/event2
触摸屏幕并观察输出,每行数据格式为:
tv_sec tv_usec type code value
02bb 9459 0003 002f 00000000 ← ABS_MT_SLOT 0
02bb 9459 0003 0039 00000005 ← ABS_MT_TRACKING_ID 5
02bb 9459 0003 0035 000003ec ← ABS_MT_POSITION_X 1004
02bb 9459 0003 0036 00000017 ← ABS_MT_POSITION_Y 23
02bb 9459 0001 014a 00000001 ← BTN_TOUCH 按下
02bb 9459 0000 0000 00000000 ← SYN_REPORT
# 安装 tslib 后配置环境变量
export TSLIB_TSDEVICE=/dev/input/event2
export TSLIB_CALIBFILE=/etc/pointercal
export TSLIB_CONFFILE=/etc/ts.conf
export TSLIB_PLUGINDIR=/lib/ts
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
# 校准(电容屏可选)
ts_calibrate
# 多点触摸测试
ts_test_mt
ts_test_mt 提供三种模式:
# 查看输入设备列表
cat /proc/bus/input/devices
# 实时查看事件
evtest /dev/input/event2
# 1. 复制驱动文件
cp ft5x06.c (内核源码)/drivers/input/touchscreen/
# 2. 修改 Makefile
echo "obj-y += ft5x06.o" >> drivers/input/touchscreen/Makefile
# 3. 重新编译内核
make -j32
| 特性 | IMX6ULL | STM32MP1 | RK3568 |
|---|---|---|---|
| I2C 控制器 | I2C1~I2C4 | I2C1~I2C6 | I2C1~I2C5 |
| 设备树格式 | 标准 Linux DT | 标准 Linux DT | 标准 Linux DT |
| 中断控制器 | GIC + GPIO | GIC + GPIO | GIC + GPIO |
| 中断线程化 | devm_request_threaded_irq | 同左 | 同左 |
| 触摸 IC | FT5426/GT9147 | FT5336/GT911 | GT911/FT5426 |
| I2C 频率 | 100K/400K | 100K/400K | 100K/400K/1M |
| 主线内核支持 | 需补丁 | 较好 | 较好 |
触摸屏驱动的核心逻辑(input 子系统 + MT 协议)在各平台间完全通用,差异仅在于:
驱动框架代码可以跨平台复用,只需修改设备树即可适配新平台。
答:Type A 协议适用于硬件无法单独跟踪触摸点的设备,每个触摸点上报后用 SYN_MT_REPORT 分隔,内核需要根据坐标聚类来确定哪些事件属于同一个触摸点,处理开销大且精度低。Type B 协议通过 slot 机制和 ABS_MT_TRACKING_ID 为每个触摸点分配唯一标识,硬件可以跟踪并维护触摸点的生命周期,内核直接按 slot 处理,效率高且准确。现代触摸 IC(如 FT5426、GT911)都支持硬件级别的触摸点跟踪,因此 Type B 成为主流。
devm_request_threaded_irq 而不是 request_irq?答:有三个原因:(1) 触摸屏中断频率高且持续时间长(手指放在屏幕上会不断产生中断),中断线程化后可以设置优先级,避免阻塞高优先级任务;(2) devm_ 前缀版本申请的资源由设备模型自动管理,驱动卸载时无需手动 free_irq,代码更简洁;(3) 触摸数据处理(I2C 读取 + 坐标解析)相对耗时,放在内核线程中可以与进程公平调度,避免长时间占用硬件中断上下文。
input_mt_init_slots 的 flags 参数 INPUT_MT_DIRECT 和 INPUT_MT_POINTER 有什么区别?答:INPUT_MT_DIRECT 表示直接输入设备(如触摸屏),触摸点坐标直接对应屏幕坐标,内核会跳过一些坐标转换逻辑。INPUT_MT_POINTER 表示间接指针设备(如触摸板),触摸点坐标需要映射到屏幕坐标,内核会进行额外的坐标变换和指针模拟。触摸屏应使用 INPUT_MT_DIRECT,触摸板应使用 INPUT_MT_POINTER。
答:FT5426 每个触摸点的 X 和 Y 坐标各占 12 位(最大值 4095),但寄存器以 8 位为单位存储。以 X 坐标为例,高 4 位存储在 TOUCH_XH 寄存器的低 4 位(bit3:0),低 8 位存储在 TOUCH_XL 寄存器。读取时将两个字节组合:(high << 8) | low 得到 16 位值,再 & 0x0fff 取低 12 位得到有效坐标。触摸屏与 IC 方向相反时还需要做坐标映射。
答:步骤:(1) 将驱动文件复制到 drivers/input/touchscreen/;(2) 修改该目录下的 Makefile,添加 obj-y += ft5x06.o(而非 obj-m);(3) 重新编译内核。需要注意:(a) 驱动编译进内核后,设备树中的 compatible 字符串必须与驱动的 of_match_table 匹配;(b) 确保设备树中触摸屏节点的 IO 配置正确且未被其他外设占用;(c) 编译进内核后,触摸屏设备的 event 编号可能与模块方式不同,需重新确认。