【项目原理】多点触摸屏驱动原理

您所在的位置:网站首页 触摸屏触摸笔原理 【项目原理】多点触摸屏驱动原理

【项目原理】多点触摸屏驱动原理

2024-05-30 17:02| 来源: 网络整理| 查看: 265

一、屏幕介绍

ATK-7016 这款屏幕其实是由 TFT LCD+触摸屏组合起来的。底下是 LCD 面板,上面是触摸面板,将两个封装到一起就成了带有触摸屏的 LCD 屏幕。电容触摸屏也是需要一个驱动 IC的,驱动 IC 一般会提供一个 I2C 接口给主控制器,主控制器可以通过 I2C 接口来读取驱动 IC里面的触摸坐标数据。ATK-7016、ATK-7084 这两款屏幕使用的触摸控制 IC 是 FT5426,ATK-4342 使用的驱动 IC 是 GT9147,ATK-4384 使用的驱动 IC 是 GT1151。这些电容屏触摸 IC 都是 I2C 接口的,使用方法基本一样。

ATK-4384 的电容触摸屏部分有 4 个 IO 用于连接主控制器:SCL、SDA、RST 和 INT,SCL 和 SDA 是 I2C 引脚,RST 是复位引脚,INT 是中断引脚。一般通过 INT 引脚来通知主控制器有触摸点按下,然后在 INT 中断服务函数中读取触摸数据。也可以不使用中断功能,采用轮询的方式不断查询是否有触摸点按下,本章实验我们使用中断方式来获取触摸数据。

二、触摸驱动分析 1、驱动框架分析

按照https://blog.csdn.net/qq_41709234/article/details/128661071的说法:

驱动程序编写主要参考《正点原子开发指南》,在裸机开发中进行触摸屏的驱动,主要流程如下:

  ①、电容触摸屏是IIC接口的,需要触摸 IC,以正点原子的 ATK4384 为例,其所使用的触摸屏控制 IC 为GT1151,因此所谓的电容触摸驱动就是 IIC设备驱动。    ②、触摸IC提供了中断信号引脚(INT),可以通过中断来获取触摸信息。    ③、电容触摸屏得到的是触摸位置绝对信息以及触摸屏是否有按下。    ④、电容触摸屏不需要校准,当然了,这只是理论上的,如果电容触摸屏质量比较差,或者触摸玻璃和 TFT 之间没有完全对齐,那么也是需要校准的。 

那么电容触摸屏的Linux驱动主要需要以下几个驱动框架的组合:

  ①、IIC 设备驱动,因为电容触摸IC基本都是IIC接口的,因此大框架就是IIC设备驱动。    ②、通过中断引脚(INT)向linux内核上报触摸信息,因此需要用到linux中断驱动框架。坐标的上报在中断服务函数中完成。    ③、触摸屏的坐标信息、屏幕按下和抬起信息都属于linux的input子系统,因此向 linux 内核上报触摸屏坐标信息就得使用input子系统。

2、多点触摸(MT)协议详解 

MT 协议隶属于 linux的 input 子系统,驱动通过大量的 ABS_MT 事件向 linux 内核上报多点触摸坐标数据。根据触摸 IC 的不同,分为 Type A 和 Type B 两种类型,目前使用最多的是 Type B 类型。

老版本的 linux 内核是不支持多点电容触摸的(Multi-touch,简称 MT),MT 协议是后面加入的,因此如果使用 2.x 版本 linux 内核的话可能找不到 MT 协议。

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

852 #define ABS_MT_SLOT 0x2f /* MT slot being modified */ 853 #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ 854 #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ 855 #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ 856 #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ 857 #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ 858 #define ABS_MT_POSITION_X 0x35 /* Center X touch position */ 859 #define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ 860 #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ 861 #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ 862 #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ 863 #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ 864 #define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ 865 #define ABS_MT_TOOL_X 0x3c /* Center X tool position */ 866 #define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ 🎍 TypeA

对于 Type A 类型的设备,通过 input_mt_sync()函数来隔离不同的触摸点数据信息:

void input_mt_sync(struct input_dev *dev) 

