【正点原子Linux连载】 第四十二章 多点电容触摸屏实验摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

news2024/12/28 4:14:31

1)实验平台:正点原子ATK-DLRK3568开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=731866264428
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban

第四十二章 多点电容触摸屏实验

随着智能手机的发展,电容触摸屏也得到了飞速的发展。相比电阻触摸屏,电容触摸屏有很多的优势,比如支持多点触控、不需要按压,只需要轻轻触摸就有反应。ALIENTEK的三款MIPI屏幕都支持多点电容触摸(MIPI 5.5英寸 LCD 720x1280 IPS屏幕,MIPI 5.5英寸 LCD 1080x1920 IPS屏幕,MIPI 10.1英寸 LCD 800x1280 IPS屏幕),本章就以MIPI 5.5英寸 LCD 1080x1920 IPS屏幕为例讲解一下如何驱动电容触摸屏,并获取对应的触摸坐标值。注,本实验编写了驱动仅供实验,若做产品以提供SDK里的触摸驱动为准,因为本实验只写了单点触摸。

42.1 Linux下电容触摸屏驱动框架简介
42.1.1多点触摸(MT)协议详解
电容触摸驱动的基本原理进行了详细的讲解,回顾一下几个重要的知识点:
①、电容触摸屏是IIC接口的,需要触摸IC,以正点原子的MIPI 5.5英寸 LCD 1080x1920 IPS屏幕为例,其所使用的触摸屏控制IC为gt911,因此所谓的电容触摸驱动就是IIC设备驱动。
②、触摸IC提供了中断信号引脚(INT),可以通过中断来获取触摸信息。
③、电容触摸屏得到的是触摸位置绝对信息以及触摸屏是否有按下。
④、电容触摸屏不需要校准,当然了,这只是理论上的,如果电容触摸屏质量比较差,或者触摸玻璃和TFT之间没有完全对齐,那么也是需要校准的。
根据以上几个知识点,我们可以得出电容触摸屏驱动其实就是以下几种linux驱动框架的组合:
①、IIC设备驱动,因为电容触摸IC基本都是IIC接口的,因此大框架就是IIC设备驱动。
②、通过中断引脚(INT)向linux内核上报触摸信息,因此需要用到linux中断驱动框架。坐标的上报在中断服务函数中完成。
③、触摸屏的坐标信息、屏幕按下和抬起信息都属于linux的input子系统,因此向linux内核上报触摸屏坐标信息就得使用input子系统。只是,我们得按照linux内核规定的规则来上报坐标信息。
经过简单的分析,我们发现IIC驱动、中断驱动、input子系统我们都已经在前面学过了,唯独没学过的就是input子系统下的多点电容触摸协议,这个才是我们本章学习的重点。linux内核中有一份文档详细的讲解了多点电容触摸屏协议,文档路径为:Documentation/input/multi-touch-protocol.rst。
老版本的linux内核是不支持多点电容触摸的(Multi-touch,简称MT),MT协议是后面加入的,因此如果使用2.x版本linux内核的话可能找不到MT协议。MT协议被分为两种类型,Type A和TypeB,这两种类型的区别如下:
Type A:适用于触摸点不能被区分或者追踪,此类型的设备上报原始数据(此类型在实际使用中非常少!)。
Type B:适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过slot更新某一个触摸点的信息,gt911就属于此类型,一般的多点电容触摸屏IC都有此能力。
触摸点的信息通过一系列的ABS_MT事件(有的资料也叫消息)上报给linux内核,只有ABS_MT事件是用于多点触摸的,ABS_MT事件定义在文件include/uapi/linux/input-event-codes.h 中,相关事件如下所示:
示例代码42.1.1.1 ABS_MT事件

756 #define ABS_MT_SLOT          0x2f    /* MT slot being modified */
757 #define ABS_MT_TOUCH_MAJOR  0x30 //Major axis of touching ellipse 
758 #define ABS_MT_TOUCH_MINOR  0x31  //Minor axis (omit if circular)
759 #define ABS_MT_WIDTH_MAJOR  0x32//Major axis of approaching ellipse 
760 #define ABS_MT_WIDTH_MINOR  0x33 /* Minor axis (omit if circular) */
761 #define ABS_MT_ORIENTATION  0x34    /* Ellipse orientation */
762 #define ABS_MT_POSITION_X   0x35    /* Center X touch position */
763 #define ABS_MT_POSITION_Y   0x36    /* Center Y touch position */
764 #define ABS_MT_TOOL_TYPE    0x37    /* Type of touching device */
765 #define ABS_MT_BLOB_ID      0x38//Group a set of packets as a blob 
766 #define ABS_MT_TRACKING_ID  0x39 //Unique ID of initiated contact */
767 #define ABS_MT_PRESSURE     0x3a    /* Pressure on contact area */
768 #define ABS_MT_DISTANCE     0x3b    /* Contact hover distance */
769 #define ABS_MT_TOOL_X       0x3c    /* Center X tool position */
770 #define ABS_MT_TOOL_Y       0x3d    /* Center Y tool position */

