深圳市专业网站建设,如何设计网站首页导航,开发一个电商app需要多少钱,阳江房地产信息网官方网站随着智能手机的发展#xff0c;电容触摸屏也得到了飞速的发展。相比电阻触摸屏#xff0c;电容触摸屏有很多的优势#xff0c;比如支持多点触控、不需要按压#xff0c;只需要轻轻触摸就有反应。ALIENTEK的三款RGB LCD屏幕都支持多点电容触摸#xff0c;本章就以ATK7016这…随着智能手机的发展电容触摸屏也得到了飞速的发展。相比电阻触摸屏电容触摸屏有很多的优势比如支持多点触控、不需要按压只需要轻轻触摸就有反应。ALIENTEK的三款RGB LCD屏幕都支持多点电容触摸本章就以ATK7016这款RGB LCD屏幕为例讲解一下如何驱动电容触摸屏并获取对应的触摸坐标值。
多点电容触摸简介
触摸屏很早就有了一开始是电阻触摸屏电阻触摸屏只能单点触摸。 和电阻触摸屏相比电容触摸屏最大的优点是支持多点触摸(后面的电阻屏也支持多点触摸)电容屏只需要手指轻触即可而电阻屏是需要手指给予一定的压力才有反应而且电容屏不需要校准。本章就来学习一下如何使用多点触摸屏如何获取到多点触摸值。只需要关注如何使用电容屏如何得到其多点触摸坐标值即可(这边可以去看我的知乎里面的笔记之前学精英板HAL库的时候课程里面是有讲到原理的而且原理也不难挺好理解的)。ALIENTEK的三款RGB LCD屏幕都是支持5点电容触摸屏的本章以ATK-7016这款屏幕为例来讲解如何使用多点电容触摸屏。
ATK-7016这款屏幕其实是由TFT LCD触摸屏组合起来的。底下是LCD面板上面是触摸面板将两个封装到一起就成了带有触摸屏的LCD屏幕。电容触摸屏也是需要一个驱动IC的驱动IC一般会提供一个I2C接口给主控制器主控制器可以通过I2C接口来读取驱动IC里面的触摸坐标数据。 ATK-7016、ATK-7084这两款屏幕使用的触摸控制 IC是FT5426ATK-4342使用的驱动IC是GT9147。这三个电容屏触摸IC都是I2C接口的 使用方法基本一样。
FT5426这款驱动IC采用15*28的驱动结构也就是15个感应通道28个驱动通道最多支持5点电容触摸。ATK-7016的电容触摸屏部分有4个IO用于连接主控制器SCL、SDA、RST和INTSCL和SDA是I2C引脚RST是复位引脚INT是中断引脚。一般通过INT引脚来通知主控制器有触摸点按下然后在INT中断服务函数中读取触摸数据。也可以不使用中断功能采用轮询的方式不断查询是否有触摸点按下本章实验使用中断方式来获取触摸数据。
跟所有的I2C器件一样FT5426也是通过读写寄存器来完成初始化和触摸坐标数据读取的STM32MP1的I2C之前已经有过学习所以本章的主要工作就是读写FT5426的寄存器。FT5426的I2C设备地址为0X38FT5426的寄存器有很多本章只用到了其中的一部分如下图所示 上图中就是本次触摸屏实验会使用到的寄存器。
Linux下电容触摸屏驱动框架简介
多点触摸(MT)协议详解
电容触摸驱动的基本原理就不详细讲了回顾一下几个重要的知识点
电容触摸屏是IIC接口的需要触摸IC以正点原子的ATK7016为例其所使用的触摸屏控制IC为FT5426因此所谓的电容触摸驱动就是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.txt。
老版本的linux内核是不支持多点电容触摸的(Multi-touch简称MT)。MT协议是后面加入的因此如果使用2.x版本linux内核的话可能找不到MT协议。 MT协议被分为两种类型Type A和TypeB这两种类型的区别如下
Type A适用于触摸点不能被区分或者追踪此类型的设备上报原始数据(此类型在实际使用中非常少 )。Type B适用于有硬件追踪并能区分触摸点的触摸设备此类型设备通过slot更新某一个触摸点的信息FT5426就属于此类型一般的多点电容触摸屏IC都有此能力。
触摸点的信息通过一系列的ABS_MT事件(有的资料也叫消息)上报给linux内核只有ABS_MT事件是用于多点触摸的ABS_MT事件定义在文件include/uapi/linux/input-event-codes.h 中相关事件如下所示 在上面这些众多的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。
Type A触摸点信息上报时序
对于Type A类型的设备发送触摸点信息的时序如下所示这里以2个触摸点为例 第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类型的多点触摸驱动的时候就需要按照示例代码47.2.2.1中的时序上报坐标信息。Linux内核里面也有Type A类型的多点触摸驱动找到st2332.c这个驱动文件路径为drivers/input/touchscreen/st1232.c找到st1232_ts_irq_handler函数此函数里面就是上报触摸点坐标信息的。 第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事件。
Type B触摸点信息上报时序
对于Type B类型的设备发送触摸点信息的时序如下所示这里以2个触摸点为例 第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来处理时序如下所示 第1行当一个触摸点(SLOT)移除以后需要通过ABS_MT_TRACKING_ID事件发送一个-1给内核。方法很简单同样使用input_mt_report_slot_state函数来完成只需要将此函数的第三个参数active设置为false即可不需要用户手动去设置-1。
第2行当所有的触摸点坐标都上传完毕以后就得发送SYN_REPORT事件。当要编写Type B类型的多点触摸驱动的时候就需要按照示例代码47.2.3.1中的时序上报坐标信息。
Linux内核里面有大量的Type B类型的多点触摸驱动程序可以参考这些现成的驱动程序来编写自己的驱动代码。这里就以ili210x这个触摸驱动IC为例看看是Type B类型是如何上报触摸点坐标信息的。找到ili210x.c这个驱动文件路径为drivers/input/touchscreen/ili210x.c找到ili210x_report_events函数此函数就是用于上报ili210x触摸坐标信息的函数内容如下所示 第194-330行使用for循环实现上报所有的触摸点坐标第202-266行从触摸芯片中读取触摸坐标数据。第282行调用input_mt_slot函数上报ABS_MT_SLOT事件。第290行调用input_mt_report_slot_state函数上报ABS_MT_TRACKING_ID事件也就是给SLOT关联一个ABS_MT_TRACKING_ID。第314行使用touchscreen_report_pos函数上报触摸点对应的(X,Y)坐标值touchscreen_report_pos 函数定义在of_touchscreen.c文件中此函数最终通过input_report_abs来上报坐标值。
第354行使用input_sync函数上报SYN_REPORT事件。
MT其他事件使用
在示例代码47.2.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.txt文档这里重点补充一下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协议来编写一个多点电容触摸驱动程序本章节所使用的触摸屏是正点原子的ATK7084(7寸800*480)和ATK7016(7寸1024*600)这两款触摸屏这两款触摸屏都使用FT5426这款触摸IC因此驱动程序是完全通用的。
多点触摸所使用的API函数
根据前面的讲解学习到linux下的多点触摸协议其实就是通过不同的事件来上报触摸点坐标信息这些事件都是通过Linux内核提供的对应API函数实现的本小节来看一下一些常见的API函数。
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)函数参数和返回值含义如下
devMT设备对应的input_dev因为MT设备隶属于input_dev。num_slots设备要使用的SLOT数量也就是触摸点的数量。flags其他一些flags信息可设置的flags如下所示 可以采用‘|’运算来同时设置多个flags标识。
返回值0成功负值失败。
input_my_slot函数
此函数用于Type B类型产生ABS_MT_SLOT事件告诉内核当前上报的是哪个触摸点 的坐标数据此函数定义在文件include/linux/input/mt.h中函数原型如下所示
void input_mt_slot(struct input_dev *dev, int slot)函数参数和返回值含义如下
devMT设备对应的input_dev。slot当前发送的是哪个slot的坐标信息也就是哪个触摸点。返回值无。
input_mt_report_slot_state函数
此函数用于Type B类型用于产生ABS_MT_TRACKING_ID和ABS_MT_TOOL_TYPE事件ABS_MT_TRACKING_ID事件给slot关联一个ABS_MT_TRACKING_IDABS_MT_TOOL_TYPE事件指定触摸类型(是笔还是手指等)。此函数定义在文件drivers/input/input-mt.c中此函数原型如下所示
bool input_mt_report_slot_state(struct input_dev *dev, unsigned int tool_type, bool active)函数参数和返回值含义如下
devMT设备对应的input_dev。tool_type触摸类型可以选择MT_TOOL_FINGER(手指)、 MT_TOOL_PEN(笔)或MT_TOOL_PALM(手掌)对于多点电容触摸屏来说一般都是手指。activetrue连续触摸input子系统内核会自动分配一个ABS_MT_TRACKING_ID给slotfalse触摸点抬起表示某个触摸点无效了input子系统内核会分配一个-1给slot表示触摸点溢出。返回值触摸有效的话返回true否则返回false。
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)函数参数和返回值含义如下
devMT设备对应的input_dev。code要上报的是什么数据可以设置为ABS_MT_POSITION_X或ABS_MT_POSITION_Y也就是X轴或者Y轴坐标数据。value具体的X轴或Y轴坐标数据值。返回值无。
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)函数参数和返回值含义如下
devMT设备对应的input_dev。use_counttrue有效的触摸点数量false追踪到的触摸点数量多于当前上报的数量。返回值无。
多点电容触摸驱动框架
前面几小节已经详细的讲解了linux下多点触摸屏驱动原理本小节来梳理一下linux下多点电容触摸驱动的编写框架和步骤。首先确定驱动需要用到哪些知识点哪些框架根据前面的分析在编写驱动的时候需要注意一下几点
多点电容触摸芯片的接口一般都为I2C接口因此驱动主框架肯定是I2C。linux里面一般都是通过中断来上报触摸点坐标信息因此需要用到中断框架。多点电容触摸属于input子系统因此还要用到input子系统框架。在中断处理程序中按照linux的MT协议上报坐标信息。
根据上面的分析多点电容触摸驱动编写框架以及步骤如下
I2C驱动框架
驱动总体采用I2C框架参考框架代码如下所示
示例代码 47.2.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 ft5x06_ts_driver {
15 .driver {
16 .owner THIS_MODULE,
17 .name edt_ft5x06,
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(ft5x06_ts_driver);
47 }
48
49 module_init(xxx_init);
50 module_exit(xxx_exit);
51 MODULE_LICENSE(GPL);
52 MODULE_AUTHOR(zuozhongkai);I2C驱动框架已经在之前的I2C驱动中已经进行了详细的讲解这里就不再赘述了。当设备树中触摸IC的设备节点和驱动匹配以后示例代码47.2.6.1中第21行的xxx_ts_probe函数就会执行可以在此函数中初始化触摸IC中断和input子系统等。
初始化触摸IC、中断和input子系统
初始化操作都是在xxx_ts_probe函数中完成参考框架如下所示(以下代码中步骤顺序可以 自行调整不一定按照示例框架来)
示例代码 47.2.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行因为一般触摸芯片都是通过中断来向系统上报触摸点坐标信息的因此需要初始化中断这里又和之前Linux中断内容结合起来了。可能会发现第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函数也就是线程的意思。那么为什么要中断线程化呢都是知道硬件中断具有最高优先级不论什么时候只要硬件中断发生那么内核都会终止当前正在执行的操作转而去执行中断处理程序(不考虑关闭中断和中断优先级的情况)如果中断非常频繁的话那么内核将会频繁的执行中断处理程序导致任务得不到及时的处理。中断线程化以后中断将作为内核线程运行而且也可以被赋予不同的优先级任务的优先级可能比中断线程的优先级高这样做的目的就是保证高优先级的任务能被优先处理。虽然下半部可以被延迟处理但是依旧先于线程执行中断线程化可以让这些比较耗时的下半部与进程进行公平竞争。 要注意并不是所有的中断都可以被线程化重要的中断就不能这么操作。对于触摸屏而言只要手指放到屏幕上它可能就会一直产生中断(视具体芯片而定FT5426是这样的)中断处理程序里面需要通过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_”前缀的都是一些和设备资源管理有关的函数。
第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。
上报坐标信息
最后就是在中断服务程序中上报读取到的坐标信息根据所使用的多点电容触摸设备类型选择使用Type A还是Type B时序。由于大多数的设备都是Type B类型因此这里就以Type B类型为例讲解一下上报过程参考驱动框架如下所示
示例代码 47.2.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类型的时序进行。
第19行每一轮触摸点坐标上报完毕以后就调用一次input_sync函数发送一个SYN_REPORT事件。
关于多点电容触摸驱动框架就讲解到这里接下来就实际编写一个多点电容触摸驱动程序。
硬件原理图分析
触摸屏原理图一共涉及到4个引脚如下图所示 I2C接口引脚I2C2_SCL和I2C2_SDA触摸IC 复位引脚CT_RST以及触摸IC中断引脚CT_INT。 I2C2_SCL和 I2C2_SDA对应的引脚为PH4和PH5。CT_INT对应的引脚为PI1CT_RST对应的引脚为PH15。
对于中断触发模式当FT5426 检测到有触摸事件发生时会将中断信号拉低所以对主机控制器来说它是一个下降沿中断触发方式当一直按着触摸屏不松开则会一直触发中断现在大部分的触摸IC都是这样设计的。一般要专门使用一个定时器来检测、处理触摸屏长按的情况但对于FT5426这类IC来说不需要这样做。
实验程序编写
添加FT5426设备节点
添加引脚节点
FT5426用到了I2C2接口以及PI1和PH15这两个IOI2C2的引脚前面实验已经修改好了本实验就不需要再修改。本实验重点只需要配置PI1以及PH15这两个引脚打开stm32mp15-pinctrl.dtsi文件添加如下引脚配置信息
示例代码47.4.1.1 ft5426中断以及复位引脚配置
1 ft5426int_reset_pins_a: ft5426int_reset_pins-0 {
2 pins1 {
3 pinmux STM32_PINMUX(I, 1, GPIO), /* ft5426 INT */
4 STM32_PINMUX(H, 15, GPIO); /* ft5426 RESET */
5 bias-pull-up;
6 slew-rate 0;
7 };
8 };FT5426节点配置
FT5426这个触摸IC挂载在STM32MP1的I2C2总线接口上因此需要向I2C2节点下添加一个子节点此子节点用于描述FT5426添加完成以后的I2C2节点内容如下所示(省略掉其他挂载到I2C2下的设备)
示例代码 47. 4.1.2 ft 5426 节点信息
1 i2c2 {
2 pinctrl-names default, sleep;
3 pinctrl-0 i2c2_pins_a;
4 pinctrl-1 i2c2_pins_sleep_a;
5 status okay;
6
7 ft5426: ft542638 {
8 compatible edt,edt-ft5426;
9 pinctrl-0 ft5426int_reset_pins_a;
10 reg 0x38;
11 irq-gpios gpioi 1 GPIO_ACTIVE_LOW;
12 reset-gpios gpioh 15 GPIO_ACTIVE_LOW;
13 status okay;
14 };
15 };第7行触摸屏所使用的FT5426 芯片节点挂载I2C2 节点下FT5426 的器件地址为0X38。
第10行reg属性描述FT5426的器件地址为0x38。
第11行irq-gpios 属性描述中断IO对应的GPIO为PI1。
第12行reset-gpios属性描述复位IO对应的GPIO 为PH15。
编写多点电容触摸驱动
首先要先进行FT5426芯片的相关寄存器宏定义。之后还要define一下对应的按下、抬起、接触以及保留的宏指令。
然后编写ft5426的设备结构体里面要放进去之前说过的四个内容也就是i2c_clientinput_dev还有两个int变量reset_gpio以及irq_gpio。
之后编写write函数通过定义struct i2c_msg的msg变量来获取flagsaddrbuf以及len(msg.lenlen1)来完成i2c的写操作然后i2c_transfer把消息发出去。
read也是类似只不过这里跟spi不一样有收发的区别写只要msg读就需要i2c_mas msg[2]然后msg[0]就是写操作msg[1]就是读操作写的len就是1读的msg[1].lenlen。然后同样是通过i2c_transfer发消息。
然后编写reset函数这里就要通过of_get_named_gpio获取设备树中的复位GPIO引脚然后通过devm_gpio_request_one来申请使用引脚这里通过msleep进行延时拉高拉低引脚先gpio_set_value_cansleep拉低然后gpio_set_value_cansleep拉高即可标识复位。
然后编写isr函数用于作为触摸屏中断服务函数来完成坐标上报。首先通过刚才自己编写的edt_ft5426_ts_read函数从0x02寄存器(自己宏定义过的)连续读取29个寄存器然后在for循环里面来读取每一个触点把连续读取的rdbuf通过6*i这个规律读到u8的buf数组中。然后在触点类型type中保存buf[0]6获取Event Flag坐标的获取则是要看屏幕和芯片自己的定义来获取然后触摸id通过(buf[2]4)0x0f来获取来看是哪一个触摸点之后通过input_mt_slot和input_mt_report_slot_state完成当前触摸点事件上报之后通过input_report_abs把当前的xy更新上报。把所有点都上报之后(跳出for循环)调用input_mt_report_pointer_emulation并把第二个参数置true最后调用input_sync完成触点坐标的获取。
之后编写irq函数用于初始化中断引脚。老样子通过of_get_named_gpio来获取中断引脚然后通过devm_gpio_request_one申请使用引脚之后通过devm_request_threaded_irq注册中断服务函数就是刚写的那个isr函数。
之后编写probe函数这里就比较常规了显示devm_kzalloc实例化设备然后edt_ft5426_ts_reset先复位芯片msleep之后初始化芯片(根据芯片手册要求通过write函数对寄存器写入对应值)然后edt_ft5426_ts_irq申请注册中断服务函数之后devm_input_allocate_device注册input设备设定是BUS_I2C通讯然后input_set_abs_params初始化屏幕范围最后input_mt_init_slots初始化一下slot。最后注册一下input设备通过input_register_device完成然后i2c_set_clientdata把input绑定到client。
之后编写remove函数这里就是通过之前已经保存的i2c的clientdata由i2c_get_clientdata获取设备之后直接input_unregister_device注销设备就可以了。
然后是of_device_id的数组设置一下跟设备树吻合的.compatible就好。
之后是写一个i2c_driver的函数里面要完成.driver保存.owner.name以及.of_match_table之后绑定.probe和.remove就好了。
最后就是module_i2c_driver把刚才的i2c_driver加入然后添加MODULE_LICENSEMODULE_AUTHOR以及MODULE_INFO就可以了。
运行测试
编译驱动程序
把Makefile里面的obj-m改成ft5x06.o然后“make”就可以了。
运行测试
编译设备树然后使用新的设备树启动linux内核。
多点电容触摸屏测试不需要编写专门的APP将上一小节编译出来ft5x06.ko拷贝到rootfs/lib/modules/5.3.41目录中启动开发板进入到目录lib/modules/5.3.41中输入如下命令加载ft5x06.ko这个驱动模块 depmod //第一次加载驱动的时候需要运行此命令 modprobe ft5x06.ko //加载驱动模块
驱动加载完成后会有如下图所示的信息输入 不同的平台event序号不同也可能是event3event4等一切以实际情况为准输入如下命令查看event1也就是多点电容触摸屏上报的原始数据 hexdump /dev/input/event1
现在用一根手指触摸屏幕的右上角然后再抬起理论坐标值为(1023,0)但是由于触摸误差的原因大概率不会是绝对的(1023,0)应该是在此值附近的一个触摸坐标值实际的上报数据如下图所示 上图中上报的信息是按照input_event类型呈现的这个同样在之前的input子系统中已经做了详细的介绍这里重点来分析一下在多点电容触摸屏上其所代表的具体含义将上图中的数据进行整理结果如下所示 第1行type为0x3说明是一个EV_ABS事件code为0x39为ABS_MT_TRACKING_ID。因此这一行就是input_mt_slot函数上报的ABS_MT_TRACKING_ID事件。value0说明屏被按下。
第2行type为0x3是一个EV_ABS事件code为0x35为ABS_MT_POSITION_X。这一行就是input_report_abs函数上报的ABS_MT_POSITION_X事件也就是触摸点的X轴坐标。value0x3fc1020说明触摸点X轴坐标为1020属于屏幕右边区域。
第3行type为0x3是一个EV_ABS事件code为0x36为ABS_MT_POSITION_Y。这一行就是input_mt_report_slot_state函数上报的ABS_MT_POSITION_Y事件也就是触摸点 的Y轴坐标。value0x1016说明Y轴坐标为16由此可以看出本次触摸的坐标为(1020,16)处于屏幕右上角区域。
第4行type为0x1是一个EV_KEY事件code0x14a为BTN_TOUCH value0x1表 示触摸屏被按下。
第5行type为0x3是一个EV_ABS事件code为0x0为ABS_X用于单点触摸的时候上报X轴坐标。在这里和ABS_MT_POSITION_X相同value也为0x3fc1020。ABS_X是由input_mt_report_pointer_emulation函数上报的。
第6行type为0x3是一个EV_ABS事件code为0x1为ABS_Y用于单点触摸的时候上报Y轴坐标。在这里和ABS_MT_POSITION_Y相同value也为0x1016。ABS_Y是由input_mt_report_pointer_emulation函数上报的。
第7行type为0x0是一个EV_SYN事件由input_sync函数上报。
第8行type为0x3是一个EV_ABS事件code为0x39也就是ABS_MT_TRACKING_IDvalue0xffffffff-1说明触摸点离开了屏幕。
第9行type为0x1是一个EV_KEY事件code0x14a为BTN_TOUCHvalue0x0表示手指离开触摸屏也就是触摸屏没有被按下了。
第10行type为0x0是一个EV_SYN事件由input_sync函数上报。
以上就是一个触摸点的坐标上报过程和前面讲解的Type B类型设备一致。
将驱动添加到内核中
前面一直将触摸驱动编译为模块每次系统启动以后在手动加载驱动模块这样很不方便。当把驱动调试成功以后一般都会将其编译到内核中这样内核启动以后就会自动加载驱动不需要再手动modprobe了。本节就来学习一下如何将ft5x06.c添加到linux内核里面步骤如下所示
将驱动文件放到合适位置
首先肯定是在内核源码中找个合适的位置将ft5x06.c放进去ft5x06.c是个触摸屏驱动因 此需要查找一下linux内核里面触摸屏驱动放到了哪个目录下。linux内核里面将触摸屏驱动放到了drivers/input/touchscreen目录下因此要将 ft5x06.c拷贝到此目录下命令如下 cp ft5x06.c (内核源码目录 )/drivers/input/touchscreen/ -f
修改对应的Makefile
修改drivers/input/touchscreen目录下的Makefile在最下面添加下面一行 obj-y ft5x06.o
完成后重新编译linux内核然后用新的uImage启动开发板。如果驱动添加成功会输出如下图所示信息 从上图可以看出触摸屏驱动已经启动了这个时候就会自动生成/dev/input/evenvtX。在本实验中将触摸屏驱动添加到linux内核里面以后触摸屏对应的是event0而不是前面编译为模块对应的event1这一点一定要注意。输入如下命令查看驱动工作是否正常 hexdump /dev/input/event0 //查看触摸屏原始数据上报信息
结果如下图所示 可以看出坐标数据的上报正常说明驱动没有问题。
tslib移植与使用
tslib移植
tslib是一个开源的第三方库用于触摸屏性能调试使用电阻屏的时候一般使用tslib进行 校准。虽然电容屏不需要校准但是由于电容屏加工的原因有的时候其不一定精准因此有时候也需要进行校准。最主要的是tslib提供了一些其他软件可以通过这些软件来测试触摸屏工作是否正常。最新版本的tslib已经支持了多点电容触摸屏因此可以通过tslib来直观的测试多点电容触摸屏驱动这个要比观看eventX原始数据方便的多。
tslib的移植很简单使用的文件系统为buildroot只要打开图形化配置界面即可tilib的配置进入buildroot的源码目录下通过“make menuconfig”打开配置界面后配置路径如下 - Target packages - Libraries - Hardware handling - [*] tslib
配置如下图所示; 保存配置在重新编译文件系统记得ubuntu要连接网络。编译完成后进入output/images目录运行以下命令把文件系统替换进去 cd output/images/ //进入到 output/images目录 sudo tar -axvf rootfs.tar -C /home/zuozhongkai/linux/nfs/rootfs //解压到 nfsroot目录
上述命令将buildroot中output/images/rootfs.tar这个压缩包解压到/home/zuozhongkai/linux/nfs/rootfs这个目录中这个目录就是教程中当前nfsroot目录根据自己的实际情况解压到对应的目录文件中。
完成以后重启开发板然后就可以进行测试了。
tslib测试
电容屏可以不用校准如果是电阻屏就要先进行校准校准的话输入如下命令 ts_calibrate
校准完成后如果不满意或者不小心对电容屏进行了校准可以直接删除掉/etc/pointercal文件。
最后使用ts_test_mt这个软件来测试触摸屏工作是否正常以及多点触摸是否有效执行如下所示命令 ts_test_mt
此命令会打开一个触摸测试界面如下图所示 在上图上有三个按钮“Drag”、“Draw”和“Quit”这三个按钮的功能如下
Drag拖拽按钮默认就是此功能可以看到屏幕中间有一个十字光标可以通过触摸屏幕来拖拽此光标。一个触摸点一个十字光标对于5点电容触摸屏如果5个手指都放到屏幕上那么就有5个光标一个手指一个。Draw绘制按钮按下此按钮就可以在屏幕上进行简单的绘制可以通过此功能检测多点触摸工作是否正常。Quit退出按钮退出ts_test_mt测试软件。
点击“Draw”按钮使用绘制功能5个手指一起划过屏幕如果多点电容屏工作正常的话就会在屏幕上留下5条线。
使用内核自带的驱动
Linux内核已经集成了很多电容触摸IC的驱动文件比如本章实验所使用FT5426。本节就来学习一下如何使用Linux内核自带的多点电容触摸驱动。在使用之前要先将前面自己添加到内核的ft5x06.c这个文件从内核中去除掉。
内核自带的FT5426的驱动文件为drivers/input/touchscreen/edt-ft5x06.c此驱动文件不仅仅能够驱动FT5426FT5206、FT5406这些都可以驱动。按照如下步骤来操作学习如何使用此驱动。
使能内核自带的FT5X06驱动
edt-ft5x06.c这个驱动默认是使能的还是要教一下如何配置Linux内核使能此驱动通过图形化配置界面即可完成配置。配置路径如下 - Device Drivers - Input device support - Generic input layer (needed for keyboard, mouse, ...) (INPUT [y]) - Touchscreens (INPUT_TOUCHSCREEN [y]) - * EDT FocalTech FT5x06 I2C Touchscreen support
配置如下图所示 修改设备树
修改之前编写的ft5426这个设备节点需要在里面添加compatible属性添加的内容就要参考edt-ft5x06.c文件了edt-ft5x06.c所支持的 ompatible属性列表如下所示 可以看出edt-ft5x06.c文件默认支持的compatible属性只有六个“edt,edt-ft5206”、“edt,edt-ft5306”和 “edt,edt-ft5406”等等。因此需要修改设备树中的ft5426节点修改以后的节点内容如下所示
示例代码 47.7.2 ft5426 节点内容
1 ft5246: ft542638 {
2 compatible edt,edt ft5406;
3 pinctrl-0 ft5426int_reset_pins_a;
4 reg 0x38;
5 interrupt-parent gpioi;
6 interrupts 1 IRQ_TYPE_EDGE_RISING;
8 reset-gpios gpioh 15 GPIO_ACTIVE_LOW;
9 status okay;
10 };第2行添加一条“edt,edt-ft5406”兼容性值。
修改完成以后重新编译设备树然后使用新得到的.dtb启动linux内核。如果一切正常的话系统启动的时候就会输出如下图所示信息 直接运行ts_test_mt来测试触摸屏是否可以使用。如果触摸点不对可运行ts_calibrate进行校准。至此关于Linux下的多点电容触摸驱动就结束了重点就是掌握linux下的触摸屏上报时序大多数都是Type B类型。
4.3寸屏触摸驱动实验
正点原子有两款4.3寸电容触摸屏分辨率分别为800480和480272这两款电容触摸屏的触摸驱动IC都是GT9147因此本质上就是编写 GT9147驱动。原理和方法基本和前面讲的7寸屏所使用的FT5426一样这里只简单讲解一下GT9147的驱动编写步骤。
在设备树的i2c2节点下添加gt9147子节点
第一肯定是修改设备树在i2c2的节点下添加一个gt9147的子节点。添加内容如下
示例代码 47.8.1 gt9147 子节点内容
1 gt9147: gt914714 {
2 compatible atk-gt9147;
3 reg 0x14;
4 interrupt-parent gpioi;
5 interrupts 1 IRQ_TYPE_EDGE_RISING;
6 interrupt-gpios gpioi 1 GPIO_ACTIVE_LOW;
7 reset-gpios gpioh 15 GPIO_ACTIVE_LOW;
8 status okay;
9 };在使用的ST官方的内核配置已经把gt9147这个驱动编译进内核了所以compatible的属性值为“atk-gt9147”免得使用内核的gt9147驱动代码。
添加屏幕参数
不同的屏幕其配置参数也不同因此需要在drivers/gpu/drm/panel/panel-simple.c文件下添加对应屏幕的参数这里就根据之前LCD章节里面的学习添加就好了我的屏幕是7寸的这里就没怎么看。
编译GT9147驱动文件
这里的方法跟之前是一样的可以编译拷贝过去之后使用tslib进行测试。
如果有如下的错误 上图中使用ts_test_mt来测试多点电容触摸但是此时提示ts_setup错误。这个时候先用hexdump查看一下原始触摸数据有没有。如果有的话就是tslib配置错误没有指定对触摸设备。打开/etc/profile文件输入如下这行即可 export TSLIB_TSDEVICE/dev/input/event1
其中LIB_TSDEVICE表示触摸屏对应的设备这里设置为/dev/input/event1要根据自己的实际情况来设置。
注意gt9147.c里面的驱动是单点触摸的因为GT9147没有硬件检测每个触摸点的按下和抬起因此在上报数据的时候不好处理。尝试过一些其他的处理方法但是效果都不理想因此改为了单点触摸。如果想直接使用Linux自带的gt9147驱动直接修改设备树的compatible属性值为“goodix,gt9147”即可。
总结
这一章节主要就是学习了触摸屏的驱动。主要就是对之前的I2C通讯以及INPUT子系统拼接在一起来完成驱动的编写。
这里要注意的是出教程的时候正点原子的7寸屏是FT5426的芯片现在买的话都已经是跟4.3寸屏一样的GT9147了。
主要记住的是咱们都是Type B的时序会用到slot的相关函数具体的编写都要看具体芯片的寄存器读写时序但是大的框架都是在中断服务函数里面Type B的slot函数来更新上报坐标的。具体的x和y坐标则是要查询相关的芯片数据手册来看读写时序。