触摸屏驱动

news2024/10/6 6:52:54

驱动框架

  • 多点电容触摸芯片的接口,一般都为 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;

}


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1060335.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PTrade获取交易日期——时间相关函数1

本系列文章将笔者平时在使用ptrade进行策略开发中使用到的与日期相关的函数进行记录&#xff0c;以便大家参考并一同改进优化。 如果读者还有其他日期相关的函数实现需求&#xff0c;也可以留言&#xff0c;大家一起讨论补充。 首先看一下ptrade提供的get_trading_day函数的用…

vivado杂项记录

文章目录 问题的解决Xilinx Vitis 启动时未响应的解决方法Spawn failed&#xff1a;No error错误vivado卡在Initializing Language Server的解决方法vivado中添加文件后出现non-module 其他关于MAX_FANOUT属性vviado 2018.3中IP的core container 问题的解决 Xilinx Vitis 启动…

typescript 分析泛型工具类Partial的实现原理理解索引查询类型

Partial实现原理 在 TypeScript 中&#xff0c;Partial 是一个非常有用的工具类型&#xff0c;它能够将一个对象类型的所有属性变为可选。Partial 的实现原理是通过使用映射类型&#xff08;Mapped Type&#xff09;和 keyof 关键字来实现的。 下面我们来看一下 Partial 的实现…

System Generator初体验FIR滤波器

文章目录 前言一、介绍1、目标2、过程 二、步骤 1&#xff1a;在 FPGA 中创建设计1、打开 Lab1_1.slx 文件2、运行仿真3、使用 System Generator 创建 FIR 滤波器<1>、从库浏览器寻找需要的模块<2>、配置系统生成器块<3>、在 FPGA 上实现设计 三、步骤 2&…

第一章 函数 极限 连续(解题方法须背诵)

&#xff08;一&#xff09;求极限的常用方法 方法1 利用有理运算法则求极限 方法2 利用基本极限求极限 方法3 利用等价无穷小求极限 方法4 利用洛必达法则求极限 方法5 利用泰勒公式求极限 方法6 利用夹逼准则求极限 方法7 利用定积分的定义求极限 方法8 利用单调有界…

JMeter界面和字体的调整

一、界面直接调整 二、调整界面比例&#xff08;分辨率&#xff09; jmeter.hidpi.modetrue jmeter.hidpi.scale.factor2.0用记事本打开 将如下代码中的第一行和第五行的注释去掉即可 #jmeter.hidpi.modetrue # HiDPI scale factor #jmeter.hidpi.scale.factor1.0 # Suggeste…

sheng的学习笔记-【中文】【吴恩达课后测验】Course 1 - 神经网络和深度学习 - 第三周测验

课程1_第3周_测验题 目录&#xff1a;目录 第一题 1.以下哪一项是正确的&#xff1f; A. 【  】 a [ 2 ] ( 12 ) a^{[2](12)} a[2](12)是第12层&#xff0c;第2个训练数据的激活向量。 B. 【  】X是一个矩阵&#xff0c;其中每个列都是一个训练示例。 C. 【  】 a 4 […

互联网Java工程师面试题·MyBatis 篇·第一弹

目录 1、什么是 Mybatis&#xff1f; 2、Mybaits 的优点 3、MyBatis 框架的缺点 4、MyBatis 框架适用场合 5、MyBatis 与 Hibernate 有哪些不同&#xff1f; 6、#{}和${}的区别是什么&#xff1f; 7、当实体类中的属性名和表中的字段名不一样 &#xff0c;怎么办 &#x…

ERROR 6400 --- [ main] com.zaxxer.hikari.pool.HikariPool : root - Exception

在引用的日志中&#xff0c;报告了Hikari连接池初始化期间的异常。具体异常信息是"Exception during pool initialization"。这个异常可能是由于与MySQL数据库的通信链接失败导致的。在引用中也提到了与SSL连接相关的错误。 根据引用中提供的代码&#xff0c;可以看到…

docker方式启动一个java项目-Nginx本地有代码,并配置反向代理

文章目录 案例导入说明1.安装MySQL1.1.准备目录1.2.运行命令1.3.修改配置1.4.重启 2.导入SQL3.导入Demo工程3.1.分页查询商品&#xff08;仔细看代码&#xff0c;很多新的MP编程技巧&#xff09;3.2.新增商品3.3.修改商品3.4.修改库存3.5.删除商品3.6.根据id查询商品3.7.根据id…