在上面这些众多的ABS_MT事件中,我们最常用的就是ABS_MT_SLOT、ABS_MT_POSITION_X、ABS_MT_POSITION_Y和ABS_MT_TRACKING_ID。其中ABS_MT_POSITION_X和ABS_MT_POSITION_Y用来上报触摸点的(X,Y)坐标信息,ABS_MT_SLOT用来上报触摸点ID,对于Type B类型的设备,需要用到ABS_MT_TRACKING_ID事件来区分触摸点。
对于Type A类型的设备,通过input_mt_sync()函数来隔离不同的触摸点数据信息,此函数原型如下所示:
void input_mt_sync(struct input_dev *dev)
此函数只要一个参数,类型为input_dev,用于指定具体的input_dev设备。input_mt_sync()函数会触发SYN_MT_REPORT事件,此事件会通知接收者获取当前触摸数据,并且准备接收下一个触摸点数据。
对于Type B类型的设备,上报触摸点信息的时候需要通过input_mt_slot()函数区分是哪一个触摸点,input_mt_slot()函数原型如下所示:
void input_mt_slot(struct input_dev *dev, int slot)
此函数有两个参数,第一个参数是input_dev设备,第二个参数slot用于指定当前上报的是哪个触摸点信息。input_mt_slot()函数会触发ABS_MT_SLOT事件,此事件会告诉接收者当前正在更新的是哪个触摸点(slot)的数据。
不管是哪个类型的设备,最终都要调用input_sync()函数来标识多点触摸信息传输完成,告诉接收者处理之前累计的所有消息,并且准备好下一次接收。Type B和Type A相比最大的区别就是Type B可以区分出触摸点, 因此可以减少发送到用户空间的数据。Type B使用slot协议区分具体的触摸点,slot需要用到ABS_MT_TRACKING_ID消息,这个ID需要硬件提供,或者通过原始数据计算出来。对于Type A设备,内核驱动需要一次性将触摸屏上所有的触摸点信息全部上报,每个触摸点的信息在本次上报事件流中的顺序不重要,因为事件的过滤和手指(触摸点)跟踪是在内核空间处理的。
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。
42.1.2 Type A触摸点信息上报时序
对于Type A类型的设备,发送触摸点信息的时序如下所示,这里以2个触摸点为例:
示例代码47.2.2.1 Type A触摸点数据上报时序

1 ABS_MT_POSITION_X x[0]
2 ABS_MT_POSITION_Y y[0]
3 SYN_MT_REPORT
4 ABS_MT_POSITION_X x[1]
5 ABS_MT_POSITION_Y y[1]
6 SYN_MT_REPORT
7 SYN_REPORT
第1行,通过ABS_MT_POSITION_X事件上报第一个触摸点的X坐标数据,通过input_report_abs函数实现,下面同理。
第2行,通过ABS_MT_POSITION_Y事件上报第一个触摸点的Y坐标数据。
第3行,上报SYN_MT_REPORT事件,通过调用input_mt_sync函数来实现。
第4行,通过ABS_MT_POSITION_X事件上报第二个触摸点的X坐标数据。
第5行,通过ABS_MT_POSITION_Y事件上报第二个触摸点的Y坐标数据。
第6行,上报SYN_MT_REPORT事件,通过调用input_mt_sync函数来实现。
第7行,上报SYN_REPORT事件,通过调用input_sync函数实现。
我们在编写Type A类型的多点触摸驱动的时候就需要按照示例代码42.1.2.1中的时序上报坐标信息。Linux内核里面也有Type A类型的多点触摸驱动,找到st2332.c这个驱动文件,路径为drivers/input/touchscreen/st1232.c,找到st1232_ts_irq_handler函数,此函数里面就是上报触摸点坐标信息的。

示例代码42.1.2.1 st1232_ts_irq_handler函数代码段

103 static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
104 {
......
111     ret = st1232_ts_read_data(ts);
112     if (ret < 0)
113         goto end;
114 
115     /* multi touch protocol */
116     for (i = 0; i < MAX_FINGERS; i++) {
117         if (!finger[i].is_valid)
118             continue;
119 
120         input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
121         input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
122         input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
123         input_mt_sync(input_dev);
124         count++;
125     }
......
140 
141     /* SYN_REPORT */
142     input_sync(input_dev);
143 
144 end:
145     return IRQ_HANDLED;
146 }
第111行,获取所有触摸点信息。
第116~125行,按照Type A类型轮流上报所有的触摸点坐标信息,第121和122行分别上报触摸点的(X,Y)轴坐标,也就是ABS_MT_POSITION_X和ABS_MT_POSITION_Y事件。每上报完一个触摸点坐标,都要在第123行调用input_mt_sync函数上报一个SYN_MT_REPORT信息。
第142行,每上报完一轮触摸点信息就调用一次input_sync函数,也就是发送一个SYN_REPORT事件。

42.1.3 Type B触摸点信息上报时序
对于Type B类型的设备,发送触摸点信息的时序如下所示,这里以2个触摸点为例:
示例代码42.1.3.1 Type B触摸点数据上报时序

