触屏需要的知识点

首先触摸屏的驱动,也是使用之前使用input的知识点。

通过注册函数注册到内核,

触控屏幕,电容屏幕是控制 IC ,所以要使用到IIC的驱动,因为触摸 IC 提供了中断信号引脚(INT),可以通过中断来获取触摸信息 ,上报屏幕坐标等信息需要使用input的系统上报。
单点触摸和多点触摸
触摸屏分为多点触摸屏幕和单点触摸屏幕,单点触摸设备只支持单点触摸,一个同步事件完成数据只包含一个触摸点信息,单点触摸设备以ABS_XXX事件承载、上报触摸点的信息,有的设备还支持其他事件例如压力,z轴坐标等数据。
多点电容触摸的(Multi-touch,简称 MT), MT 协议被分为两种类型, TypeA 和 TypeB,这两种类型的区别如下:
Type A:适用于触摸点不能被区分或者追踪,此类型的设备上报原始数据(此类型在实际使用中非常少! )。
Type B:适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过 slot 更新某一个触摸点的信息, FT5426 就属于此类型,一般的多点电容触摸屏 IC 都有此能力。

触摸点的信息通过一系列的 ABS_MT 事件(有的资料也叫消息)上报给 linux 内核 ,ABS_MT 事件定义在文件 include/uapi/linux/input.h中。

1
2
void input_mt_sync(struct input_dev *dev)			//A型隔离触摸点的数据
void input_mt_slot(struct input_dev *dev, int slot) //B型区分触摸点数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
			/*对于 Type A 类型的设备,发送触摸点信息的时序*/
ABS_MT_POSITION_X x[0] //通过 ABS_MT_POSITION_X 事件上报第一个触摸点的 X 坐标数据
ABS_MT_POSITION_Y y[0] //通过 ABS_MT_POSITION_Y 事件上报第一个触摸点的 Y 坐标数据。
SYN_MT_REPORT //上报 SYN_MT_REPORT 事件,通过调用 input_mt_sync 函数来实现。
ABS_MT_POSITION_X x[1] //通过 ABS_MT_POSITION_X 事件上报第二个触摸点的 X 坐标数据。
ABS_MT_POSITION_Y y[1] //通过 ABS_MT_POSITION_Y 事件上报第二个触摸点的 Y 坐标数据。
SYN_MT_REPORT //上报 SYN_MT_REPORT 事件,通过调用 input_mt_sync 函数来实现。
SYN_REPORT //上报 SYN_REPORT 事件,通过调用 input_sync 函数实现。
/*对于 Type B 类型的设备,发送触摸点信息的时序*/
ABS_MT_SLOT 0 //上报 ABS_MT_SLOT 事件,也就是触摸点对应的 SLOT。
ABS_MT_TRACKING_ID 45 /*根据 Type B 的要求,每个 SLOT 必须关联一个 ABS_MT_TRACKING_ID,通过修改 SLOT 关联的 ABS_MT_TRACKING_ID 来完成对触摸点的添加、替换或删除。*/
ABS_MT_POSITION_X x[0] //上报触摸点 0 的 X 轴坐标,使用函数 input_report_abs 来完成
ABS_MT_POSITION_Y y[0] //上报触摸点 0 的 Y 轴坐标
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函数来完成。

以上是TypeA和B 类型的时序,使用多点触摸驱动的时候就需要以上的时序上报坐标信息。

多点触摸所使用到的 API 函数

  1. input_mt_init_slots 函数用于初始化 MT 的输入 slots,编写 MT 驱动的时候必须先调用此函数初始化 slots。

    1
    2
    3
    int	input_mt_init_slots( struct input_dev *dev,	//MT 设备对应的 input_dev,因为 MT 设备隶属于 input_dev。
    unsigned int num_slots, //设备要使用的 SLOT 数量,也就是触摸点的数量。
    unsigned int flags) //其他一些 flags 信息,
  2. input_mt_slot 函数 此函数用于 Type B 类型,此函数用于产生 ABS_MT_SLOT 事件,告诉内核当前上报的是哪个触摸点的坐标数据

    1
    void input_mt_slot(struct input_dev *dev,int 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 事 件 指 定 触 摸 类 型 ( 是 笔 还 是 手 指 等 )。

    1
    2
    3
    4
    5
    6
    7
    void 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,表示触摸
    点溢出。*/
  4. input_report_abs 函数Type A 和 Type B 类型都使用此函数上报触摸点坐标信息,通过 ABS_MT_POSITION_X 和ABS_MT_POSITION_Y 事 件 实 现 X 和 Y 轴 坐 标 信 息 上 报 。

    1
    2
    3
    4
    5
    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,表示当前的触摸点数量(函数会获取到具体的触摸点数量,不需要用户给出)

    1
    2
    3
    void input_mt_report_pointer_emulation(struct input_dev *dev,bool use_count)
    /*dev: MT 设备对应的 input_dev。
    use_count: true,有效的触摸点数量; false,追踪到的触摸点数量多于当前上报的数量。*/

多点电容触摸驱动框架

①、多点电容触摸芯片的接口,一般都为 I2C 接口,因此驱动主框架肯定是 I2C。
②、 linux 里面一般都是通过中断来上报触摸点坐标信息,因此需要用到中断框架。
③、多点电容触摸属于 input 子系统,因此还要用到 input 子系统框架。
④、在中断处理程序中按照 linux 的 MT 协议上报坐标信息。

IIC驱动框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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");

初始化触摸 IC、中断和 input 子系统

在probe函数中完成初始化中断,input等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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 }

