输入设备之触摸屏
迪丽瓦拉
2025-05-30 01:32:49
0

目录

一、解析触摸屏设备上报的数据

单点触摸和多点触摸

单点触摸设备事件上报的流程大概如下:

 # 点击触摸屏时

# 滑动 

多点触摸设备--事件上报的顺序

MT 协议之 Type B 协议

测试

二、获取触摸屏的信息

 代码编写

代码如下

验证

三、多点触摸APP

验证


一、解析触摸屏设备上报的数据

        触摸屏设备是一个绝对位移设备,可以上报绝对位移事件,绝对位移事件如下:

单点触摸和多点触摸

        触摸屏分为多点触摸设备和单点触摸设备。 单点触摸设备只支持单点触摸, 一轮(一个同步事件称为一轮) 完整的数据只包含一个触摸点信息; 单点触摸设备以 ABS_XXX 事件承载、上报触摸点的信息,譬如 ABS_X(value 值对应的是 X 轴坐标值)、 ABS_Y(value 值对应的是 Y 轴坐标值)等绝对位移事件,而有些设备可能还支持 Z 轴坐标(通过 ABS_Z 事件上报、 value 值对应的便是 Z 轴坐标值)、 按压力大小(通过 ABS_PRESSURE 事件上报、 value 值对应的便是按压力大小) 以及接触面积等属性。 大部分的单点触摸设备都会上报 ABS_X 和 ABS_Y 事件,而其它绝对位移事件则根据具体的设备以及驱动的实现而定!

        多点触摸设备可支持多点触摸,对于多点触摸设备, 一轮完整的数据可能包含多个触摸点信息。 多点触摸设备则是以 ABS_MT_XXX(MT 是 Multi-touch, 意思为: 多点触摸)事件承载、上报触摸点的信息, 如 ABS_MT_POSITION_X(X 轴坐标) 、 ABS_MT_POSITION_Y(Y 轴坐标)等绝对位移事件

        触摸屏设备除了上报绝对位移事件之外,还可以上报按键类事件和同步类事件。因为几乎每一个输入设备都会上报同步事件、告知应用层本轮数据是否完整;当手指点击触摸屏或手指从触
摸屏离开时,此时就会上报按键类事件, 用于描述按下触摸屏和松开触摸屏; 具体的按键事件为
BTN_TOUCH(code=0x14a,也就是 330) ,手指在触摸屏上滑动不会上报 BTN_TOUCH 事件。 BTN_TOUCH 事件不支持长按状态,故其 value 不会等于 2。 对于多点触摸设备来说,只有第一个点按下时上报 BTN_TOUCH 事件 value=1、当最后一个点离开触摸屏时上报 BTN_TOUCH 事件 value=0。

单点触摸设备事件上报的流程大概如下:

 # 点击触摸屏时

BTN_TOUCH
ABS_X
ABS_Y
SYN_REPORT

        当手指点击触摸屏时,首先上报 BTN_TOUCH 事件,此时 value=1,表示按下;接着上报 ABS_X、 ABS_Y事件将 X、 Y 轴坐标数据发送给应用层;数据上报完成接着上报一个同步事件 SYN_REPORT,表示此次触摸点信息已经完整

# 滑动 

         此时状态手指已经在屏幕上,是接着上面已经完成点击屏幕但手指没松开的

ABS_X
ABS_Y
SYN_REPORT

        手指在触摸屏上滑动时,并不会上报 BTN_TOUCH 事件,因为滑动过程并未发生按下、松开这种动作。 

# 松开 

BTN_TOUCH
SYN_REPORT

         当松开时,首先上报了 BTN_TOUCH 事件,此时 value=0,表示手指已经松开了触摸屏,接着上报一个同步事件 SYN_REPORT。

        上面列举出只是一个大致流程,实际上对于不同的触摸屏设备,能够获取到的信息量大小是不相同的,譬如某设备只能读取到触摸点的 X 和 Y 坐标、而另一设备却能读取 X、 Y 坐标以及按压力大小、 触点面积等信息, 总之这些数据都会在 SYN_REPORT 同步事件之前上报给应用层