1 ABS_MT_SLOT 0
2 ABS_MT_TRACKING_ID 45
3 ABS_MT_POSITION_X x[0]
4 ABS_MT_POSITION_Y y[0]
5 ABS_MT_SLOT 1
6 ABS_MT_TRACKING_ID 46
7 ABS_MT_POSITION_X x[1]
8 ABS_MT_POSITION_Y y[1]
9 SYN_REPORT
第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来完成。
第5~8行,和第1~4行类似,只是换成了上报触摸点1的(X,Y)坐标信息
第9行,当所有的触摸点坐标都上传完毕以后就得发送SYN_REPORT事件,使用input_sync函数来完成。
当一个触摸点移除以后,同样需要通过SLOT关联的ABS_MT_TRACKING_ID来处理,时序如下所示:

示例代码47.2.3.2 Type B触摸点移除时序

1 ABS_MT_TRACKING_ID -1
2 SYN_REPORT
第1行,当一个触摸点(SLOT)移除以后,需要通过ABS_MT_TRACKING_ID事件发送一个-1给内核。方法很简单,同样使用input_mt_report_slot_state函数来完成,只需要将此函数的第三个参数active设置为false即可,不需要用户手动去设置-1。
第2行,当所有的触摸点坐标都上传完毕以后就得发送SYN_REPORT事件。

当要编写Type B类型的多点触摸驱动的时候就需要按照示例代码42.1.3.1中的时序上报坐标信息。Linux内核里面有大量的Type B类型的多点触摸驱动程序,我们可以参考这些现成的驱动程序来编写自己的驱动代码。这里就以ili210x这个触摸驱动IC为例,看看是Type B类型是如何上报触摸点坐标信息的。找到ili210x.c这个驱动文件,路径为drivers/input/touchscreen/ili210x.c,找到ili210x_report_events函数,此函数就是用于上报ili210x触摸坐标信息的,函数内容如下所示:
示例代码42.1.3.1 ili210x_report_events函数代码段

78 static void ili210x_report_events(struct input_dev *input,
 79                                   const struct touchdata *touchdata)
 80 {
 81         int i;
 82         bool touch;
 83         unsigned int x, y;
 84         const struct finger *finger;
 85 
 86         for (i = 0; i < MAX_TOUCHES; i++) {
 87                 input_mt_slot(input, i);
 88 
 89                 finger = &touchdata->finger[i];
 90 
 91                 touch = touchdata->status & (1 << i);
 92              input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
 93                 if (touch) {
 94                         x = finger->x_low | (finger->x_high << 8);
 95                         y = finger->y_low | (finger->y_high << 8);
 96 
 97                         input_report_abs(input, ABS_MT_POSITION_X, x);
 98                         input_report_abs(input, ABS_MT_POSITION_Y, y);
 99                 }
100         }
101 
102         input_mt_report_pointer_emulation(input, false);
103         input_sync(input);
104 }
第86~100行,使用for循环实现上报所有的触摸点坐标,第87~91行从触摸芯片中读取触摸坐标数据。第87行调用input_mt_slot函数上报ABS_MT_SLOT事件。第92行调用input_mt_report_slot_state函数上报ABS_MT_TRACKING_ID事件,也就是给SLOT关联一个ABS_MT_TRACKING_ID。第97~98行使用input_report_abs 函数上报触摸点对应的(X,Y)坐标值。
第103行,使用input_sync函数上报SYN_REPORT事件。

42.1.4 MT其他事件的使用
在示例代码42.1.1.1中给出了Linux所支持的所有ABS_MT事件,大家可以根据实际需求将这些事件组成各种事件组合。最简单的组合就是ABS_MT_POSITION_X和ABS_MT_POSITION_Y,可以通过在这两个事件上报触摸点,如果设备支持的话,还可以使用ABS_MT_TOUCH_MAJOR和ABS_MT_WIDTH_MAJOR这两个消息上报触摸面积信息,关于其他ABS_MT事件的具体含义大家可以查看Linux内核中的multi-touch-protocol.rst文档,这里我们重点补充一下ABS_MT_TOOL_TYPE事件。
ABS_MT_TOOL_TYPE事件用于上报触摸工具类型,很多内核驱动都不能区分出触摸设备类型是手指还是触摸笔?这种情况下,这个事件可以忽略掉。目前的协议支持MT_TOOL_FINGER(手指)、MT_TOOL_PEN(笔)和MT_TOOL_PALM(手掌)这三种触摸设备类型,于Type B类型,此事件由input子系统内核处理。如果驱动程序需要上报ABS_MT_TOOL_TYPE事件,那么可以使用input_mt_report_slot_state函数来完成此工作。
关于Linux系统下的多点触摸(MT)协议就讲解到这里,简单总结一下,MT协议隶属于linux的input子系统,驱动通过大量的ABS_MT事件向linux内核上报多点触摸坐标数据。根据触摸IC的不同,分为Type A和Type B两种类型,不同的类型其上报时序不同,目前使用最多的是Type B类型。接下来我们就根据前面学习过的MT协议来编写一个多点电容触摸驱动程序,本章节所使用的触摸屏是正点原子的MIPI 5.5英寸 LCD 720x1280 IPS屏幕,MIPI 5.5英寸 LCD 1080x1920 IPS屏幕,MIPI 10.1英寸 LCD 800x1280 IPS屏幕这两款触摸屏,这三款触摸屏都使用gt9xx类型触摸IC,因此驱动程序是完全通用的。
42.1.5 多点触摸所使用到的API函数
根据前面的讲解,我们知道linux下的多点触摸协议其实就是通过不同的事件来上报触摸点坐标信息,这些事件都是通过Linux内核提供的对应API函数实现的,本小节我们来看一下一些常见的API函数。
1、input_mt_init_slots函数
input_mt_init_slots函数用于初始化MT的输入slots,编写MT驱动的时候必须先调用此函数初始化slots,此函数定义在文件drivers/input/input-mt.c中,函数原型如下所示:

