嵌入式-linux-驱动

input子系统

基于 input 子系统注册成功的输入设备,都会在/dev/input 目录下生成对应的设备节点(设备文件), 设备节点名称通常为 eventX(X 表示一个数字编号 0、 1、 2、 3 等),譬如/dev/input/event0、 /dev/input/event1、/dev/input/event2 等

应用程序如何解析数据

应用程序打开设备对应的设备文件对其进行读操作,获取的数据是一个结构体struct input_event 结构体类型数据定义在<linux/input.h>

1
2
3
4
5
6
struct input_event {
struct timeval time; //内核会记录每个上报的事件其发生的时间,并通过变量 time 返回给应用程序
__u16 type;
__u16 code;
__s32 value;
};

type: type 用于描述发生了哪一种类型的事件(对事件的分类) , Linux 系统所支持的输入事件类型如下所示:定义也是在<linux/input.h>头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*
* Event types
*/
#define EV_SYN 0x00 //同步类事件,用于同步事件
#define EV_KEY 0x01 //按键类事件
#define EV_REL 0x02 //相对位移类事件(譬如鼠标)
#define EV_ABS 0x03 //绝对位移类事件(譬如触摸屏) 全称是 Event Absolute
#define EV_MSC 0x04 //其它杂类事件
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)

code: code 表示该类事件中的哪一个具体事件, 以上列举的每一种事件类型中都包含了一系列具体事件, 譬如一个键盘上通常有很多按键, 譬如字母 A、 B、 C、 D 或者数字 1、 2、 3、 4 等, 而 code变量则告知应用程序是哪一个按键发生了输入事件。每一种事件类型都包含多种不同的事件,譬如
按键类事件:

1
2
3
4
5
6
7
8
9
10
#define KEY_RESERVED 0
#define KEY_ESC 1 //ESC 键
#define KEY_1 2 //数字 1 键
#define KEY_2 3 //数字 2 键
#define KEY_TAB 15 //TAB 键
#define KEY_Q 16 //字母 Q 键
#define KEY_W 17 //字母 W 键
#define KEY_E 18 //字母 E 键
#define KEY_R 19 //字母 R 键
……

相对位移事件:触摸屏设备是一种绝对位移设备,它能够产生绝对位移事件 ; 譬如对于触摸屏来说,一个触摸点所包含的信息可能有多种,譬如触摸点的 X 轴坐标、 Y 轴坐标、 Z 轴坐标、按压力大小以及接触面积等, 所以 code变量告知应用程序当前上报的是触摸点的哪一种信息(X 坐标还是 Y 坐标、亦或者其它);绝对位移事件如下:

1
2
3
4
5
6
7
8
9
10
11
12
#define REL_X 0x00 //X 轴
#define REL_Y 0x01 //Y 轴
#define REL_Z 0x02 //Z 轴
#define REL_RX 0x03
#define REL_RY 0x04
#define REL_RZ 0x05
#define REL_HWHEEL 0x06
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
#define REL_MAX 0x0f
#define REL_CNT (REL_MAX+1)

绝对位移事件

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
#define ABS_X 0x00 //X 轴
#define ABS_Y 0x01 //Y 轴
#define ABS_Z 0x02 //Z 轴
#define ABS_RX 0x03
#define ABS_RY 0x04
#define ABS_RZ 0x05
#define ABS_THROTTLE 0x06
#define ABS_RUDDER 0x07
#define ABS_WHEEL 0x08
#define ABS_GAS 0x09
#define ABS_BRAKE 0x0a
#define ABS_HAT0X 0x10
#define ABS_HAT0Y 0x11
#define ABS_HAT1X 0x12
#define ABS_HAT1Y 0x13
#define ABS_HAT2X 0x14
#define ABS_HAT2Y 0x15
#define ABS_HAT3X 0x16
#define ABS_HAT3Y 0x17
#define ABS_PRESSURE 0x18
#define ABS_DISTANCE 0x19
#define ABS_TILT_X 0x1a
#define ABS_TILT_Y 0x1b
#define ABS_TOOL_WIDTH 0x1c
......

在<linux/input.h>头文件(这些宏其实是定义在input-event-codes.h 头文件中,该头文件被<linux/input.h>所包含了)

value: 内核每次上报事件都会向应用层发送一个数据 value, 对 value 值的解释随着 code 的变化而变化。譬如对于按键事件(type=1) 来说, 如果 code=2(键盘上的数字键 1,也就是 KEY_1), 那么如果 value 等于 1,则表示 KEY_1 键按下; value 等于 0 表示 KEY_1 键松开,如果 value 等于 2则表示 KEY_1 键长按。再比如, 在绝对位移事件中(type=3),如果 code=0 (触摸点 X 坐标 ABS_X),那么 value 值就等于触摸点的 X 轴坐标值; 同理, 如果 code=1(触摸点 Y 坐标 ABS_Y),此时value 值便等于触摸点的 Y 轴坐标值; 所以对 value 值的解释需要根据不同的 code 值而定!

数据同步

