电容触摸屏驱动其实就是以下几种 linux 驱动框架的组合:
1,IIC 设备驱动,因为电容触摸 IC 基本都是 IIC 接口的,因此大框架就是 IIC 设备驱动。
2,通过中断引脚(INT)向 linux 内核上报触摸信息,因此需要用到 linux 中断驱动框架。坐标的上报在中断服务函数中完成。
3,触摸屏的坐标信息、屏幕按下和抬起信息都属于 linux 的 input 子系统,因此向 linux 内核上报触摸屏坐标信息就得使用 input 子系统。只是,我们得按照 linux 内核规定的规则来上报坐标信息。
我们应该已经了解了IIC驱动,中断驱动还有input子系统,唯一不太清楚的可能就是在input子系统里面如何上报多点电容触摸的信息。这是跟多点触摸协议相关的。
老版本的linux内核是不支持多点电容触摸的(Multi-Touch),简称MT,是后面的版本新加上去的。MT协议被分为两种类型,TypeA和TypeB,两种类型区别如下:
TypeA:适用于触摸点不能被区分或者追踪,此类型的设备上报原始数据(实际应用中很少)
TypeB:适用于有硬件追踪并能区分触摸触摸点的触摸设备,此类型设备通过slot更新某一个触摸点信息,FT5426就属于此类型,一般的多点电容触摸IC都有此能力。
触摸点信息通过一系列的ABS_MT事件上报给linux内核,只有ABS_MT事件用于多点触摸的,ADS_MT事件如下:
在上面众多AMS_MT事件中,我们最常用的是AMS_MT_SLOT,AMS_MT_POSITION_X,AMS_MT_POSITION_Y和AMS_MT_TRACKING_ID。其中ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y 用 来 上报 触 摸点 的 (X,Y) 坐 标 信息 , ABS_MT_SLOT 用 来 上 报 触 摸 点 ID ,对于 Type B 类 型 的 设 备 ,需 要 用 到ABS_MT_TRACKING_ID 事件来区分触摸点。
对于TypeA类型的设备,通过input_mt_sync函数来隔离不同的触摸点数据信息,此函数原型如下所示:
此函数只有一个参数,类型为input_dev,用于指定具体的input_dev设备,input_mt_sync函数会触发SYN_MT_REPORT事件,此事件会通知接受者获取当前触摸数据,并准备接收下一个触摸点信息。
对于TypeB类型的设备,上报触摸点信息的时候需要通过input_mt_slot函数区分是哪一个触摸点,函数原型如下:
第二个参数slot用于指定当前上报的是哪一个触摸点的信息,input_mt_slot函数会触发ABS_MT_SLOT事件,此事件会告诉接收者当前更新的是哪个触摸点(slot)的数据。
不管那种类型的设备,最终都需要调用input_sync函数来标识多点触摸信息传输完成,告诉接收者处理之前累计的所有信息,并准备好下一次接收。Type B 和 Type A 相比最大的区别就是 Type B 可以区分出触摸点, 因此可以减少发送到用户空间的数据。Type B 使用 slot 协议区分具体的触摸点,slot需要用到AMS_MT_TRACKING_ID消息,这个ID需要硬件提供,或者通过原始数据计算出来。
Type B 设备驱动需要给每个识别出来的触摸点分配一个 slot,后面使用这个slot来上报触摸点信息。可以通过slot的ABS_MT_TRACKING_ID来新增,替换,或者删除触摸点,一个非负值的ID表示一个有效的触摸点,-1这个ID表示未使用slot。一个以前不存在的ID表示这是一个新增加的触摸点,一个ID如果再也不存在了就表示删除了。
有些设备识别或追踪的触摸点信息要比他上报的多(跟相应的设备有关),这些设备驱动应该给硬件上报的每个触摸点分配一个 Type B 的 slot。一旦检测到某一个 slot 关联的触摸点 ID 发生了变化,驱动就 应该改变这个 slot 的 ABS_MT_TRACKING_ID,使这个 slot 失效。如果硬件设备追踪到了比他正在上报的还要多的触摸点,那么驱动程序应该发送 BTN_TOOL_*TAP 消息,并且调用input_mt_report_pointer_emulation函数,将此函数的第二个参数 use_count 设置为 false。
TypeA触摸点信息上报时序
对于TypeA类型的设备,发送触摸点信息的时序如下,这里以两个触摸点为例:
第 1 行, ABS_MT_POSITION_X 事件上报第一个触摸点的 X 坐标数据,通过 input_report_abs 函数实现,下面同理。
第 7 行,上报 SYN_REPORT 事件,通过调用 input_sync 函数实现。这个是上报完每一轮触摸点信息就调用一次,比如上报3个触摸点信息,将3个触摸点信息上报完在调用这个就好。也就是发送一个SYN_REPORT事件。
Type B 触摸点信息上报时序
对于Type B 类型的设备,发送触摸点信息的时序如下所示,这里以 2 个触摸点为例:
第1行,上报ABS_MT_SLOT事件,也就是触摸点对应的SLOT,每次上报一个触摸点坐标之前要先使用input_mt_slot函数上报当前触摸点SLOT,触摸点SLOT其实就是触摸点ID,由硬件提供。
第2行,根据TypeB的要求,每个SLOT必须关联一个AMS_MT_TRACKING_ID来完成对触摸点的增加,替换,或删除。具体用到的函数就是input_mt_solt_state,如果添加一个新的触摸点,那么此函数的第三个参数active要设置为true,,linux 内核会自动分配一个 ABS_MT_TRACKING_ID 值,不需要用户去指定具体的 ABS_MT_TRACKING_ID 值。
第 3 行,上报触摸点 0 的 X 轴坐标,使用函数 input_report_abs 来完成。
当一个触摸点移除以后,同样需要通过 SLOT 关联的 ABS_MT_TRACKING_ID 来处理,需要通过ABS_MT_TRACKING_ID事件发送一个-1给内核,方法很简单,同样使用input_mt_report_slot_state函数来完成,只需要将第三个参数active设置为false即可,不需要手动设置为-1,时序如下所示:
//在中断处理函数里面,我们需要将读取到的数据上报,但是需要按照上报时序来完成
//主要看硬件可以是几点触发器,触发就循环多少次
//下面是一个简略的中断处理函数,可以大致看出上报流程
static irqreturn_t ft5x06_handler(int irq, void *dev_id)
{
/* 读取FT5X06触摸点坐标从0X02寄存器开始,连续读取29个寄存器 */
ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG, rdbuf, FT5X06_READLEN);
/* 上报每一个触摸点坐标 */
for (i = 0; i < 5; 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;
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);
}
//这个函数就是硬件的追踪的点比上报的多的话就为false,当不是的话就位ture
input_mt_report_pointer_emulation(multidata->input, true);
//在5点信息全部上报完的时候,再执行下面的函数,表示本次信息全部上报完成
input_sync(multidata->input);
}
dev:MT设备对应的input_dev,因为MT设备隶属于input_dev
num_slots:设备要使用slot数量,也就是触摸点的数量
flags:其他一些flags信息,可设置如下所示:
可以采样‘|’运算来同时设置多个flags标识
返回值:0,成功;负值,失败。
2,input_mt_slot函数
此函数用于TypeB类型,此函数用于产生ABS_MT_SLOT事件,告诉内核当前上报的是哪个触摸点的坐标数据。
dev:MT设备对应的input_dev
slot:当前发送的是哪个slot的坐标信息,也就是那个触摸点,那就是硬件发送过来的信息ID
3,input_mt_report_slot_state函数
此函数用于TypeB类型,用于产生ABS_MT_TRACKING_ID 和 ABS_MT_TOOL_TYPE事件给slot关联一个ABS_MT_TRACKING_ID ,ABS_MT_TOOL_TYPE 事 件 指 定 触 摸 类 型 ( 是 笔 还 是 手 指 等 )。函数原型如下:
dev:MT设备对应的input_dev。
tool_type:触摸类型,可以选择MT_TOOL_FINGER(手指)、MT_TOOL_PEN(笔)或MT_TOOL_PALM(手掌),对于多点电容触摸屏来说一般都是手指。
active:ture,连续触摸,input子系统内核汇自动分配一个ABS_MT_TRACKING_ID给slot。false,触摸点抬起。表示某个触摸点无效了,input子系统就会给内核分配一个-1给slot,表示触摸点移除。
4,input_report_abs函数
TypeA 和 Type B 类型都使用此函数上报触摸点坐标信息,通过ABS_MT_POSITION_X 和ABS_MT_POSITION_Y 事 件 实 现 X 和 Y 轴 坐 标 信 息 上 报 。函数定义如下:
dev:mt设备对应的input_dev
code:要上报的是什么数据,要设置为ABS_MT_POSITION_X或 ABS_MT_POSITION_Y,也就是X轴或者Y轴坐标点数据。
value:具体的X轴和Y轴坐标点数值
5,input_mt_report_pointer_emulation函数
如果追踪到的触摸点数量多于当前上报的数量,驱动程序使用 BTN_TOOL_TAP 事件来通知用户空间当前追踪到的触摸点总数量,然后调用 input_mt_report_pointer_emulation 函数将 use_count 参数设置为 false。否则的话将 use_count 参数设置为 true,表示当前的触摸点数量(此函数会获取到具体的触摸点数量,不需要用户给出)。
dev:MT设备对应的input_dev
use_count:true,有效的触摸点数量;flase,追踪到的触摸点数量多于当前上报的数量
多点电容触摸驱动框架
多点电容需要用到那些框架呢?我们需要注意以下几点:
- 多点电容触摸芯片的接口,一般都为I2C接口,因此驱动主框架肯定是I2C
- linux里面一般都是通过中断来上报触摸点坐标信息的,因此需要中断框架
- 多点电容触摸属于input子系统,因此还要用到input子系统框架
- 在中断处理程序中按照linux的MT协议上报坐标信息