int input_mt_init_slots(	struct input_dev 	*dev, 
					unsigned int 		num_slots, 
					unsigned int 		flags)

函数参数和返回值含义如下:
dev: MT设备对应的input_dev,因为MT设备隶属于input_dev。
num_slots:设备要使用的SLOT数量,也就是触摸点的数量。
flags:其他一些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_UNUSED	0x0004	/* 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函数
此函数用于Type B类型,产生ABS_MT_SLOT事件,告诉内核当前上报的是哪个触摸点的坐标数据,此函数定义在文件include/linux/input/mt.h中,函数原型如下所示:
void input_mt_slot(struct input_dev *dev,
int slot)
函数参数和返回值含义如下:
dev: MT设备对应的input_dev。
slot:当前发送的是哪个slot的坐标信息,也就是哪个触摸点。
返回值:无。
3、input_mt_report_slot_state函数
此函数用于Type B类型,用于产生ABS_MT_TRACKING_ID和ABS_MT_TOOL_TYPE事件,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: MT设备对应的input_dev。
tool_type:触摸类型,可以选择MT_TOOL_FINGER(手指)、MT_TOOL_PEN(笔)或MT_TOOL_PALM(手掌),对于多点电容触摸屏来说一般都是手指。
active:true,连续触摸,input子系统内核会自动分配一个ABS_MT_TRACKING_ID给slot。false,触摸点抬起,表示某个触摸点无效了,input子系统内核会分配一个-1给slot,表示触摸点溢出。
返回值:触摸有效的话返回true,否则返回false。
4、input_report_abs函数
Type A和Type B类型都使用此函数上报触摸点坐标信息,通过ABS_MT_POSITION_X和ABS_MT_POSITION_Y事件实现X和Y轴坐标信息上报。此函数定义在文件include/linux/input.h中,函数原型如下所示:

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、input_mt_report_pointer_emulation函数
如果追踪到的触摸点数量多于当前上报的数量,驱动程序使用BTN_TOOL_TAP事件来通知用户空间当前追踪到的触摸点总数量,然后调用input_mt_report_pointer_emulation函数将use_count参数设置为false。否则的话将use_count参数设置为true,表示当前的触摸点数量(此函数会获取到具体的触摸点数量,不需要用户给出),此函数定义在文件drivers/input/input-mt.c中,函数原型如下:

void input_mt_report_pointer_emulation(struct input_dev 	*dev, 
								 bool 			use_count)

函数参数和返回值含义如下:
dev: MT设备对应的input_dev。
use_count:true,有效的触摸点数量;false,追踪到的触摸点数量多于当前上报的数量。
返回值:无。

42.1.6 多点电容触摸驱动框架
前面几小节已经详细的讲解了linux下多点触摸屏驱动原理,本小节我们来梳理一下linux下多点电容触摸驱动的编写框架和步骤。首先确定驱动需要用到哪些知识点,哪些框架?根据前面的分析,我们在编写驱动的时候需要注意一下几点:
①、多点电容触摸芯片的接口,一般都为I2C接口,因此驱动主框架肯定是I2C。
②、linux里面一般都是通过中断来上报触摸点坐标信息,因此需要用到中断框架。
③、多点电容触摸属于input子系统,因此还要用到input子系统框架。
④、在中断处理程序中按照linux的MT协议上报坐标信息。
根据上面的分析,多点电容触摸驱动编写框架以及步骤如下:
1、I2C驱动框架
驱动总体采用I2C框架,参考框架代码如下所示:
示例代码42.1.6.1 多点电容触摸驱动I2C驱动框架

1   /* 设备树匹配表 */ 
2  static const struct i2c_device_id xxx_ts_id[] = {
3   	{ "xxx", 0, },
4   	{ /* sentinel */ }
5  };
6  
7  /* 设备树匹配表 */
8  static const struct of_device_id xxx_of_match[] = {
9   	{ .compatible = "xxx", },
10  	{ /* sentinel */ }
11 };
12 
13 /* i2c驱动结构体 */ 
14 static struct i2c_driver gt9147_ts_driver = {
15  	.driver = {
16      	.owner = THIS_MODULE,
17      	.name = "gt9147",
18      	.of_match_table = of_match_ptr(xxx_of_match),
19  	},
20  	.id_table = xxx_ts_id,
21  	.probe    = xxx_ts_probe,
22  	.remove   = xxx_ts_remove,
23 };
24 
25 /*
26  * @description  : 驱动入口函数
27  * @param        : 无
28  * @return       : 无
29  */
30 static int __init xxx_init(void)
31 {
32  	int ret = 0;
33 
34  	ret = i2c_add_driver(&xxx_ts_driver);
35 
36  	return ret;
37 }
38 
39 /*
40  * @description  : 驱动出口函数
41  * @param        : 无
42  * @return       : 无
43  */
44 static void __exit xxx_exit(void)
45 {
46  	i2c_del_driver(&gt9147_ts_driver);
47 }
48 
49 module_init(xxx_init);
50 module_exit(xxx_exit);
51 MODULE_LICENSE("GPL");
52 MODULE_AUTHOR("zuozhongkai");
I2C驱动框架已经在四十章进行了详细的讲解,这里就不再赘述了。当设备树中触摸IC的设备节点和驱动匹配以后,示例代码42.1.6.1中第21行的xxx_ts_probe函数就会执行,我们可以在此函数中初始化触摸IC,中断和input子系统等。
2、初始化触摸IC、中断和input子系统
初始化操作都是在xxx_ts_probe函数中完成,参考框架如下所示(以下代码中步骤顺序可以自行调整,不一定按照示例框架来):