上报坐标信息

typeB时序的设备多,按照typeB的时序来进行抒写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 }

移植tslib库

tslib 是一个开源的第三方库,用于触摸屏性能调试,使用电阻屏的时候一般使用 tslib 进行校准。虽然电容屏不需要校准,但是由于电容屏加工的原因,有的时候其不一定精准,因此有时候也需要进行校准。最主要的是 tslib 提供了一些其他软件,我们可以通过这些软件来测试触摸屏工作是否正常。最新版本的 tslib 已经支持了多点电容触摸屏,因此可以通过 tslib 来直观的测试多点电容触摸屏驱动,这个要比观看 eventX 原始数据方便的多。
tslib 的移植很简单,步骤如下:
1、获取 tslib 源码
首先肯定是获取 tslib 的源码, git 地址为 https://github.com/kergoth/tslib,目前最新的版本是1.21。
2、修改 tslib 源码所属用户
修改解压得到的 tslib-1.21 目录所属用户为当前用户,这一步一定要做!否则在稍后的编译
中会遇到各种问题。我当前 ubuntu 的登录用户名为“moss”,那么修改命令如下:

1
sudo chown moss:moss tslib-1.21 -R

3、 ubuntu 工具安装
编译 tslib 的时候需要先在 ubuntu 中安装一些文件,防止编译 tslib 过程中出错,命令如下所示:

1
2
3
sudo apt-get install autoconf
sudo apt-get install automake
sudo apt-get install libtool

4、编译 tslib
首先在 ubuntu 中创建一个名为“tslib”的目录存放编译结果,比如我们创建的 tslib 目录路径为: /home/zuozhongkai/linux/IMX6ULL/tool/tslib。
接下来输入如下命令配置并编译 tslib:

1
2
3
4
5
cd tslib-1.21/ //进入 tslib 源码目录
./autogen.sh
./configure --host=arm-linux-gnueabihf --prefix=/home/moss/linux/tool/tslib
make //编译
make install //安装

注意,在使用./configure 配置 tslib 的时候“–host”参数指定编译器,“–prefix”参数指定编译完成以后的 tslib 文件安装到哪里,这里肯定是安装到我们刚刚创建的“tslib”目录下。
bin 目录下是可执行文件,包括 tslib 的测试工具。 etc 目录下是 tslib 的配置文件, lib 目录下是相关的库文件。将lib的所有文件拷贝到开发板的根文件系统中,命令如下:

1
sudo cp * -rf /home/zuozhongkai/linux/nfs/rootfs

5、 配置 tslib
打开/etc/ts.conf 文件,找到下面这一行:module_raw input
如果上面这句前面有“#”的话就删除掉“#”。打开/etc/profile 文件,在里面加入如下内容:

1
2
3
4
5
6
1 export TSLIB_TSDEVICE=/dev/input/event2
2 export TSLIB_CALIBFILE=/etc/pointercal
3 export TSLIB_CONFFILE=/etc/ts.conf
4 export TSLIB_PLUGINDIR=/lib/ts
5 export TSLIB_CONSOLEDEVICE=none
6 export TSLIB_FBDEVICE=/dev/fb0

第 1 行, TSLIB_TSDEVICE 表示触摸设备文件,这里设置为/dev/input/event2,这个要根据具体情况设置,如果你的触摸设备文件为 event2 那么就应该设置为/dev/input/event2,以此类推。
第 2 行, TSLIB_CALIBFILE 表示校准文件,如果进行屏幕校准的话校准结果就保存在这个文件中,这里设置校准文件为/etc/pointercal, 此文件可以不存在,校准的时候会自动生成。
第 3 行, TSLIB_CONFFILE 表示触摸配置文件,文件为/etc/ts.conf,此文件在移植 tslib 的时候会生成。
第 4 行, TSLIB_PLUGINDIR 表示 tslib 插件目录位置,目录为/lib/ts。
第 5 行, TSLIB_CONSOLEDEVICE 表示控制台设置,这里不设置,因此为 none。
第 6 行, TSLIB_FBDEVICE 表示 FB 设备,也就是屏幕,根据实际情况配置,我的屏幕文件为/dev/fb0,因此这里设置为/dev/fb0。全部配置好以后重启开发板,然后就可以进行测试了。