文章目录
1. 硬件接口
- SCL、SDA信号线:IIC接口,读取坐标信息
- INT信号线:检测触摸信号,产生外部中断,通过中断获取触摸信息
- RST信号线:复位触摸芯片
所以Linux下电容触摸屏驱动框架为:i2c驱动框架,中断机制,input子系统,多点触摸协议
2.多点触摸(Multi-touch) 协议
在Linux内核中有一份详细的文档介绍了多点电容触摸协议,位置在Documentation/input/multitouch-protocol.txt ,多点触摸协议又简称MT协议,MT协议又分为TypeA,TypeB
-
TypeA:触摸点不能被区分,即使两次相同的触摸信息,都一律上报,这种类型用得比较少
-
TypeB:触摸点能够被硬件追踪区分,两次相同的触摸数据不上报,而是缓存在slot对象中,且通过slot更新某一个触摸点的信息
本文只讨论TypeB协议,对于TypeAg感兴趣的小伙伴可以查看相关博客
3. 多点触摸事件
触摸点的信息是通过一系列ABS_MT事件上报给Linux内核,只有ABS_MT事件用于多点触摸,ABS_MT 事件定义在文件 include/uapi/linux/input.h 中,相关定义为
#define ABS_RESERVED 0x2e
#define ABS_MT_SLOT 0x2f /* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
#define ABS_MT_POSITION_X 0x35 /* Center X touch position */
#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */
#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
#define ABS_MT_TOOL_X 0x3c /* Center X tool position */
#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
- ABS_MT_SLOT:上报触点ID
- ABS_MT_TRACKING_ID:为触摸点分配ID,用于轨迹跟踪
- ABS_MT_POSITION_X:上报触摸点X轴坐标信息
- ABS_MT_POSITION_Y:上报触摸点Y轴坐标信息
- ABS_MT_TOUCH_MAJOR:上报触摸区域长轴信息(触点椭圆形)
- ABS_MT_WIDTH_MAJOR:上报触摸区域短轴信息(触点椭圆形)
4.TypeB上报时序
ABS_MT_SLOT 0 //上报触摸点序号
ABS_MT_TRACKING_ID 45 //为触摸点分配ID
ABS_MT_POSITION_X x[0] //上报触摸点X轴坐标信息
ABS_MT_POSITION_Y y[0] //上报触摸点Y轴坐标信息
ABS_MT_SLOT 1 //以下同上
ABS_MT_TRACKING_ID 46
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_REPORT //同步事件
-
使用input_mt_slot函数 上报ABS_MT_SLOT事件,即上报对应的触摸点ID,需要触摸IC提供,
-
根据 Type B 的要求,每个 SLOT 必须关联一个 ABS_MT_TRACKING_ID,通过
修改 SLOT 关联的 ABS_MT_TRACKING_ID 来完成对触摸点的添加、替换或删除。具体用到的函数就是 input_mt_report_slot_state,如果是添加一个新的触摸点,那么此函数的第三个参数active 要设置为 true, linux 内核会自动分配一个 ABS_MT_TRACKING_ID 值,不需要用户去指定具体的 ABS_MT_TRACKING_ID 值 -
上报触摸点0的X轴坐标和Y轴坐标,使用函数 input_report_abs 来完成
-
和第 1~4 行类似,只是换成了上报触摸点 1 的(X,Y)坐标信息
-
当所有的触摸点坐标都上传完毕以后就得发送 SYN_REPORT 事件,使用 input_sync
函数来完成
5.使用到的API函数
1.input_mt_init_slots
在编写MT驱动的时候必须先调用此函数初始化slots,input_mt_init_slots()函数,原型为:
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,unsigned int flags)
-
dev:具体输入设备
-
num_slots:设备要使用触摸点数量,由触摸芯片决定
-
flags:触摸输入设备的flags信息
#define INPUT_MT_POINTER 0x0001 /* pointer device, e.g. trackpad */触控板 #define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */触摸屏 #define INPUT_MT_DROP_UNUSED0x0004 /* drop contacts not seen in frame */ #define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */ #define INPUT_MT_SEMI_MT 0x0010 /* semi-mt device, finger count handled manually */
可以采用‘|’运算来同时设置多个 flags 标识
返回值:
-
成功:0
-
失败:负数
2.input_mt_slot 函数
此函数用于产生ABS_MT_SLOT事件,告诉内核上报的是哪个触摸点的坐标数据,即上报触摸点的序号
static inline void input_mt_slot(struct input_dev *dev, int slot)
{
input_event(dev, EV_ABS, ABS_MT_SLOT, slot);
}
参数:
- dev : 具体输入设备
- slot:slot对象的序号,也就是哪个触摸点
返回值:
- 成功:0
- 失败:负数
3.input_mt_report_slot_state 函数
用于产生 ABS_MT_TRACKING_ID 和 ABS_MT_TOOL_TYPE事 件 , 给触摸点分配ID,ABS_MT_TRACKING_ID 事 件 给 slot 关 联 一 个 ABS_MT_TRACKING_ID ,ABS_MT_TOOL_TYPE 事 件 指 定 触 摸 类 型 ( 是 笔 还 是 手 指 等 )。 此 函 数 定 义 在 文 件drivers/input/input-mt.c 中,此函数原型如下所示:
bool input_mt_report_slot_state(struct input_dev *dev,
unsigned int tool_type, bool active)
参数:
-
dev : 具体输入设备
-
tool_type:触摸类型
- MT_TOOL_FINGER:手指
- MT_TOOL_PEN:笔
- MT_TOOL_PALM:手掌
-
active:
- true:连续触摸,动态分配id
- false:触摸点离开,表示触摸点无效,id为-1
4.input_report_abs 函数
Type A 和 Type B 类型都使用此函数上报触摸点坐标信息,通过 ABS_MT_POSITION_X 和ABS_MT_POSITION_Y 事 件 实 现 X 和 Y 轴 坐 标 信 息 上 报 。 此 函 数 定 义 在 文 件include/linux/input.h 中,函数原型如下所示:
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
参数:
-
dev: MT 设备对应的 input_dev。
-
code:要上报的是什么数据,可以设置为 ABS_MT_POSITION_X 或ABS_MT_POSITION_Y,也就是 X 轴或者 Y 轴坐标数据。
-
value: 具体的 X 轴或 Y 轴坐标数据值
5.上报时序案例
drivers/input/touchscreen/ili210x.c
static void ili210x_report_events(struct input_dev *input,
const struct touchdata *touchdata)
{
int i;
bool touch;
unsigned int x, y;
const struct finger *finger;
for (i = 0; i < MAX_TOUCHES; i++) {
input_mt_slot(input, i);
finger = &touchdata->finger[i];
touch = touchdata->status & (1 << i);
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
if (touch) {
x = finger->x_low | (finger->x_high << 8);
y = finger->y_low | (finger->y_high << 8);
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
}
}
input_mt_report_pointer_emulation(input, false);
input_sync(input);
}
使用 for 循环实现上报所有的触摸点坐标,调用 input_mt_slot 函数上 报 ABS_MT_SLOT 事 件 。 用input_mt_report_slot_state 函 数 上 报ABS_MT_TRACKING_ID 事件,也就是给 SLOT 关联一个ABS_MT_TRACKING_ID。使用 input_report_abs 函数上报触摸点对应的(X,Y)坐标值。使用 input_sync 函数上报 SYN_REPORT 事件
6.多点电容触摸屏驱动编写
主要用到以下知识点
①、多点电容触摸芯片的接口,一般都为 I2C 接口,因此驱动主框架肯定是 I2C。
②、 linux 里面一般都是通过中断来上报触摸点坐标信息,因此需要用到中断框架。
③、多点电容触摸属于 input 子系统,因此还要用到 input 子系统框架。
④、在中断处理程序中按照 linux 的 MT 协议上报坐标信息
根据上面的分析,多点电容触摸驱动的框架编写以及步骤为:
1.I2C驱动框架
/* 传统匹配表 */
static const struct i2c_device_id xxx_ts_id[] = {
{ "xxx", 0, },
{ /* sentinel */ }
};
/* 设备树匹配表 */
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx", },
{ /* sentinel */ }
};
/* i2c设备驱动*/
static struct i2c_driver ft5x06_ts_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "xxx",
.of_match_table = of_match_ptr(xxx_of_match),
},
.id_table = xxx_ts_id,
.probe = yyy_probe,
.remove = zzz_remove,
};
/* 模块入口函数 */
static int __init xxx_init(void)
{
int ret = 0;
ret = i2c_add_driver(&xxx_ts_driver);
return ret;
}
2. probe函数
初始化触摸芯片,外部中断,input子系统
static int yyy_probe(struct i2c_client *client, const struct
i2c_device_id *id)
{
struct input_dev *input;
//初始化触摸芯片,iic配置相关寄存器
...
//申请中断
devm_request_threaded_irq(&client->dev, client->irq, NULL,
zzz_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->name, &XXX);
//分配外部输入设备并初步初始化
input = devm_input_allocate_device(&client->dev);
ts->input_dev->name = "Goodix Capacitive TouchScreen";
ts->input_dev->phys = "input/ts";
ts->input_dev->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;
//配置输入设备的事件类型
/* 4,初始化 input 和 MT */
//config
__set_bit(EV_ABS, input->evbit);
__set_bit(BTN_TOUCH, input->keybit);
//单指触摸
input_set_abs_params(input, ABS_X, 0, width, 0, 0);
input_set_abs_params(input, ABS_Y, 0, height, 0, 0);
//多指触摸
input_set_abs_params(input, ABS_MT_POSITION_X,0, width, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,0, height, 0, 0);
input_mt_init_slots(input, MAX_SUPPORT_POINTS,INPUT_MT_DIRECT);
//初始化所有触摸点slot对象
input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
//注册输入设备
input_register_device(input);
...
}
-
设置 input_dev 需要上报的事件为 EV_ABS 和 BTN_TOUCH,因为多点电容屏的触摸坐标为绝对值,因此需要上报 EV_ABS 事件。触摸屏有按下和抬起之分,因此需要上报 BTN_TOUCH 按键。
-
调用 input_set_abs_params 函数设置 EV_ABS 事件需要上报 ABS_X、ABS_Y、
ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y。单点触摸需要上报 ABS_X 和 ABS_Y,对于多点触摸需要上报 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y -
使用
“devm_”
前缀的函数申请到的资源可以由系统自动释放,不需要我们手动处理
3.上报坐标信息
在中断服务程序中上报读取到的坐标信息
static irqreturn_t xxx_handler(int irq, void *dev_id)
{
int num; /* 触摸点数量 */
int x[n], y[n]; /* 保存坐标值 */
/* 1、从触摸芯片获取各个触摸点坐标值 */
......
/* 2、上报每一个触摸点坐标 */
for (i = 0; i < num; i++) {
input_mt_slot(input, id);
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
input_report_abs(input, ABS_MT_POSITION_X, x[i]);
input_report_abs(input, ABS_MT_POSITION_Y, y[i]);
}
......
input_sync(input);
......
return IRQ_HANDLED;
}
-
进入中断处理程序之后先从触摸IC中读取触摸坐标以及触摸点数量,假设触摸点数量保存到 num 变量,触摸点坐标存放到 x, y 数组里面。
-
循环上报某一个触摸点的信息,按照typeB类型的时序进行上报
-
每一轮触摸点坐标上报完毕以后就调用一次 input_sync 函数发送一个
SYN_REPORT 事件