示例代码42.1.6.2 xxx_ts_probe驱动框架

1  static int xxx_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
2  {
3   	struct input_dev *input;
4   
5   	/* 1、初始化I2C               */
6   	......
7   
8   	/* 2,申请中断, */
9   	devm_request_threaded_irq(&client->dev, client->irq, NULL,
10              xxx_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
11              client->name, &xxx);
12  	......
13
14  	/* 3,input设备申请与初始化   */
15  	input = devm_input_allocate_device(&client->dev);
16 
17  	input->name = client->name;
18  	input->id.bustype = BUS_I2C;
19  	input->dev.parent = &client->dev;
20  	......
21  
22  	/* 4,初始化input和MT          */
23  	__set_bit(EV_ABS, input->evbit);
24  	__set_bit(BTN_TOUCH, input->keybit);
25 
26  	input_set_abs_params(input, ABS_X, 0, width, 0, 0);
27  	input_set_abs_params(input, ABS_Y, 0, height, 0, 0);
28  	input_set_abs_params(input, ABS_MT_POSITION_X,0, width, 0, 0);
29  	input_set_abs_params(input, ABS_MT_POSITION_Y,0, height, 0, 0);      
30  	input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
31  	......
32  
33  	/* 5,注册input_dev           */
34  	input_register_device(input);
35  	......
36 }
第5~7行,首先肯定是初始化触摸芯片,包括芯片的相关IO,比如复位、中断等IO引脚,然后就是芯片本身的初始化,也就是配置触摸芯片的相关寄存器。