该函数会触发 SYN_MT_REPORT 事件,此事件会通知接收者获取当前触摸数据,并且准备接收下一个触摸点数据。 

不管是哪个类型的设备,最终都要调用 input_sync()函数来标识多点触摸信息传输完成,告诉接收者处理之前累计的所有消息,并且准备好下一次接收。

对于 Type A 类型的设备,发送触摸点信息的时序如下所示,这里以 2 个触摸点为例:

ABS_MT_POSITION_X x[0]// ABS_MT_POSITION_X 、 ABS_MT_POSITION_Y事件 ABS_MT_POSITION_Y y[0]// input_report_abs来完成坐标上报 SYN_MT_REPORT// input_mt_sync()函数会触发SYN_MT_REPORT事件 ABS_MT_POSITION_X x[1] ABS_MT_POSITION_Y y[1] SYN_MT_REPORT SYN_REPORT

 实例:

static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) { ... // 获取所有触摸点的信息 ret = st1232_ts_read_data(ts); if (ret < 0) goto end; /* multi touch protocol */ for (i = 0; i < MAX_FINGERS; i++) { if (!finger[i].is_valid) continue; // 按照TypeA时序上报所有触摸点的信息 input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);/ ABS_MT_TOUCH_MAJOR常数用于表示触摸接触区域的主轴长度 input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x); input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y); input_mt_sync(input_dev); count++; } /* SYN_REPORT */ input_sync(input_dev); end: return IRQ_HANDLED; } 🎍 TypeB

Type B 使用 slot 协议区分具体的触摸点,Type B 设备驱动需要给每个识别出来的触摸点分配一个 slot,后面使用这个 slot 来上报触摸点信息。可以通过 slot 的 ABS_MT_TRACKING_ID 来新增、替换或删除触摸点。

ABS_MT_TRACKING_ID跟踪ID:一个非负数的 ID 表示一个有效的触摸点,-1 这个 ID 表示未使用 slot。一个以前不存在的 ID 表示这是一个新加的触摸点,一个 ID 如果再也不存在了就表示删除了。

上报触摸点信息的时候需要通过 input_mt_slot()函数区分是哪一个触摸点:

void input_mt_slot(struct input_dev *dev, int slot) 

该函数会触发 ABS_MT_SLOT 事件,此事件会告诉接收者当前正在更新的是哪个触摸点(slot)的数据。 

不管是哪个类型的设备,最终都要调用 input_sync()函数来标识多点触摸信息传输完成,告诉接收者处理之前累计的所有消息,并且准备好下一次接收。

对于 Type B 类型的设备,发送触摸点信息的时序如下所示,这里以 2 个触摸点为例: 

ABS_MT_SLOT 0// 使用 input_mt_slot 函数上报当前触摸点的 ABS_MT_SLOT 事件,触摸IC提供 ABS_MT_TRACKING_ID 45// input_mt_report_slot_state完成 Type B 的要求,即每个 SLOT 必须关联一个ABS_MT_TRACKING_ID ABS_MT_POSITION_X x[0] ABS_MT_POSITION_Y y[0] ABS_MT_SLOT 1 ABS_MT_TRACKING_ID 46 ABS_MT_POSITION_X x[1] ABS_MT_POSITION_Y y[1] SYN_REPORT// 当所有的触摸点坐标都上传完毕以后就得发送 SYN_REPORT 事件,使用 input_sync 函数来完成。 // 当一个触摸点移除以后 ABS_MT_TRACKING_ID -1 // 负1表示这个ID不使用SLOT,同样使用input_mt_report_slot_state函数,第三个参数为false即可,无需手动置为-1 SYN_REPORT// 使用input_sync函数表示上报结束

实例: 

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);// ABS_MT_SLOT事件 finger = &touchdata->finger[i]; touch = touchdata->status & (1 x_low | (finger->x_high y_low | (finger->y_high dev.of_node, "interrupt-gpios", 0); gt1151.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0); // printk("irq_pin=%d, reset_pin=%d\r\n", gt1151.irq_pin, gt1151.reset_pin); /* 复位GT1151 */ ret = gt1151_ts_reset(client, 1151); if(ret < 0) { goto fail; } /* 初始化GT1151 */ data = 0x02; gt1151_write_regs(1151, GT_CTRL_REG, &data, 1); /* 软复位 */ mdelay(100); data = 0x0; gt1151_write_regs(1151, GT_CTRL_REG, &data, 1); /* 停止软复位 */ mdelay(100); /* 初始化GT1151,读取固件 */ // 应该是读触摸设备的信息 ret = gt1151_read_firmware(client, 1151); if(ret != 0) { printk("Fail !!! check !!\r\n"); goto fail; } /* 2、input设备申请与初始化 */ gt1151.input = devm_input_allocate_device(&client->dev); if (!gt1151.input) { ret = -ENOMEM; goto fail; } gt1151.input->name = client->name; gt1151.input->id.bustype = BUS_I2C; gt1151.input->dev.parent = &client->dev; __set_bit(EV_KEY, gt1151.input->evbit);// 支持按键事件 __set_bit(BTN_TOUCH, gt1151.input->keybit);// 设置按键位图为抬起和按下之分的按键 __set_bit(EV_ABS, gt1151.input->evbit);// 支持绝对坐标事件 /* 设置 EV_ABS 事件需要上报 ABS_X、ABS_Y、ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y。 */ input_set_abs_params(gt1151.input, ABS_X, 0, gt1151.max_x, 0, 0); input_set_abs_params(gt1151.input, ABS_Y, 0, gt1151.max_y, 0, 0); input_set_abs_params(gt1151.input, ABS_MT_POSITION_X,0, gt1151.max_x, 0, 0); input_set_abs_params(gt1151.input, ABS_MT_POSITION_Y,0, gt1151.max_y, 0, 0); ret = input_mt_init_slots(gt1151.input, MAX_SUPPORT_POINTS, 0);// 初始化slot if (ret) { goto fail; } /* 注册input_dev */ ret = input_register_device(gt1151.input); if (ret) goto fail; /* 3、最后初始化中断 */ ret = gt1151_ts_irq(client, 1151); if(ret < 0) { goto fail; } return 0; fail: return ret; } 3、上报坐标信息

