138.多点电容触摸协议基本概念

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					//同步事件
  1. 使用input_mt_slot函数 上报ABS_MT_SLOT事件,即上报对应的触摸点ID,需要触摸IC提供,

  2. 根据 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 值

  3. 上报触摸点0的X轴坐标和Y轴坐标,使用函数 input_report_abs 来完成

  4. 和第 1~4 行类似,只是换成了上报触摸点 1 的(X,Y)坐标信息

  5. 当所有的触摸点坐标都上传完毕以后就得发送 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 事件

猜你喜欢

转载自blog.csdn.net/weixin_43824344/article/details/119866157