html 高性能 简易轮播图

目标 实现简易轮播图动画效果 设计理念 无论有多少个轮播图&#xff0c;仅使用常数个轮播图tab&#xff0c;通过js替换更新dom内容&#xff0c;实现性能优化&#xff1b;使用bfc避免回流&#xff0c;&#xff08;重绘是基本上无法避免&#xff0c;不在考虑&#xff09;&#…

CompletableFuture异步回调

CompletableFuture异步回调 CompletableFutureFuture模式CompletableFuture详解1.CompletableFuture的UML类关系2.CompletionStage接口3.使用runAsync和supplyAcync创建子任务4.设置子任务回调钩子5.调用handle()方法统一处理异常和结果6.线程池的使用 异步任务的串行执行thenA…

【做题笔记】多项式/FFT/NTT

HDU1402 - A * B Problem Plus 题目链接 大数乘法是多项式的基础应用&#xff0c;其原理是将多项式 f ( x ) a 0 a 1 x a 2 x 2 a 3 x 3 ⋯ a n x n f(x)a_0a_1xa_2x^2a_3x^3\cdotsa_nx^n f(x)a0​a1​xa2​x2a3​x3⋯an​xn中的 x 10 x10 x10&#xff0c;然后让大数的…

20230922 比赛总结

反思 A 考场降智&#xff0c;没想到拆分成 2 α 5 β x 2^{\alpha}5^{\beta}x 2α5βx 的形式&#xff0c;一直在卡精度&#xff08;thx anti) B 又是随机题&#xff0c;又是 b l bl bl 题&#xff0c;差点又被区分了 C 我是 s b sb sb&#xff0c;排序顺序有点小问题…

concrt140.dll丢失怎么恢复?教你三种最简单的解决方法

今天早上&#xff0c;当我打开电脑时&#xff0c;突然看到一个提示窗口&#xff0c;显示找不到 concrt140.dll 文件。我一下子懵了&#xff0c;不知道这是怎么回事&#xff0c;也不知道如何解决这个问题。于是&#xff0c;我开始了寻找答案的旅程。首先&#xff0c;我了解到 co…

深入理解浏览器渲染原理

文章目录 浏览器是如何渲染页面的渲染流程解析HTML&#xff08;构建DOM树&#xff09;解析过程中遇到JS代码 样式计算1. 解析CSS代码2. 转换样式表中的属性值&#xff0c;使其标准化3. 计算DOM树中每个节点的具体样式CSS继承规则CSS层叠规则 布局分层分层update layer tree 绘制…

博物馆藏品管理系统-美术馆藏品管理系统

一、项目背景 文物作为前史留存下来最为珍贵的遗物&#xff0c;具有非常高的科学价值和艺术价值&#xff0c;博物馆的存在便是为了保存这些珍贵的前史文化遗产&#xff0c;所以对博物馆的建造必定要重视品质问题。对博物馆的库存办理工作也必定要注意细节&#xff0c;不能出一…

【LeetCode热题100】--20.有效的括号

20.有效的括号 使用栈&#xff1a; class Solution {public boolean isValid(String s) {Stack<Character> stack new Stack<>();int num s.length();for(int i 0;i<num;i){char c s.charAt(i);if(c(||c[||c{){stack.push(c);}else if(stack.isEmpty() ||c…

矩阵求导中的分子布局和分母布局

1.求偏导的自变量的符号区别 使用标量、向量和矩阵总共有九种可能性。请注意&#xff0c;当我们考虑每个自变量和因变量中更多数量的分量时&#xff0c;我们可能会留下非常多的可能性。下表收集了最能以矩阵形式最整齐地组织的六种导数。 在这里&#xff0c;我们使用了最一般…

AI配套的技术: 矢量数据库的概念

一、说明 随着人工智能的快速采用和围绕大型语言模型发生的创新&#xff0c;我们需要在所有这些的中心&#xff0c;能够获取大量数据&#xff0c;将其上下文化&#xff0c;处理它&#xff0c;并使其能够有意义地搜索。 为原生整合生成式 AI 功能而构建的生成式 AI 流程和应用程…