多点触摸设备--事件上报的顺序

        多点触摸设备上报的一轮完整数据中可能包含多个触摸点的信息,譬如 5 点触摸设备,如果 5 个手指同时在触摸屏上滑动,那么硬件就会更新 5 个触摸点的信息, 内核需要把这 5 个触摸点的信息上报给应用层。
        在 Linux 内核中, 多点触摸设备使用多点触摸(MT)协议上报各个触摸点的数据, MT 协议分为两种类型: Type A 和 Type B, Type A 协议几乎处于淘汰的边缘,重点来看看 Type B 协议

MT 协议之 Type B 协议

        Type B 协议适用于能够追踪并区分触摸点的设备。 Type B协议的重点是通过 ABS_MT_SLOT 事件上报各个触摸点信息的更新!能够追踪并区分触摸点的设备通常在硬件上能够区分不同的触摸点, 譬如对于一个 5 点触摸设备来说,硬件能够为每一个识别到的触摸点与一个 slot 进行关联,这个 slot 就是一个编号, 触摸点 0、触摸点 1、触摸点 2 等。底层驱动向应用层上报 ABS_MT_SLOT 事件, 此事件会告诉接收者当前正在更新的是哪个触摸点的数据, ABS_MT_SLOT 事件中对应的 value 数据存放的便是一个 slot、以告知应用层当前正在更新 slot
关联的触摸点对应的信息

        每个识别出来的触摸点分配一个 slot, 与该 slot 关联起来, 利用这个 slot 来传递对应触点的变化。 除了ABS_MT_SLOT 事 件 之 外 , Type B 协 议 还 会 使 用 到ABS_MT_TRACTKING_ID 事 件 ,ABS_MT_TRACTKING_ID 事件则用于触摸点的创建、替换和销毁工作,ABS_MT_TRACTKING_ID 事件携带的数据 value 表示一个 ID,一个非负数的 ID(ID>=0) 表示一个有效的触摸点,如果 ID 等于-1 表示该触摸点已经不存在、被移除了;一个以前不存在的 ID 表示这是一个新的触摸点

        Type B 协议可以减少发送到用户空间的数据,只有发生了变更的数据才会上报,譬如某个触摸点发生了移动,但仅仅只改变了 X 轴坐标、而未改变 Y 轴坐标,那么内核只会将改变后的 X 坐标值通过ABS_MT_POSITION_X 事件发送给应用层

测试

用上一章读取input_event结构体数据的代码测试一下触摸屏,首先在测试触摸屏之前,需要保证开发板上已经连接了 LCD 屏,使用命令"cat /proc/bus/input/devices",确定触摸屏对应的设备节点

 这里使用名字是goodix-ts,使用的是其它屏,那么查看到的名称可能不是"goodix-ts",设备文件是event1

加载APP测试一下

        APP是上章使用读取input_event结构体数据的,动作是先放第一个手指在触摸屏上不松开

        第一行上报了绝对位移事件 EV_ABS(type=3)中的 ABS_MT_TRACKING_ID(code=57)事件,并且 value 值等于 0,也就是 ID,这个 ID 是一个非负数,所以表示这是一个新的触摸点被创建,也就意味着触摸屏上产生了一个新的触摸点(手指按下)

        第二行上报了绝对位移事件 EV_ABS(type=3)中的 ABS_MT_POSITION_X(code=53)事件,其 value对应的便是触摸点的 X 坐标;
        第三行上报了 ABS_MT_POSITION_Y(code=54)事件,其 value 值对应的便是触摸点 Y 坐标,所以由此可知该触摸点的坐标为(314, 237)

        第四行上报了ABS_MT_TOUCH_MAJOR(code=48)事件
       第五行上报了 ABS_MT_WIDTH_MAJOR(code=50)事件
        最后一行上报了同步类事件 EV_SYN(type=0)中的 SYN_REPORT(code=0)事件,表示此次触摸点的信息全部上报完毕。