第9行,因为一般触摸芯片都是通过中断来向系统上报触摸点坐标信息的,因此我们需要初始化中断,这里又和第三十一章内容结合起来了。大家可能会发现第9行并没有使用request_irq函数申请中断,而是采用了devm_request_threaded_irq这个函数,为什么使用这个函数呢?是不是request_irq函数不能使用?答案肯定不是的,这里用request_irq函数是绝对没问题的。那为何要用devm_request_threaded_irq呢?这里我们就简单的介绍一下这个API函数,devm_request_threaded_irq函数特点如下:
①、用于申请中断,作用和request_irq函数类似。
②、此函数的作用是中断线程化,大家如果直接在网上搜索“devm_request_threaded_irq”会发现相关解释很少。但是大家去搜索request_threaded_irq函数就会有很多讲解的博客和帖子,这两个函数在名字上的差别就是前者比后者多了个“devm_”前缀,“devm_”前缀稍后讲解。大家应该注意到了“request_threaded_irq”相比“request_irq”多了个threaded函数,也就是线程的意思。那么为什么要中断线程化呢?我们都是知道硬件中断具有最高优先级,不论什么时候只要硬件中断发生,那么内核都会终止当前正在执行的操作,转而去执行中断处理程序(不考虑关闭中断和中断优先级的情况),如果中断非常频繁的话那么内核将会频繁的执行中断处理程序,导致任务得不到及时的处理。中断线程化以后中断将作为内核线程运行,而且也可以被赋予不同的优先级,任务的优先级可能比中断线程的优先级高,这样做的目的就是保证高优先级的任务能被优先处理。大家可能会疑问,前面不是说可以将比较耗时的中断放到下半部(bottom half)处理吗?虽然下半部可以被延迟处理,但是依旧先于线程执行,中断线程化可以让这些比较耗时的下半部与进程进行公平竞争。
要注意,并不是所有的中断都可以被线程化,重要的中断就不能这么操作。对于触摸屏而言只要手指放到屏幕上,它可能就会一直产生中断(视具体芯片而定,GT911是这样的),中断处理程序里面需要通过I2C读取触摸信息并上报给内核,I2C的速度最大只有400KHz,算是低速外设。不断的产生中断、读取触摸信息、上报信息会导致处理器在触摸中断上花费大量的时间,但是触摸相对来说不是那么重要的事件,因此可以将触摸中断线程化。如果你觉得触摸中断很重要,那么就可以不将其进行线程化处理。总之,要不要将一个中断进行线程化处理是需要自己根据实际情况去衡量的。linux内核自带的goodix.c(汇顶科技)、mms114.c(MELFAS公司)、zforce_ts.c(zForce公司)等多点电容触摸IC驱动程序都采用了中断线程化,当然也有一些驱动没有采用中断线程化。
③、最后来看一下“devm_”前缀,在linux内核中有很多的申请资源类的API函数都有对应的“devm_”前缀版本。比如devm_request_irq和request_irq这两个函数,这两个函数都是申请中断的,我们使用request_irq函数申请中断的时候,如果驱动初始化失败的话就要调用free_irq函数对申请成功的irq进行释放,卸载驱动的时候也需要我们手动调用free_irq来释放irq。假如我们的驱动里面申请了很多资源,比如:gpio、irq、input_dev,那么就需要添加很多goto语句对其做处理,当这样的标签多了以后代码看起来就不整洁了。“devm_”函数就是为了处理这种情况而诞生的,“devm_”函数最大的作用就是:
使用“devm_”前缀的函数申请到的资源可以由系统自动释放,不需要我们手动处理。
如果我们使用devm_request_threaded_irq函数来申请中断,那么就不需要我们再调用free_irq函数对其进行释放。大家可以注意一下,带有“devm_”前缀的都是一些和设备资源管理有关的函数。关于“devm_”函数的实现原理这里就不做详细的讲解了,我们的重点在于学会如何使用这些API函数,感兴趣的可以查阅一些其他文档或者帖子来看一下“devm_”函数的实现原理。
第15行,接下来就是申请input_dev,因为多点电容触摸属于input子系统。这里同样使用devm_input_allocate_device函数来申请input_dev,也就是我们前面讲解的input_allocate_device函数加“devm_”前缀版本。申请到input_dev以后还需要对其进行初始化操作。
第23~24行,设置input_dev需要上报的事件为EV_ABS和BTN_TOUCH,因为多点电容屏的触摸坐标为绝对值,因此需要上报EV_ABS事件。触摸屏有按下和抬起之分,因此需要上报BTN_TOUCH按键。
第26~29行,调用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。
第30行,调用input_mt_init_slots函数初始化多点电容触摸的slots。
第34行,调用input_register_device函数系统注册前面申请到的input_dev。
3、上报坐标信息
最后就是在中断服务程序中上报读取到的坐标信息,根据所使用的多点电容触摸设备类型选择使用Type A还是Type B时序。由于大多数的设备都是Type B类型,因此这里就以Type B类型为例讲解一下上报过程,参考驱动框架如下所示:
示例代码41.1.6.3 xxx_handler中断处理程序

1  static irqreturn_t xxx_handler(int irq, void *dev_id)
2  {
3  
4   	int num;            	/* 触摸点数量 */
5   	int x[n], y[n];     	/* 保存坐标值 */
6   
7   	/* 1、从触摸芯片获取各个触摸点坐标值 */
8   	......
9   
10  	/* 2、上报每一个触摸点坐标 */
11  	for (i = 0; i < num; i++) {
12      	input_mt_slot(input, id);
13      	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
14      	input_report_abs(input, ABS_MT_POSITION_X, x[i]);
15      	input_report_abs(input, ABS_MT_POSITION_Y, y[i]);
16  	}
17  	......
18  
19  	input_sync(input);
20  	......
21  
22  	return IRQ_HANDLED;
23 }
进入中断处理程序以后首先肯定是从触摸IC里面读取触摸坐标以及触摸点数量,假设触摸点数量保存到num变量,触摸点坐标存放到x,y数组里面。
第11~16行,循环上报每一个触摸点坐标,一定要按照Type B类型的时序进行,这个已经在47.2.3小节进行详细的讲解,这里就不再赘述了。
第19行,每一轮触摸点坐标上报完毕以后就调用一次input_sync函数发送一个SYN_REPORT事件。
关于多点电容触摸驱动框架就讲解到这里,接下来我们就实际编写一个多点电容触摸驱动程序。

42.2 硬件原理图分析
触摸屏原理图一共涉及到 4 个引脚,如下所示:

在这里插入图片描述