进入中断处理程序以后首先肯定是从触摸 IC 里面读取触摸坐标以及触摸点数量,假设触摸点数量保存到 num 变量,触摸点坐标存放到 x,y 数组里面。

每一轮触摸点坐标上报完毕以后就调用一次 input_sync 函数发送一个SYN_REPORT 事件。

// 中断服务函数 static irqreturn_t gt1151_irq_handler(int irq, void *dev_id) { int touch_num = 0; int input_x, input_y; int id = 0; int ret = 0; u8 data; u8 touch_data[5]; struct gt1151_dev *dev = dev_id; // printk("enter irq handler!\r\n"); ret = gt1151_read_regs(dev, GT_GSTID_REG, &data, 1);// GT1151当前检测到的触摸情况 if (data == 0x00) { /* 没有触摸数据,直接返回 */ goto fail; } else { /* 统计触摸点数据 */ touch_num = data & 0x0f; } /* 由于GT1151没有硬件检测每个触摸点按下和抬起,因此每个触摸点的抬起和按 * 下不好处理,尝试过一些方法,但是效果都不好,因此这里暂时使用单点触摸 */ if(touch_num) { /* 单点触摸按下 */ gt1151_read_regs(dev, GT_TP1_REG, touch_data, 5); id = touch_data[0] & 0x0F; if(id == 0) { //读取成功 input_x = touch_data[1] | (touch_data[2] input, MT_TOOL_FINGER, true); input_report_abs(dev->input, ABS_MT_POSITION_X, input_x); input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y); } } else if(touch_num == 0){ /* 单点触摸释放 */ input_mt_slot(dev->input, id); input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false);// 删除触摸点 } input_mt_report_pointer_emulation(dev->input, true);// 没有出现硬件检测到的点比上报的触摸点多的情况 input_sync(dev->input); data = 0x00; /* 向0X814E寄存器写0 */ gt1151_write_regs(dev, GT_GSTID_REG, &data, 1); fail: return IRQ_HANDLED; }



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3