在第一个触摸点的基础上,增加第二个触摸点,打印信息如下所示

         1-6行与上同理,第7行上报了绝对位移事件 EV_ABS(type=3)中的 ABS_MT_SLOT 事件(code=47),表示目前要更新 slot=1 所关联的触摸点(也就是触摸点 1) 对应的信息

        第8行上报了绝对位移事件 EV_ABS(type=3)中的 ABS_MT_TRACKING_ID 事(code=57), ID=1,这是之前没有出现过的 ID,表示这是一个新的触摸点

        第九、十、十一十二行分别上报了 ABS_MT_POSITION_X事件 、 ABS_MT_POSITION_Y 事件、ABS_MT_TOUCH_MAJOR(code=48事件和 ABS_MT_WIDTH_MAJOR(code=50)事件

        最后一行上报同步事件(type=0、 code=0) ,告知应用层数据完整

当松开时,触摸点就会被销毁,上报 ABS_MT_TRACKING_ID 事件,并将 value 设置为-1(ID)

         不管是键盘也好、或者是鼠标、触摸屏,都可以像上面那样将输入设备的数据直接打印出来,然后自己再去分析,确定该输入设备上报事件的规则和流程,把这些弄懂之后再去编写程序验证结果

二、获取触摸屏的信息

        先了解一下ioctl()函数,原型如下

int ioctl(int fd, unsigned long request, ...);

        第一个参数 fd 对应文件描述符;第二个参数 request 与具体要操作的对象有关,没有统一值, 表示向文件描述符请求相应的操作,也就是请求指令;此函数是一个可变参函数, 第三个参数需要根据 request 参数来决定,配合 request 来使用

        ioctl()是一个文件 I/O 操作的杂物箱,可以处理的事情非常杂、不统一,一般用于操作特殊文件或设备文件
在 input.h 头文件有这样一些宏定义

      每一个宏定义后面都有注释,对于 input 输入设备,对其执行 ioctl()操作需要使用这些宏, 不同的宏表示不同请求指令,EVIOCG(get)开头的表示获取信息, EVIOCS(set)开头表示设置

      比如使用 EVIOCGNAME 宏获取设备名称,使用方式如下:

char name[100];
ioctl(fd, EVIOCGNAME(sizeof(name)), name);

        EVIOCGNAME(len)就表示用于接收字符串数据的缓冲区大小,而此时 ioctl()函数的第三个参数需要传入一个缓冲区的地址,该缓冲区用于存放设备名称对应的字符串数据

        想获取触摸屏支持的最大触摸点数,首先了解这个EVIOCGABS(abs)宏,原型如下

#define EVIOCGABS(abs) _IOR('E', 0x40 + (abs), struct input_absinfo)

         可以看到使用该宏需要传入一个 abs 参数,该参数表示为一个 ABS_XXX 绝对位移事件,譬如 EVIOCGABS(ABS_MT_SLOT)表示获取触摸屏的 slot 信息,此时 ioctl()函数的第三个参数是
一个 struct input_absinfo *的指针,指向一个 struct input_absinfo 对象,调用 ioctl()会将获取到的信息写入到struct input_absinfo 对象中

        struct input_absinfo 结构体如下所示:

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

 代码编写

 25行把ABS_MT_SLOT传给宏EVIOCGABS,表示获取触摸屏的 slot 信息,,后面为 struct input_absinfo 结构体,用于接收数据

31行,计算最大触摸点,因为 slot 数组下标从0开始,所以需要将 slot.maximum 加1

代码如下

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include int main(int argc, char *argv[])
{struct input_absinfo info;int fd = -1, max_slots;if (argc != 2){fprintf(stderr, "usage: %s \n", argv[0]);exit(EXIT_FAILURE);}if ((fd = open(argv[1], O_RDONLY)) < 0){perror("open error");exit(EXIT_FAILURE);}if (ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &info) < 0){perror("ioctl error");close(fd);exit(EXIT_FAILURE);}max_slots = info.maximum +1 - info.minimum;printf("max_slots: %d\n", max_slots);close(fd);exit(EXIT_SUCCESS);
}

验证

  所以这个屏是一个 5 点触摸屏