应用程序读取输入设备上报的数据时,一次 read 操作只能读取一个 struct input_event 类型数据,譬如对于触摸屏来说,一个触摸点的信息包含了 X 坐标、 Y 坐标以及其它信息, 对于这样情况,应用程序需要执行多次 read 操作才能把一个触摸点的信息全部读取出来, 这样才能得到触摸点的完整信息。那么应用程序如何得知本轮已经读取到完整的数据了呢?其实这就是通过同步事件来实现的, 内核将本轮需要上报、发送给接收者的数据全部上报完毕后,接着会上报一个同步事件,以告知应用程序本轮数据已经完整、 可以进行同步了。

同步类事件中也包含了多种不同的事件,如下所示:

1
2
3
4
5
6
7
8
9
/*
* Synchronization events.
*/
#define SYN_REPORT 0
#define SYN_CONFIG 1
#define SYN_MT_REPORT 2
#define SYN_DROPPED 3
#define SYN_MAX 0xf
#define SYN_CNT (SYN_MAX+1)

判断设备和设备文件的对应

通过od查看设备文件 然后操作设备 如果有数据打印 即对应

必须sudo才可以查看设备文件

触摸屏应用

单点触摸设备

事件上报顺序为

# 点击触摸屏时

BTN_TOUCH (code=0x14a,也就是 330) value=1按下 value=0松开

ABS_X

ABS_Y

SYN_REPORT

#滑动

ABS_X

ABS_Y

SYN_REPORT

#松开

BTN_TOUCH

SYN_REPORT

多点触摸设备-MT协议

在 Linux 内核中, 多点触摸设备使用多点触摸(MT)协议上报各个触摸点的数据, MT 协议分为两种类型: Type A 和 Type B, Type A 协议实际使用中用的比较少,几乎处于淘汰的边缘, 这里就不再给大家介绍了,我们重点来看看 Type B 协议

Type B 协议下多点触摸设备上报数据的流程列举如下:

ABS_MT_SLOT 0 (code=47)

ABS_MT_TRACKING_ID 10 (code=57)

ABS_MT_POSITION_X

ABS_MT_POSITION_Y

ABS_MT_SLOT 1

ABS_MT_TRACKING_ID 11

ABS_MT_POSITION_X

ABS_MT_POSITION_Y

SYN_REPORT

slot 是硬件上的一个概念 对于一个多点触摸设备来说,它最大支持的触摸点数是确定的,譬如 5 个触摸设备,最多支持 5 个触摸点 按照时间先后顺序来的,譬如第一根手指先触碰到触摸屏,那第一根手指就对应触摸点 0(slot=0),接着第二根手指触碰到触摸屏则对应触摸点 1(slot=1)以此类推! 这个通常是硬件所支持的。

ID 可认为是软件上的一个概念 它也用于区分不同的触摸点,但是它跟 slot 不同, 不是同一层级的概念;举个例子,譬如一根手指触碰到触摸屏之后拿开,然后再次触碰触摸屏,这个过程中,假设只有这一根手指进行触碰操作,那么两次触碰对应都是触摸点 0(slot=0),这个无疑义!但从触摸点的生命周期来看, 它们是同一个触摸点吗?答案肯定不是,为啥呢?手指从触摸屏上离开后,该触摸点就消失了、被删除了, 该触摸点的生命周期也就到此结束了,所以它们自然是不同的触摸点, 所以它们的 ID 是不同的。

单点的

两触摸点的

可以看出slot0不会出现 直接出现slot1

获取触摸屏信息

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
//定义
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);



//使用 EVIOCGNAME 宏获取设备名称
char name[100];
ioctl(fd, EVIOCGNAME(sizeof(name)), name);



//获取触摸屏支持的最大触摸点数
struct input_absinfo info;
int max_slots; //最大触摸点数
if (0 > ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &info))
perror("ioctl error");
max_slots = info.maximum + 1 - info.minimum;

struct input_absinfo {
__s32 value; //最新的报告值
__s32 minimum; //最小值
__s32 maximum; //最大值
__s32 fuzz;
__s32 flat;
__s32 resolution;
};

tslib 库

tslib 是专门为触摸屏设备所开发的 Linux 应用层函数库,并且是开源

tslib 为触摸屏驱动和应用层之间的适配层, 它把应用程序中读取触摸屏 struct input_event 类型数据(这是输入设备上报给应用层的原始数据)并进行解析的操作过程进行了封装,向使用者提供了封装好的 API 接口。 tslib 从触摸屏中获得原始的坐标数据, 并通过一系列的去噪、去抖、坐标变换等操作,来去除噪声并将原始的触摸屏坐标转换c的屏幕坐标。

tslib 有一个配置文件 ts.conf, 该配置文件中提供了一些配置参数

tslib 可以作为 Qt 的触摸屏输入插件,为 Qt 提供触摸输入支持, 如果在嵌入式 Linux 硬件平台下开发过Qt 应用程序的读者应该知道;当然,并不是只有 tslib 才能作为 Qt 的插件、为其提供触摸输入支持,还有很多插件都可以,只不过大部分都会选择使用 tslib

源码下载地址:https://github.com/libts/tslib/releases ALPHA/Mini 开发板出厂系统中已经移植了 tslib,并且版本为 1.16

ts_finddev 命令查看到它的版本信息