触摸屏驱动

驱动框架

  • 多点电容触摸芯片的接口,一般都为 I2C 接口,因此驱动主框架肯定是 I2C。

当设备树中触摸 IC的设备节点和驱动匹配以后 。进入probe入口函数。在此函数中初始化触摸 IC,中断和 input 子系统等

  • linux 里面一般都是通过中断来上报触摸点坐标信息,因此需要用到中断框架。

在中断服务程序中上报读取到的坐标信息,根据所使用的多点电容触摸设备类型选择使用 Type A 还是 Type B 时序

  • 触摸屏的坐标信息、屏幕按下和抬起信息都属于 linux 的 input 子系统,因此向 linux 内
    核上报触摸屏坐标信息就得使用 input 子系统 。
  • 在中断处理程序中按照 linux 的 MT 协议上报坐标信息。

input 子系统下的多点电容触摸协议

触摸屏坐标信息时,需要按照MT协议来上报

MT 协议被分为两种类型, TypeA 和 TypeB,这两种类型的区别如下:
Type A:适用于触摸点不能被区分或者追踪,此类型的设备上报原始数据(此类型在实际使用中非常少! 。
Type B:适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过 slot 更新某一个触摸点的信息, FT5426 就属于此类型,一般的多点电容触摸屏 IC 都有此能力

触摸点的信息通过一系列的 ABS_MT 事件(有的资料也叫消息)上报给 linux 内核,只有ABS_MT 事件是用于多点触摸的, 常用的是一下几类

触摸点数据上报时序

  • 第 1 行,上报 ABS_MT_SLOT 事件,也就是触摸点对应的 SLOT。每次上报一个触摸点坐
    标之前要先使用input_mt_slot函数上报当前触摸点SLOT,触摸点的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 轴坐标,使用函数 input_report_abs 来完成。
  • 第 4 行,上报触摸点 0 的 Y 轴坐标,使用函数 input_report_abs 来完成

驱动编写

I2C 驱动框架

当设备树中触摸 IC的设备节点和驱动匹配以后, probe 函数就会执行,可以在此函数中初始化触摸 IC,中断和 input 子系统等。



static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    
}

/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client 	: i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5x06_ts_remove(struct i2c_client *client)
{	
	/* 释放input_dev */
	input_unregister_device(ft5x06.input);
	return 0;
}

/*
 *  传统驱动匹配表
 */ 
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,
};

/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init ft5x06_init(void)
{
	int ret = 0;

	ret = i2c_add_driver(&ft5x06_ts_driver);

	return ret;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
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");

初始化触摸 IC、中断和 input 子系统

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;

/*
 * @description	: 根据i2c协议从FT5X06读取多个寄存器数据
 * @param - dev:  ft5x06设备
 * @param - reg:  要读取的寄存器首地址
 * @param - val:  读取到的数据
 * @param - len:  要读取的数据长度
 * @return 		: 操作结果
 */
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 = (struct i2c_client *)dev->client;

	/* msg[0],第一条写消息,发送要读取的寄存器首地址 */
	msg[0].addr = client->addr;			/* ft5x06地址 */
	msg[0].flags = 0;					/* 标记为发送数据 */
	msg[0].buf = ®					/* 读取的首地址 */
	msg[0].len = 1;						/* reg长度*/

	/* msg[1],第二条读消息,读取寄存器数据  */
	msg[1].addr = client->addr;			/* ft5x06地址 */
	msg[1].flags = I2C_M_RD;			/* 标记为读取数据*/
	msg[1].buf = val;					/* 读取数据缓冲区 */
	msg[1].len = len;					/* 要读取的数据长度*/

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		ret = -EREMOTEIO;
	}
	return ret;
}


/*
  * @description     : i2c驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @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); 		/* FT5426中断模式	*/

	/* 5,input设备注册 
       使用“devm_”前缀的函数申请到的资源可以由系统自动释放,不需要我们手动处理
    */
	ft5x06.input = devm_input_allocate_device(&client->dev);
	if (!ft5x06.input) {
		ret = -ENOMEM;
		goto fail;
	}
    /* 6,初始化input设备 */
	ft5x06.input->name = client->name;
	ft5x06.input->id.bustype = BUS_I2C;
	ft5x06.input->dev.parent = &client->dev;
     /* 7,设置事件类型 */
	__set_bit(EV_KEY, ft5x06.input->evbit);
	__set_bit(EV_ABS, ft5x06.input->evbit);
	__set_bit(BTN_TOUCH, ft5x06.input->keybit);
    /* 7,设置EV_ABS事件上报信息 */
	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);	
    
    /*8, 初始化多点电容触摸的 slots (触摸点)*/
	ret = input_mt_init_slots(ft5x06.input, MAX_SUPPORT_POINTS, 0);
	if (ret) {
		goto fail;
	}
    /*8, 注册input设备*/
	ret = input_register_device(ft5x06.input);
	if (ret)
		goto fail;

	return 0;

fail:
	return ret;
}

上报坐标信息

在中断服务程序中上报读取到的坐标信息,根据所使用的多点电容触摸设备类型选择使用 Type A 还是 Type B 时序。

/*
 * @description     : FT5X06中断服务函数
 * @param - irq 	: 中断号 
 * @param - dev_id	: 设备结构。
 * @return 			: 中断执行结果
 */
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; 	/* 偏移1,也就是0X02+1=0x03,从0X03开始是触摸值 */
	tplen = 6;		/* 一个触摸点有6个寄存器来保存触摸值 */

	memset(rdbuf, 0, sizeof(rdbuf));		/* 清除 */

	/* 读取FT5X06触摸点坐标从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;
        //产生 ABS_MT_SLOT 事件,告诉内核当前上报的是哪个触摸点的坐标数据
		input_mt_slot(multidata->input, id);
        /* 给 slot 关 联 一 个 ABS_MT_TRACKING_ID ,
          ABS_MT_TOOL_TYPE 事 件 指 定 触 摸 类 型 ( 是 笔 还 是 手 指 等 )
        */
		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;

}


 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值