三、多点触摸APP

        编写代码获取触摸点的坐标信息,并将其打印出来,全部代码如下

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include struct ts_mt
{int x;int y;int id;int valid;
};struct tp_xy
{int x;int y;
};
static int ts_read(const int fd, const int max_slots,struct ts_mt *mt)
{int i;struct input_event in_ev;static int slot = 0;static struct tp_xy xy[12] = {0};memset(mt, 0x0, max_slots * sizeof(struct ts_mt));for (i = 0; i < max_slots; i++)mt[i].id = -2;for (;;){if (sizeof(struct input_event) !=read(fd, &in_ev, sizeof(struct input_event))){perror("read error");return -1;}switch (in_ev.type){case EV_ABS:switch (in_ev.code){case ABS_MT_SLOT:slot = in_ev.value;break;case ABS_MT_POSITION_X:xy[slot].x = in_ev.value;mt[slot].valid = 1;break;case ABS_MT_POSITION_Y:xy[slot].y = in_ev.value;mt[slot].valid = 1;break;case ABS_MT_TRACKING_ID:mt[slot].id = in_ev.value;mt[slot].valid = 1;break;}break;case EV_SYN:if (SYN_REPORT == in_ev.code){for (i = 0; i < max_slots; i++){mt[i].x = xy[i].x;mt[i].y = xy[i].y;}}return 0;}}
}
int main(int argc, char *argv[])
{struct input_absinfo slot;struct ts_mt *mt = NULL;int max_slots;int fd, i;if (2 != argc){fprintf(stderr, "usage: %s \n", argv[0]);exit(EXIT_FAILURE);}fd = open(argv[1], O_RDONLY);if (0 > fd){perror("open error");exit(EXIT_FAILURE);}if (ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot) < 0){perror("ioctl error");close(fd);exit(EXIT_FAILURE);}max_slots = slot.maximum + 1 - slot.minimum;printf("max_slots: %d\n", max_slots);mt = calloc(max_slots, sizeof(struct ts_mt));for (;;){if (ts_read(fd, max_slots, mt) < 0){break;}for (i = 0; i < max_slots; i++){if (mt[i].valid){if (mt[i].id >= 0)printf("slot<%d>, 按下(%d, %d)\n", i, mt[i].x, mt[i].y);else if (mt[i].id == -1)printf("slot<%d>, 松开\n", i);elseprintf("slot<%d>, 移动(%d, %d)\n", i, mt[i].x, mt[i].y);}}}close(fd);free(mt);exit(EXIT_SUCCESS);
}

        代码开头定义两个结构体,第一个结构体用于描述多点触摸中每一个触摸点的信息,第二个结构体就是一个触摸点中的x和y坐标

        在main函数中,实例一个input_absinfo 用来描述输入设备上的一个信息的结构体,接着定义mt结构体指针,下面用if判断传参是否等于2,接收打开文件,获取触摸屏支持的最大触摸点数max_slots,然后给mt指针申请空间,calloc()在动态分配完内存后,自动初始化该内存空间为零

        进入for死循环中,把文件描述符,最大触摸点传给ts_read函数。

        进入ts_read函数,这个函数用于读取输入设备文件中的事件数据。首先定义一个input_event 结构体用于描述输入设备事件,定义的结构体数组xy为12个元素,主要是用于保存上一次的x和y坐标值,这里假设触摸屏支持的最大触摸点数不会超过12,然后用memset函数初始化缓冲区,全部清零,然后把触摸点的idc全部初始化为-2(因为 id=-1表示触摸点删除, id>=0表示创建),进入ts_read函数的for死循环,先用if来判断读取量是否相等,不相等则意味着读取的数据大小不正确,可能会导致错误。开始判断type类型,如果事件类型是EV_ABS(表示绝对事件),则根据事件代码来处理。例如,如果事件代码是ABS_MT_SLOT(表示触摸点的槽位),则更新当前的槽位;如果事件代码是ABS_MT_POSITION_X或ABS_MT_POSITION_Y(表示触摸点的x坐标和y坐标),则更新当前槽位对应的xy坐标,并将该触摸点的有效标志位设为1;如果事件代码是ABS_MT_TRACKING_ID(表示跟踪ID),则将该跟踪ID保存到当前槽位对应的mt结构体中,并将该触摸点的有效标志位设为1。如果事件类型是EV_SYN(表示同步事件),则根据事件代码来处理。例如,如果事件代码是SYN_REPORT(表示一组事件已经结束),则将保存在xy数组中的坐标值赋值给mt结构体,并返回0。

        返回到main函数的for死循环中,下一步就是在读取事件数据成功后,程序遍历mt结构体数组,判断每一个触摸点信息是否发生更新(关注的信息发生更新),如果发生更新,则输出相应的信息,例如按下、松开或移动等。最后,程序关闭输入设备文件,释放内存,并退出。

验证

实现5点触摸的信息读取

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...