【i.MX6ULL】驱动开发13--电容触摸驱动实践(下)
上篇文章介绍了电容触摸驱动的编写,包括设备树的修改和驱动程序(IIC驱动+中断+input子系统),并通过将触摸坐标值实时打印出来的方式,对触摸功能进行测试。
本篇,先来介绍一会测试触摸是库——tslib,使用它可以进行图形化的触摸测试。之后,再回头来分析分析触摸协议上报的原理以及通过input子系统上报的数据的具体含义。
[TOC]
1 tslib的使用
Tslib是一个开源的程序,能够为触摸屏驱动获得的采样提供诸如滤波、去抖、校准等功能,通常作为触摸屏驱动的适配层,为上层的应用提供了一个统一的接口。
1.1 tslib库移植
首先下载tslib库的源码:https://github.com/libts/tslib/tags
目前最新的是1.22,不过本篇先使用1.21版本
1.1.1 ubuntu上编译tslib
将下载的源码拷贝到ubuntu虚拟机中,然后解压:
1 | tar xvf tslib-1.21.tar.bz2 |
编译 tslib 的时候需要先在 ubuntu 中安装一些文件
1 | sudo apt-get install autoconf |
在 ubuntu 中创建一个名为“tslib”的目录存放编译结果,然后执行以下指令进行编译:
1 | cd tslib-1.21/ |
编译完成后,make install会将编译成果复制到指定的tslib目录中:
可以看到最终编译生成的是5个文件夹。
1.1.2 开发板上配置tslib
将编译出的5个文件夹整个复制到开发板的根文件系统中:
1 | sudo cp * -rf ~/myTest/nfs/rootfs/ |
然后打开板子的**/etc/ts.conf** 文件,找到下面这一行:
1 | module_raw input |
如果这句前面有“#”注释,就删除掉“#“,我这个默认是没有的,所以不用修改
打开板子的**/etc/profile**文件,我的板子此时没有这个文件,所以我新建了一个该文件,然后在里面加入如下内容:
1 | export TSLIB_TSDEVICE=/dev/input/event2 |
- TSLIB_TSDEVICE :触摸设备文件,要根据具体情况设置为/dev/input/event1还是event2(如果接口鼠标键盘,这个编号可能还会变,比如我接了无线键盘后,触摸就又变成了event)
- TSLIB_CALIBFILE :校准文件,此文件可以不存在,校准的时候会自动生成
- **TSLIB_CONFFILE **:触摸配置文件,在移植 tslib 的时候会生成
- TSLIB_PLUGINDIR :tslib 插件目录位置
- TSLIB_CONSOLEDEVICE :控制台设置,这里不设置,设为none
- TSLIB_FBDEVICE:FB 设备,也就是屏幕,也要根据实际情况配置设置为/dev/fb0或是其它
1.2 tslib库测试
1.2.1 屏幕校准
电容屏可以不用校准,不过也可以看看tslib的校准测试用例,输入如下指令:
1 | ts_calibrate |
校准完成以后如果不满意,删除掉/etc/pointercal文件即可
1.2.2 多点触摸拖拽测试
使用如下指令:
1 | ts_test_mt |
然后会出现一个触摸测试界面,先测试Drag功能,手指接触屏幕后进行移动,屏幕上的十字标记就会跟着移动:
1.2.3 多点触摸划线测试
还是刚才的指令,再来测试Draw功能,手指接触屏幕后进行移动,屏幕上就会出现滑过的轨迹线:
2 多点触摸(MT)协议讲解
多点触摸协议,即Multi-touch (MT) Protocol,该协议的介绍,在linux内核源码中有对应的文档,如下图:
多点电容触摸的协议分为两种类型:TypeA和TypeB,目前基本都是使用TypeB协议。
- TypeA协议适用于触摸点不能被区分或者追踪,此类设备上报原始数据。
- TypeB协议适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过slot更新某一个触摸点的信息。
触摸点的信息通过一系列的 ABS_MT事件上报给linux内核,这些事件的定义在include/uapi/linux/input.h中:
比较常用的有:
- ABS_MT_SLOT :上报触摸点ID
- ABS_MT_POSITION_X:上报触摸点的X坐标信息
- ABS_MT_POSITION_Y:上报触摸点的Y坐标信息
- ABS_MT_TRACKING_ID:TypeB区分触摸点
下面具体介绍两种协议的区别。
2.1 TypeA协议
TypeA协议适用于触摸点不能被区分或者追踪,此类设备上报原始数据。
TypeA协议发送触摸点信息的时序如下(以 2 个触摸点为例):
1 | ABS_MT_POSITION_X x[0] |
- 首先每上报一个点的x和y
- 然后上报一个SYN_MT_REPORT
- 依次循环上报其它点
- 所有的点上报完后,再上报一个SYN_REPORT
当第一个触点离开后,上报的时序如下(就是只上报剩下的那一个):
1 | ABS_MT_POSITION_X x[1] |
当第二个触点也离开后,上报的时序如下(就是上报空数据):
1 | SYN_MT_REPORT |
如果驱动除了ABS_MT事件外还上报BTN_TOUCH或ABS_PRESSURE之一,则最后一个SYN_MT_REPORT事件可能被忽略。另外,最后的SYN_REPORT会被输入内核放弃,从而导致没有空触事件到达用户层。
2.2 TypeB协议
TypeB协议适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过slot更新某一个触摸点的信息。
TypeA协议发送触摸点信息的时序如下(以 2 个触摸点为例):
1 | ABS_MT_SLOT 0 |
- 每个数据点前,先上报ABS_MT_SLOT事件,带上一个触摸点ID,此ID由触摸IC提供
- TypeB要求每个SLOT须关联一个ABS_MT_TRACKING_ID,这个ID由linux内核自动分配
- 然后上报一个点的x和y
- 依次循环上报其它点
- 所有的点上报完后,再上报一个SYN_REPORT。
当触点45在X方向上移动后,上报的时序如下:
1 | ABS_MT_SLOT 0 |
当slot 0中触点离开后,上报的时序如下:
1 | ABS_MT_TRACKING_ID -1 |
由于slot被修改为0,因此这个ABS_MT_SLOT被忽略。这条信息移除了slot 0和触点45的联系,因此销毁触点45同时释放slot 0给另外的触点再次使用。
当第二个触点离开后,上报的时序如下:
1 | ABS_MT_SLOT 1 |
总结对比一下两个触摸协议的区别:
2.3 多点触摸API函数
了解了两种触摸协议,在编程时,就要使用其相应的API函数来实现触摸数据的上报,下面是常用的API函数。
2.3.1 input_mt_init_slots
该函数用于初始化MT的输入slots,其函数原型如下:
1 | /** |
其中第3个参数,可设置的flags包括:
1 |
可以使用‘|’运算来同时设置多个flags标识
2.3.2 input_mt_slot
该函数用于Type B类型,用于产生 ABS_MT_SLOT事件,其函数原型如下:
1 | /** |
2.3.3 input_mt_report_slot_state
该函数用于Type B类型,用于产生ABS_MT_TRACKING_ID和ABS_MT_TOOL_TYPE事件,其函数原型如下:
1 | /** |
其中第2个参数,tool_type包括:
- MT_TOOL_FINGER:手指
- MT_TOOL_PEN:笔
- MT_TOOL_PALM:手掌
其中第3个参数,active包括:
- true: 连续触摸, input子系统内核会自动分配一个ABS_MT_TRACKING_ID给slot
- false:触摸点抬起,表示某个触摸点无效了,input子系统内核会分配一个-1给slot
2.3.4 input_report_abs
该函数用于上报触摸点坐标,TypeA和TypeB类型都使用此函数上报触摸点坐标信息,其函数原型如下:
1 | /** |
其中第2个参数,code包括:
- ABS_MT_POSITION_X
- ABS_MT_POSITION_Y
2.3.5 input_mt_report_pointer_emulation
如果追踪到的触摸点数量多于当前上报的数量,驱动程序使用 BTN_TOOL_TAP 事件来通知用户空间当前追踪到的触摸点总数量,然后调用 input_mt_report_pointer_emulation 函数将use_count 参数设置为 false,否则的话将 use_count 参数设置为 true。
1 | /** |
3 input子系统上报数据含义讲解
3.1 input子系统简介
在Linux中,对于输入设备,例如按键、 鼠标、 键盘、 触摸屏等,为了更加方便统一的管理, Linux内核为此专门做了一个input子系统的框架来处理输入事件。
input是输入的意思,就是管理输入的子系统,和 pinctrl、gpio 子系统一样,都是 Linux 内核针对某一类设备而创建的框架。input 子系统框架图如下:
3.2 input输出事件
3.2.1 事件类型
evbit 表示输入事件类型,可选的事件类型定义在 include/uapi/linux/input.h 文件中,事件类型如下:
各个的含义为:
1 |
例如,如果要使用按键的inpu件功能,就需要注册EV_KEY事件,若还要使用连按功能,需要注册EV_REP事件。
如果要使用触摸屏的inpu件功能,就需要注册EV_KEY事件,
3.2.2 按键值类型
evbit、keybit、relbit 等等都是存放不同事件对应的值,Linux 内核定义了很多按键值:
1 |
|
具体的定义在input.h文件中:
3.3 触摸数据上报实例分析
上篇文章只是将触摸坐标打印到了屏幕,实际是使用触摸屏时,需要将坐标数据通过input子系统上报应用层,现在来具体分析一下input子系统上报的这些数据的含义,例如按下触摸键后,串口会有如下打印:
将数据内容摘出来看:
1 | /*****************input_event 类型********************/ |
- type 为事件类型
- 0000:EV_SYN,同步事件
- 0001:EV_KEY,按键事件
- 0003:EV_ABS,绝对坐标事件
- code 为事件编码,也就是按键号
- 0000:ABS_X,单点触摸上报X坐标值
- 0001:ABS_Y,单点触摸上报Y坐标值
- 0035:ABS_MT_POSITION_X,多点触摸上报X坐标值
- 0036:ABS_MT_POSITION_Y,多点触摸上报Y坐标值
- 0039:ABS_MT_TRACKING_ID,触摸点的track id
- 014a:BTN_TOUCH,触摸按键
- value 就是按键值, 为 1 表示按下, 为 0 的话表示松开
来分析一下每行输出的含义:
第1行:绝对坐标事件,触摸点的track id,id=0
第2行:绝对坐标事件,多点触摸X坐标值,X=0x9d (157)
第3行:绝对坐标事件,多点触摸Y坐标值,Y=0xc1 (193)
第4行:按键事件,触摸按键,1表示按键按下
第5行:绝对坐标事件,单点触摸X坐标值,X=0x9d (157)
第6行:绝对坐标事件,单点触摸Y坐标值,Y=0xc1 (193)
第7行:同步事件,由input_sync函数上报
第8行:绝对坐标事件,触摸点的track id,id=0xffffffff=-1,即触摸点离开了屏幕
第9行:按键事件,触摸按键,0表示没有按键
第10行:同步事件,由input_sync函数上报
注:上面的打印,有多点触摸和单点触摸的上报,实际上如果使用了多点触摸,可以将单点触摸的上报去掉,如下:
去掉后,再次测试,可以看到只有多点触摸数据的上报:
4 将触摸驱动编译到内核
自己编写的触摸驱动,每次系统启动后,都要手动加载驱动模块后才能使用,比较麻烦,现在驱动文件不需要再改了,就可以将自己的驱动直接编译到内核中。方法如下:
将自己写的触摸屏驱动文件拷贝到Linux内核的drivers/input/touchscreen/目录下:
1 | cp gt911.c ../../kernel/nxp_kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga/drivers/input/touchscreen/ -f |
修改 drivers/input/touchscreen 目录下的 Makefile,在最下面添加下面一行:
1 | obj-y += gt911.o |
然后(使用之前编写的编译脚本)重新编译linux内核
再将zImage拷贝到板子中,重新启动板子。
正常情况下,在内核启动的时候就打印出触摸驱动的event编号信息,我这里确实也打印了,只是随后一直刷IIC错误:
暂时看不出来是什么原因,才这居打印看,触摸开始读数据时才会进到这里,感觉像是触摸驱动刚加载完成,就触发了中断,但在中断里通过IIC读取触摸数据时,又出现了问题。。。
一个暂时的替代方式是,可以在开机自启动文件中进行触摸驱动的加载,在/etc/init.d/rcS文件中补充如下语句即可:
1 | cd /lib/modules/4.1.15 |
5 总结
本篇首先介绍了测试触摸是库——tslib,使用它可以进行图形化的触摸测试。随后,又分析触摸协议上报的原理以及通过input子系统上报的数据的具体含义。
附:演示视频
https://www.bilibili.com/video/BV1XL4y1t7kf?spm_id_from=333.999.0.0