图42.2.1 触摸原理图
I2C 接口引脚 I2C1_SCL_TP 和 I2C1_SDA_TP,触摸 IC 复位引脚 TP_RST_L以及触摸 IC 中断引脚 TP_INT_L。I2C1_SCL_TP 和 I2C1_SDA_TP 对应的引脚为GPIO0_B3和GPIO0_B4。TP_INT_L对应的引脚为GPIO0_B5,TP_RST_L对应的引脚为GPIO0_B6。
对于中断触发模式,当 GT911检测到有触摸事件发生时,会将中断信号拉低,所以对主机控制器来说,它是一个下降沿中断触发方式;当一直按着触摸屏不松开,则会一直触发中断,现在大部分的触摸 IC 都是这样设计的。相对于以前老式的触摸 IC,它们只有检测到按下这个动作发生时才会触发一次中断,一直按着不放是不会再次触发中断的。这样的设计对我们编写驱动来说在软件处理上会比较麻烦,一般要专门使用一个定时器来检测、处理触摸屏长按的情况,但对于 GT911 这类 IC 来说,不需要这样做。
42.3 实验程序编写
本试验以正点原子的MIPI 5.5英寸 LCD 720x1280 IPS屏幕,MIPI 5.5英寸 LCD 1080x1920 IPS屏幕,MIPI 10.1英寸 LCD 800x1280 IPS屏幕这三款屏幕所使用的gt9xx(由于5.5寸屏用了gt911及10.1英寸屏用了gt928触摸芯片,我们统称为gt9xx)系列触摸芯片为例,讲解如何编写多点电容触摸驱动。本实验对应的例程路径为:开发板光盘A盘01、程序源码03、Linux驱动例程 26_multitouch。
42.3.1 添加gt9xx设备节点
1、添加引脚节点
gt9xx用到了I2C1接口以及GPIO0_B5和GPIO0_B6这两个IO,I2C1的引脚一般厂商都会给你配置好,本实验就不需要再修改。本实验重点只需要配置GPIO0_B5以及GPIO0_B6这两个引脚,打开rk3568-evb.dtsi文件,添加如下引脚配置信息,注意默认提供的SDK早已经添加好下面的,不用我们去添加这些配置:
示例代码42.3.1.1 gt9xx中断以及复位引脚配置

1   touch {
2           touch_gpio: touch-gpio {
3                   rockchip,pins =
4                           <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_up>,
5                           <0 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>;
6           };
7   };

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

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

相关文章

C++复习 -- 常用关键字

this 关键字: 概念 在 C 中&#xff0c; this 关键字是一个指向调用对象的指针。它在成员函数内部使用&#xff0c;用于引用调用该函数的对象。使用 this 可以明确指出成员函数正在操作的是哪个对象的数据成员。 case :证明他就是一个指向被调用对象的指针: #include <iost…

Nginx的正向代理与反向代理

你好呀&#xff0c;我是赵兴晨&#xff0c;文科程序员。 今天&#xff0c;我们将一起了解什么是Nginx的正向代理&#xff1f;什么是Nginx的反向代理&#xff1f;并实际动手实践。 以下内容都是满满的干货&#xff0c;绝对不容错过。我建议先收藏这篇文章&#xff0c;然后找一…

Transformer+Classification学习笔记

论文名称&#xff1a;An Image is Worth 16x16 Words:Transformers for Image Recognition at Scale [2112.11010] MPViT: Multi-Path Vision Transformer for Dense Prediction (arxiv.org) 参考博客与视频&#xff1a; Vision Transformer 超详细解读 (原理分析代码解读) …

Jmeter 性能-需求分析业务/场景设计

1、性能测试需求分析与业务调研 性能测试的需求不能直接从产品经理那里获得&#xff0c;因为产品经理定义的性能需求比较抽象。 要落实到可执行的性能测试需求往往需要进一步分析和细化。这也是为什么获取具体的性能需求比较难的一个原因。 一般情况下&#xff0c;要知道性能…

(二刷)代码随想录第6天|242.有效的字母异位词、349.两个数组的交集

242.有效的字母异位词 242. 有效的字母异位词 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 学透哈希表&#xff0c;数组使用有技巧&#xff01;Leetcode&#xff1a;242.有效的字母异位词_哔哩哔哩_bilibili 给定两个字符串 s 和 t &#xff…

基于大模型训练的数字识别

创作原因 现在写电赛题&#xff0c;题目有识别数字的要求。但使用设备openmv使用模板匹配的算法帧率很低&#xff0c;且识别效果不是很好&#xff0c;于是我们就想到了利用神经网络训练模型来识别数字 正文部分 内容介绍 本文内容是基于openmv使用Edge Impulse训练大模型。…

网络安全等级保护测评指标解读(2.0)

网络安全等级保护2.0测评指标解读&#xff1a; 1、物理和环境安全 2、网络和通信安全 3、设备和计算安全 4、应用和数据安全 5、安全策略和管理制度 6、安全管理机构和人员 7、安全建设管理 8、安全运维管理 软件资料清单列表部分文档&#xff1a; 工作安排任务书&#xff0c;…

【数据结构】红黑树(定义性质、插入、查找、删除)解析+完整代码

3.3 红黑树 3.3.1 定义和性质 为什么发明红黑树&#xff1f; 平衡二叉树和红黑树的时间复杂度相同&#xff0c;但是平衡二叉树的平衡特性容易被破坏&#xff0c;需要频繁调整树的形态。 红黑树RBT&#xff1a;插入/删除很多时候不会破坏红黑特性&#xff0c;无需频繁调整树的形…

