【i.MX6ULL】在i.MX 6Ull上驱动电容触摸屏 开始今天的分享,首先,我们先来介绍一会测试触摸的库——tslib,使用它可以进行图形化的触摸测试。之后,再回头来分析分析触摸协议上报的原理以及通过input子系统上报的数据的具体含义。
1 先介绍一下tslib
Tslib是一个开源的程序,能够为触摸屏驱动获得的采样提供诸如滤波、去抖、校准等功能,通常作为触摸屏驱动的适配层,为上层的应用提供了一个统一的接口。
1.1 tslib库移植
首先下载tslib库的源码:https://github.com/libts/tslib/tags,本篇使用的1.21版本,大家可以选择最新版本。
1.1.1 ubuntu上编译tslib
将下载的源码拷贝到ubuntu虚拟机中,然后解压:
- tar xvf tslib-1.21.tar.bz2
复制代码 编译 tslib 的时候需要先在 ubuntu 中安装一些文件
- sudo apt-get install autoconf
- sudo apt-get install automake
- sudo apt-get install libtool
复制代码 在 ubuntu 中创建一个名为“tslib”的目录存放编译结果,然后执行以下指令进行编译:
- cd tslib-1.21/
- ./autogen.sh
- ./configure --host=arm-linux-gnueabihf --prefix=/home/xxpcb/myTest/imx6ull/otherlib/tslib/tslib/
- make
- make install
复制代码 编译完成后,make install会将编译成果复制到指定的tslib目录中:
可以看到最终编译生成的是5个文件夹。
1.1.2 开发板上配置tslib
将编译出的5个文件夹整个复制到开发板的根文件系统中:
- sudo cp * -rf ~/myTest/nfs/rootfs/
复制代码 然后打开板子的/etc/ts.conf 文件,找到下面这一行:
如果这句前面有“#”注释,就删除掉“#“,我这个默认是没有的,所以不用修改
打开板子的/etc/profile文件,我的板子此时没有这个文件,所以我新建了一个该文件,然后在里面加入如下内容:
- export TSLIB_TSDEVICE=/dev/input/event2
- export TSLIB_CALIBFILE=/etc/pointercal
- export TSLIB_CONFFILE=/etc/ts.conf
- export TSLIB_PLUGINDIR=/lib/ts
- export TSLIB_CONSOLEDEVICE=none
- export TSLIB_FBDEVICE=/dev/fb0
复制代码 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的校准测试用例,输入如下指令:
校准完成以后如果不满意,删除掉/etc/pointercal文件即可
1.2.2 多点触摸拖拽测试
使用如下指令:
然后会出现一个触摸测试界面,先测试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 个触摸点为例):
- ABS_MT_POSITION_X x[0]
- ABS_MT_POSITION_Y y[0]
- SYN_MT_REPORT
- ABS_MT_POSITION_X x[1]
- ABS_MT_POSITION_Y y[1]
- SYN_MT_REPORT
- SYN_REPORT
复制代码 ·首先每上报一个点的x和y
·然后上报一个SYN_MT_REPORT
·依次循环上报其它点
·所有的点上报完后,再上报一个SYN_REPORT
当第一个触点离开后,上报的时序如下(就是只上报剩下的那一个):
- ABS_MT_POSITION_X x[1]
- ABS_MT_POSITION_Y y[1]
- SYN_MT_REPORT
- SYN_REPORT
复制代码 当第二个触点也离开后,上报的时序如下(就是上报空数据):
如果驱动除了ABS_MT事件外还上报BTN_TOUCH或ABS_PRESSURE之一,则最后一个SYN_MT_REPORT事件可能被忽略。另外,最后的SYN_REPORT会被输入内核放弃,从而导致没有空触事件到达用户层。
2.2 TypeB协议
TypeB协议适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过slot更新某一个触摸点的信息。
TypeA协议发送触摸点信息的时序如下(以 2 个触摸点为例):
- ABS_MT_SLOT 0
- ABS_MT_TRACKING_ID 45
- 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
复制代码 每个数据点前,先上报ABS_MT_SLOT事件,带上一个触摸点ID,此ID由触摸IC提供
TypeB要求每个SLOT须关联一个ABS_MT_TRACKING_ID,这个ID由linux内核自动分配
然后上报一个点的x和y
依次循环上报其它点
所有的点上报完后,再上报一个SYN_REPORT。
当触点45在X方向上移动后,上报的时序如下:
- ABS_MT_SLOT 0
- ABS_MT_POSITION_X x[0]
- SYN_REPORT
复制代码 当slot 0中触点离开后,上报的时序如下:
- ABS_MT_TRACKING_ID -1
- SYN_REPORT
复制代码 由于slot被修改为0,因此这个ABS_MT_SLOT被忽略。这条信息移除了slot 0和触点45的联系,因此销毁触点45同时释放slot 0给另外的触点再次使用。
当第二个触点离开后,上报的时序如下:
- ABS_MT_SLOT 1
- ABS_MT_TRACKING_ID -1
- SYN_REPORT
复制代码 总结对比一下两个触摸协议的区别:
2.3 多点触摸API函数
了解了两种触摸协议,在编程时,就要使用其相应的API函数来实现触摸数据的上报,下面是常用的API函数。
2.3.1 input_mt_init_slots
该函数用于初始化MT的输入slots,其函数原型如下:
- /**
- * dev: MT设备对应的input_dev
- * num_slots: 设备要使用的slot的数量,也就是触摸点的数量
- * flags: 其他一些flags信息
- * return: 0-成功 负值-失败
- */
- int input_mt_init_slots(struct input_dev *dev,
- unsigned int num_slots,
- unsigned int flags)
复制代码 其中第3个参数,可设置的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标识
2.3.2 input_mt_slot
该函数用于Type B类型,用于产生 ABS_MT_SLOT事件,其函数原型如下:
- /**
- * dev: MT设备对应的input_dev
- * slot: 当前发送的是哪个slot的坐标信息,也就是哪个触摸点
- * return: 无
- */
- void input_mt_slot(struct input_dev *dev, int slot)
复制代码 2.3.3 input_mt_report_slot_state
该函数用于Type B类型,用于产生ABS_MT_TRACKING_ID和ABS_MT_TOOL_TYPE事件,其函数原型如下:
- /**
- * dev: MT设备对应的input_dev
- * tool_type: 触摸类型
- * active: 触摸或抬起
- * return: 无
- */
- void input_mt_report_slot_state(struct input_dev *dev,
- unsigned int tool_type,
- bool active)
复制代码 其中第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类型都使用此函数上报触摸点坐标信息,其函数原型如下:
- /**
- * dev: MT设备对应的input_dev
- * code: 要上报的是什么数据
- * value: 要上报的数据值
- * return: 无
- */
- void input_report_abs(struct input_dev *dev,
- unsigned int code,
- int value)
复制代码 其中第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。
- /**
- * dev: MT设备对应的input_dev
- * use_count: true-有效的触摸点数量 false-追踪到的触摸点数量多于当前上报的数量
- * return: 无
- */
- void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
复制代码 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 文件中,事件类型如下:
各个的含义为:
- #define EV_SYN 0x00 /* 同步事件 */
- #define EV_KEY 0x01 /* 按键事件 */
- #define EV_REL 0x02 /* 相对坐标事件 */
- #define EV_ABS 0x03 /* 绝对坐标事件 */
- #define EV_MSC 0x04 /* 杂项(其他)事件 */
- #define EV_SW 0x05 /* 开关事件 */
- #define EV_LED 0x11 /* LED */
- #define EV_SND 0x12 /* sound(声音) */
- #define EV_REP 0x14 /* 重复事件 */
- #define EV_FF 0x15 /* 压力事件 */
- #define EV_PWR 0x16 /* 电源事件 */
- #define EV_FF_STATUS 0x17 /* 压力状态事件 */
复制代码 例如,如果要使用按键的inpu件功能,就需要注册EV_KEY事件,若还要使用连按功能,需要注册EV_REP事件。
如果要使用触摸屏的inpu件功能,就需要注册EV_KEY事件,
3.2.2 按键值类型
evbit、keybit、relbit 等等都是存放不同事件对应的值,Linux 内核定义了很多按键值:
- #define KEY_RESERVED 0
- #define KEY_ESC 1
- #define KEY_1 2
- #define KEY_2 3
- #define KEY_3 4
- #define KEY_4 5
- //......
- #define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */
- #define BTN_TOUCH 0x14a
- #define BTN_STYLUS 0x14b
- //......
- #define ABS_X 0x00
- #define ABS_Y 0x01
- #define ABS_Z 0x02
- #define ABS_RX 0x03
- #define ABS_RY 0x04
- #define ABS_RZ 0x05
- #define ABS_MT_SLOT 0x2f /* MT slot being modified */
- #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
- #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
- #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
- #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
- #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
- #define ABS_MT_POSITION_X 0x35 /* Center X touch position */
- #define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */
- #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
- #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
- #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
- #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
- #define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
- #define ABS_MT_TOOL_X 0x3c /* Center X tool position */
- #define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
复制代码 具体的定义在input.h文件中:
3.3 触摸数据上报实例分析
实际是使用触摸屏时,需要将坐标数据通过input子系统上报应用层,现在来具体分析一下input子系统上报的这些数据的含义,例如按下触摸键后,串口会有如下打印:
将数据内容摘出来看:
- /*****************input_event 类型********************/
- /*编号*/ /*tv_sec*/ /*tv_usec*/ /*type*/ /*code*/ /*value*/
- 0000000 00f6 0000 e539 0003 0003 0039 0000 0000
- 0000010 00f6 0000 e539 0003 0003 0035 009d 0000
- 0000020 00f6 0000 e539 0003 0003 0036 00c1 0000
- 0000030 00f6 0000 e539 0003 0001 014a 0001 0000
- 0000040 00f6 0000 e539 0003 0003 0000 009d 0000
- 0000050 00f6 0000 e539 0003 0003 0001 00c1 0000
- 0000060 00f6 0000 e539 0003 0000 0000 0000 0000
- 0000070 00f6 0000 11ad 0005 0003 0039 ffff ffff
- 0000080 00f6 0000 11ad 0005 0001 014a 0000 0000
- 0000090 00f6 0000 11ad 0005 0000 0000 0000 0000
复制代码 ·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/目录下:
- 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,在最下面添加下面一行:
然后(使用之前编写的编译脚本)重新编译linux内核
再将zImage拷贝到板子中,重新启动板子。
正常情况下,在内核启动的时候就打印出触摸驱动的event编号信息,我这里确实也打印了,只是随后一直刷IIC错误:
暂时看不出来是什么原因,才这居打印看,触摸开始读数据时才会进到这里,感觉像是触摸驱动刚加载完成,就触发了中断,但在中断里通过IIC读取触摸数据时,又出现了问题。。。
一个暂时的替代方式是,可以在开机自启动文件中进行触摸驱动的加载,在/etc/init.d/rcS文件中补充如下语句即可:
- cd /lib/modules/4.1.15
- depmod
- modprobe gt911.ko
- cd /
复制代码 5 总结
本篇首先介绍了测试触摸是库——tslib,使用它可以进行图形化的触摸测试。随后,又分析触摸协议上报的原理以及通过input子系统上报的数据的具体含义。
|