Kimi智能助手:你的全天候AI伙伴

Kimi使用链接&#xff1a; https://kimi.moonshot.cn/ Kimi手机端下载链接&#xff1a; https://kimi.moonshot.cn/download/app?refchat 简介&#xff1a; Kimi AI 是由月之暗面科技有限公司&#xff08;Moonshot AI&#xff09;开发的一款人工智能助手&#xff0c;它具备多…

正运动技术与合作伙伴邀您共聚2024武汉光博会

■展会名称&#xff1a; 2024“中国光谷”光电子博览会暨论坛&#xff08;以下简称“武汉光博会”&#xff09; ■展会日期 2024年5月16日 - 18日 ■展馆地点 中国光谷科技会展中心A3馆 ■展位号 3A21 5月16至18日&#xff0c;中国光谷科技会展中心将举办第二十届武汉光…

nmap使用教程

nmap使用教程 一、nmap简介二、nmap常用命令2.1、target specification&#xff08;目标规范&#xff09;2.1.1、用法2.1.2、详情 2.2、HOST DISCOVERY&#xff08;主机发现&#xff09;2.2.1、用法2.2.2、详情 2.3、SCAN TECHNIQUES&#xff08;扫描技术&#xff09;2.4、PORT…

【Web】HNCTF 2024 题解(部分)

目录 Please_RCE_Me ezFlask GoJava ez_tp GPTS Please_RCE_Me <?php if($_GET[moran] flag){highlight_file(__FILE__);if(isset($_POST[task])&&isset($_POST[flag])){$str1 $_POST[task];$str2 $_POST[flag];if(preg_match(/system|eval|assert|call|…

从零开始开发企业培训APP:在线教育系统源码剖析

今天&#xff0c;小编将深入剖析企业培训APP的开发&#xff0c;从零开始为企业构建一个高效、实用的在线教育系统。 一、需求分析 1.主要功能需求 包括但不限于&#xff1a; -用户管理 -课程管理 -学习计划 -互动功能 -考核评估 -统计分析 二、技术选型 1.前端技术 …

PG数据文件和块管理与Oracle比较

之前有说过PG数据库中的对象oid与数据文件一一对应&#xff0c;创建的数据库如果没有指定表空间&#xff0c;则会默认放在默认表空间中&#xff0c;例如&#xff1a; 1.对象OID与数据文件对应关系 Oracle的逻辑与物理对应关系如下&#xff1a; 两种结果相比较而言&#xff1a; …

拯救者杯OPENAIGC开发者大赛城市巡回沙龙,苏州站报名开启!

由联想拯救者、AIGC开放社区、英特尔联合主办的“AI生成未来第二届拯救者杯OPENAIGC开发者大赛”自上线以来&#xff0c;吸引了广大开发者的热情参与。 为了向技术开发者、业务人员、高校学生、以及个体创业人员等参赛者们提供更充分的帮助与支持&#xff0c;AIGC开放社区特别…

RockChip Android8.1 EthernetService分析

一:概述 本篇文章将围绕RK Android8.1 SDK对Ethernet做一次框架分析,包含Framework层和APP层。 当前版本SDK默认只支持一路Ethernet,熟悉Ethernet工作流程后通过修改最终会在系统Setting以太网中呈现多路选项(可以有多种实现方式),博主通过增加ListPreference实现的效果…

C++设计模式|创建型 5.原型模式

1.什么是原型模式&#xff1f; 原型模式⼀种创建型设计模式&#xff0c;该模式的核⼼思想是基于现有的对象创建新的对象&#xff0c;⽽不是从头开始创建。 在原型模式中&#xff0c;通常有⼀个原型对象&#xff0c;它被⽤作创建新对象的模板。新对象通过复制原型对象的属性和状…

namenode启动失败 org.apache.hadoop.hdfs.server.common.InconsistentFSStateException:

小白的Hadoop学习笔记 2024/5/14 18:26 文章目录 问题解决报错浅浅分析一下core-ste.xml 问题 namenode启动失败 读日志 安装目录下 vim /usr/local/hadoop/logs/hadoop-tangseng-namenode-hadoop102.log2024-05-14 00:22:46,262 ERROR org.apache.hadoop.hdfs.server.namen…

开源可视化表单服务商:提升自主研发 助力流程化办公!

当前&#xff0c;做好流程化办公可以为企业实现提质增效的办公效果&#xff0c;助力企业进入数字化转型。作为开源可视化表单服务商&#xff0c;流辰信息坚持研发创新理念&#xff0c;努力提升自主研发能力&#xff0c;专为广大客户朋友提供集产品、框架定制、产品交付为一体的…

Java面试八股之Java中有哪些原子类,原理是什么

Java中有哪些原子类&#xff0c;原理是什么 AtomicInteger 和 AtomicLong&#xff1a; 用于对整数&#xff08;int&#xff09;和长整数&#xff08;long&#xff09;进行原子操作。 原理&#xff1a;它们内部封装了一个整型或长整型变量&#xff0c;并通过使用Unsafe类提供…