当前位置: 首页 > news >正文

Linux输入设备应用编程

本章学习输入设备的应用编程,首先要知道什么是输入设备?输入设备其实就是能够产生输入事件的设备就称为输入设备,常见的输入设备包括鼠标、键盘、触摸屏、按钮等等,它们都能够产生输入事件,产生输入数据给计算机系统。

对于输入设备的应用编程其主要是获取输入设备上报的数据、输入设备当前状态等,譬如获取触摸屏当前触摸点的 X、Y 轴位置信息以及触摸屏当前处于按下还是松开状态。

本章将会讨论如下主题内容。

⚫ 什么是输入设备;

⚫ 如何读取输入设备的数据;

⚫ 如何解析从输入设备中获取到的数据;

⚫ 按键、触摸屏设备如何解析数据、应用编程。

输入类设备编程介绍

什么是输入设备

先来了解什么是输入设备(也称为 input 设备),常见的输入设备有鼠标、键盘、触摸屏、遥控器、电脑画图板等,用户通过输入设备与系统进行交互。

input 子系统

由上面的介绍可知,输入设备种类非常多,每种设备上报的数据类型又不一样,那么 Linux 系统如何管理呢?Linux 系统为了统一管理这些输入设备,实现了一套能够兼容所有输入设备的框架,那么这个框架就是 input 子系统。驱动开发人员基于 input 子系统开发输入设备的驱动程序,input 子系统可以屏蔽硬件的差异,向应用层提供一套统一的接口。

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

读取数据的流程

如果我们要读取触摸屏的数据,假设触摸屏设备对应的设备节点为/dev/input/event0,那么数据读取流程如下:

①、应用程序打开/dev/input/event0 设备文件;

②、应用程序发起读操作(譬如调用 read),如果没有数据可读则会进入休眠(阻塞 I/O 情况下);

③、当有数据可读时,应用程序会被唤醒,读操作获取到数据返回;

④、应用程序对读取到的数据进行解析。

当无数据可读时,程序会进入休眠状态(也就是阻塞),譬如应用程序读触摸屏数据,如果当前并没有去触碰触摸屏,自然是无数据可读;当我们用手指触摸触摸屏或者在屏上滑动时,此时就会产生触摸数据、应用程序就有数据可读了,应用程序会被唤醒,成功读取到数据。那么对于其它输入设备亦是如此,无数据可读时应用程序会进入休眠状态(阻塞式 I/O 方式下),当有数据可读时才会被唤醒。

应用程序如何解析数据

首先我们要知道,应用程序打开输入设备对应的设备文件,向其发起读操作,那么这个读操作获取到的是什么样的数据呢?其实每一次 read 操作获取的都是一个 struct input_event 结构体类型数据,该结构体定义在<linux/input.h>头文件中,它的定义如下:

示例代码 17.1.1 struct input_event 结构体

结构体中的 time 成员变量是一个 struct timeval 类型的变量,该结构体在前面给大家介绍过,内核会记录每个上报的事件其发生的时间,并通过变量 time 返回给应用程序。时间参数通常不是那么重要,而其它3 个成员变量 type、code、value 更为重要。

type:type 用于描述发生了哪一种类型的事件(对事件的分类),Linux 系统所支持的输入事件类型如下所示:

以上这些宏定义也是在<linux/input.h>头文件中,所以在应用程序中需要包含该头文件;一种输入设备通常可以产生多种不同类型的事件,譬如点击鼠标按键(左键、右键,或鼠标上的其它按键)时会上报按键类事件,移动鼠标时则会上报相对位移类事件。

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

相对位移事件

绝对位移事件

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

除了以上列举出来的之外,还有很多,大家可以自己浏览<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 值而定!

数据同步

上面我们提到了同步事件类型 EV_SYN,同步事件用于实现同步操作、告知接收者本轮上报的数据已经完整。应用程序读取输入设备上报的数据时,一次 read 操作只能读取一个 struct input_event 类型数据,譬如对于触摸屏来说,一个触摸点的信息包含了 X 坐标、Y 坐标以及其它信息,对于这样情况,应用程序需要执行多次 read 操作才能把一个触摸点的信息全部读取出来,这样才能得到触摸点的完整信息。

那么应用程序如何得知本轮已经读取到完整的数据了呢?其实这就是通过同步事件来实现的,内核将本轮需要上报、发送给接收者的数据全部上报完毕后,接着会上报一个同步事件,以告知应用程序本轮数据已经完整、可以进行同步了。

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

所以的输入设备都需要上报同步事件,上报的同步事件通常是 SYN_REPORT,而 value 值通常为 0。

读取 struct input_event 数据

根据前面的介绍可知,对输入设备调用 read()会读取到一个 struct input_event 类型数据,本小节编写一个简单地应用程序,将读取到的 struct input_event 类型数据中的每一个元素打印出来、并对它们进行解析。

本例程源码对应的路径为:开发板光盘->11、Linux C 应用编程例程源码->17_input->read_input.c。

执行程序时需要传入参数,这个参数就是对应的输入设备的设备节点(设备文件),程序中会对传参进行校验。程序中首先调用 open()函数打开设备文件,之后在 for 循环中调用 read()函数读取文件,将读取到的数据存放在 struct input_event 结构体对象中,之后将结构体对象中的各个成员变量打印出来。注意,程序中使用了阻塞式 I/O 方式读取设备文件,所以当无数据可读时 read 调用会被阻塞,知道有数据可读时才会被唤醒!

Tips:设备文件不同于普通文件,读写设备文件之前无需设置读写位置偏移量。

使用交叉编译工具编译上述代码得到可执行文件 testApp:

在开发板上验证

ALPHA 和 Mini 开发板上都有一个用户按键 KEY0,它就是一个典型的输入设备,如下图所示:

该按键是提供给用户使用的一个 GPIO 按键,在出厂系统中,该按键驱动基于 input 子系统而实现,所以在/dev/input 目录下存在 KEY0 的设备节点,具体是哪个设备节点,可以使用 13.1.1 小节介绍的方法进行判断,这里不再重述!也可以通过查看/proc/bus/input/devices 文件得知,查看该文件可以获取到系统中注册的所有输入设备相关的信息,如下所示:

接下来我们使用这个按键进行测试,执行下面的命令:

程序运行后,执行按下 KEY0、松开 KEY0 等操作,终端将会打印出相应的信息,如上图所示。

第一行中 type 等于 1,表示上报的是按键事件 EV_KEY,code=114,打开 input-event-codes.h 头文件进行查找,可以发现 cpde=114 对应的是键盘上的 KEY_VOLUMEDOWN 按键,这个是 ALPHA/Mini 开发板出厂系统已经配置好的。而 value=1 表示按键按下,所以整个第一行的意思就是按键 KEY_VOLUMEDOWN被按下。

第二行,表示上报了 EV_SYN 同步类事件(type=0)中的 SYN_REPORT 事件(code=0),表示本轮数据已经完整、报告同步。

第三行,type 等于 1,表示按键类事件,code 等于 114、value 等于 0,所以表示按键 KEY_VOLUMEDOWN被松开。

第四行,又上报了同步事件。

所以整个上面 4 行的打印信息就是开发板上的 KEY0 按键被按下以及松开这个过程,内核所上报的事件以及发送给应用层的数据 value。

我们试试长按按键 KEY0,按住不放,如下所示:

可以看到上报按键事件时,对应的 value 等于 2,表示长按状态。

按键应用编程

本小节编写一个应用程序,获取按键状态,判断按键当前是按下、松开或长按状态。从上面打印的信息可知,对于按键来说,它的事件上报流程如下所示:

如果是按下,则上报 KEY_A 事件时,value=1;如果是松开,则 value=0;如果是长按,则 value=2。

接下来编写按键应用程序,读取按键状态并将结果打印出来,代码如下所示:

本例程源码对应的路径为:开发板光盘->11、Linux C 应用编程例程源码->17_input->read_key.c。

在 for 循环中,调用 read()读取输入设备上报的数据,当按键按下或松开(以及长按)动作发生时,read()会读取到输入设备上报的数据,首先判断此次上报的事件是否是按键类事件(EV_KEY),如果是按键类事件、接着根据 value 值来判断按键当前的状态是松开、按下还是长按。

将上述代码进行编译:

将编译得到的可执行文件拷贝开发板 Linux 系统的/home/root 目录下。

首先我们来测试开发板的 KEY0 按键,执行应用程序:

./testApp /dev/input/event2 //测试开发板上的 KEY0

运行程序之后,按下 KEY0 或松开 KEY0 以及长按情况下,终端会打印出相应的信息,如上图所示。

code=114(KEY_VOLUMEDOWN 按键)。

除了测试开发板上的 KEY0 按键之外,我们还可以测试键盘上的按键,首先找到一个 USB 键盘连接到开发板的 USB HOST 接口上,当键盘插入之后,终端将会打印出相应的驱动加载信息:

驱动加载成功之后,可以查看下该键盘设备对应的设备节点,使用命令"cat/proc/bus/input/devices",在打印信息中找到键盘设备的信息:

譬如笔者使用的是一个罗技的USB 键盘"Logitech USB Keyboard",对应的设备节点为/dev/input/event3,

运行测试程序并按下、松开键盘上的按键:

大家可以根据 code 值查询到对应的按键(通过 input-event-codes.h 头文件),譬如 code=30 对应的是键盘上的字母 A 键,code=48 对应的是字母 B 键。

触摸屏应用编程

本小节编写触摸屏应用程序,获取触摸屏的坐标信息并将其打印出来。

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

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

单点触摸和多点触摸

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

多点触摸设备可支持多点触摸,譬如 ALPHA/Mini 开发板配套使用的 4.3 寸、7 寸等触摸屏均支持多点触摸,对于多点触摸设备,一轮完整的数据可能包含多个触摸点信息。多点触摸设备则是以 ABS_MT_XXX(MT Multi-touch,意思为:多点触摸)事件承载、上报触摸点的信息,如 ABS_MT_POSITION_XX 轴坐标)、ABS_MT_POSITION_YY 轴坐标)等绝对位移事件。

触摸屏设备除了上报绝对位移事件之外,还可以上报按键类事件和同步类事件。同步事件很好理解,因为几乎每一个输入设备都会上报同步事件、告知应用层本轮数据是否完整;当手指点击触摸屏或手指从触摸屏离开时,此时就会上报按键类事件,用于描述按下触摸屏和松开触摸屏;具体的按键事件为BTN_TOUCH(code=0x14a,也就是 330),当然,手指在触摸屏上滑动不会上报 BTN_TOUCH 事件。

Tips:BTN_TOUCH 事件不支持长按状态,故其 value 不会等于 2。对于多点触摸设备来说,只有第一个点按下时上报 BTN_TOUCH 事件 value=1、当最后一个点离开触摸屏时上报 BTN_TOUCH 事件 value=0。

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

通过上面的介绍可知,单点触摸设备事件上报的流程大概如下所示:

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

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

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

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

以上就是单点触摸设备事件上报的一个大致流程,接下来看看多点触摸设备。

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

多点触摸设备上报的一轮完整数据中可能包含多个触摸点的信息,譬如 5 点触摸设备,如果 5 个手指同时在触摸屏上滑动,那么硬件就会更新 5 个触摸点的信息,内核需要把这 5 个触摸点的信息上报给应用层。

Linux 内核中,多点触摸设备使用多点触摸(MT)协议上报各个触摸点的数据,MT 协议分为两种类型:Type A Type BType 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,一个非负数的 IDID>=0)表示一个有效的触摸点,如果 ID 等于-1 表示该触摸点已经不存在、被移除了;一个以前不存在的 ID 表示这是一个新的触摸点。

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

以上关于 Type B 协议就给大家介绍这么多,为了帮助大家理解,笔者把 Type B 协议下多点触摸设备上报数据的流程列举如下:

单看这个可能大家看不懂,接下来我们打印触摸屏的数据一个一个进行分析。

Tips:大家可能会对 slot 和 ID 这两个概念有点混乱,这里笔者将自己的理解告知大家;slot 是硬件上的一个概念、而 ID 则可认为是软件上的一个概念;对于一个多点触摸设备来说,它最大支持的触摸点数是确定的,譬如 5 个触摸设备,最多支持 5 个触摸点;每一个触摸点在硬件上它有一个区分的编号,譬如触摸点0、触摸点 1、触摸点 2 等,这个编号就是一个 slot(通常从 0 开始);如何给识别到的触点分配一个 slot 呢(触点与 slot 关联)?通常是按照时间先后顺序来的,譬如第一根手指先触碰到触摸屏,那第一根手指就对应触摸点 0(slot=0),接着第二根手指触碰到触摸屏则对应触摸点 1(slot=1)以此类推!这个通常是硬件所支持的。而 ID 可认为是软件上的一个概念,它也用于区分不同的触摸点,但是它跟 slot 不同,不是同一层级的概念;举个例子,譬如一根手指触碰到触摸屏之后拿开,然后再次触碰触摸屏,这个过程中,假设只有这一根手指进行触碰操作,那么两次触碰对应都是触摸点 0(slot=0),这个无疑义!但从触摸点的生命周期来看,它们是同一个触摸点吗?答案肯定不是,为啥呢?手指从触摸屏上离开后,该触摸点就消失了、被删除了,该触摸点的生命周期也就到此结束了,所以它们自然是不同的触摸点,所以它们的 ID 是不同的。

触摸屏上报数据分析

首先在测试触摸屏之前,需要保证开发板上已经连接了 LCD 屏,ALPHA/Mini I.MX6U 开发板出厂系统配套支持多种不同分辨率的 LCD 屏,包括 4.3 480*2724.3 800*4807 800*4807 1024*600以及 10.1 1280*800,在启动开发板之前需要将 LCD 屏通过软排线连接到开发板的 LCD 接口,开发板连接好 LCD 屏之后上电启动开发板、运行出厂系统。

触摸屏与 LCD 液晶屏面板是粘合在一起的,也就是说触摸屏是直接贴在了 LCD 液晶屏上面,直接在LCD 屏上触摸、滑动操作即可。为了测试方便,可以将出厂系统的 GUI 应用程序退出,如何退出呢?点击屏幕进入设置页面,可以看到在该页面下有一个退出按钮选项,直接点击即可!

使用命令"cat /proc/bus/input/devices",确定触摸屏对应的设备节点,如下所示:

笔者使用的是开发板配套的 4.3 800*480 LCD 屏,如果各位读者使用的是其它屏,那么查看到的名称可能不是"goodix-ts"。执行示例代码 17.2.1 对应的可执行文件,一个手指点击触摸屏先不松开,终端将会打印如下信息:

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

第二行上报了绝对位移事件 EV_ABStype=3)中的 ABS_MT_POSITION_Xcode=53)事件,其 value对应的便是触摸点的 X 坐标;第三行上报了 ABS_MT_POSITION_Ycode=54)事件,其 value 值对应的便是触摸点 Y 坐标,所以由此可知该触摸点的坐标为(372, 381)

第四行上报了按键类事件 EV_KEYtype=1)中的 BTN_TOUCHcode=330),value 值等于 1,表示这是触摸屏上最先产生的触摸点(slot=0、也就是触摸点 0)。

第五行和第六行分别上报了绝对位移事件 EV_ABStype=3)中的 ABS_Xcode=0)和 ABS_Ycode=1),其 value 分别对应的是触摸点的 X 坐标和 Y 坐标。多点触摸设备也会通过 ABS_XABS_Y 事件上报触摸点的 XY 坐标,但通常只有触摸点 0 支持,所以可以把多点触摸设备当成单点触摸设备来使用。

最后一行上报了同步类事件 EV_SYNtype=0)中的 SYN_REPORTcode=0)事件,表示此次触摸点的信息全部上报完毕。

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

1~7 行不再解释,第八行上报了绝对位移事件 EV_ABStype=3)中的 ABS_MT_SLOT 事件(code=47),表示目前要更新 slot=1 所关联的触摸点(也就是触摸点 1)对应的信息。

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

第十、十一行分别上报了 ABS_MT_POSITION_X ABS_MT_POSITION_Y 事件。

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

当手指松开时,触摸点就会被销毁,上报 ABS_MT_TRACKING_ID 事件,并将 value 设置为-1ID),如下所示:

关于触摸屏数据的分析就给大家介绍这么多,不管是键盘也好、或者是鼠标、触摸屏,都可以像上面那样将输入设备的数据直接打印出来,然后自己再去分析,确定该输入设备上报事件的规则和流程,把这些弄懂之后再去编写程序验证结果。下面我们将自己动手编写单点触摸以及多点触摸应用程序,读取触摸点的坐标信息。

获取触摸屏的信息

本小节介绍如何获取触摸屏设备的信息,譬如触摸屏支持的最大触摸点数、触摸屏 XY 坐标的范围等。通过 ioctl()函数可以获取到这些信息,3.10.2 小节给大家介绍过该函数,ioctl()是一个文件 I/O 操作的杂物箱,可以处理的事情非常杂、不统一,一般用于操作特殊文件或设备文件,为了方便讲解,再次把 ioctl()函数的原型列出:

#include <sys/ioctl.h>

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

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

先来看下输入设备的 ioctl()该怎么用,在 input.h 头文件有这样一些宏定义,如下所示:

每一个宏定义后面都有相应的注释,对于 input 输入设备,对其执行 ioctl()操作需要使用这些宏,不同的宏表示不同请求指令;譬如使用 EVIOCGNAME 宏获取设备名称,使用方式如下:

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

EVIOCGget)开头的表示获取信息,EVIOCSset)开头表示设置;这里暂且不管其它宏,重点来看看 EVIOCGABS(abs)宏,这个宏也是通常使用最多的,如下所示:

#define EVIOCGABS(abs)

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

通过这个宏可以获取到触摸屏 slotslot<0>表示触摸点 0slot<1>表示触摸点 1slot<2>表示触摸点 2,以此类推!)的取值范围,可以看到使用该宏需要传入一个 abs 参数,该参数表示为一个 ABS_XXX 绝对位移事件,譬如 EVIOCGABS(ABS_MT_SLOT)表示获取触摸屏的 slot 信息,此时 ioctl()函数的第三个参数是一个 struct input_absinfo *的指针,指向一个 struct input_absinfo 对象,调用 ioctl()会将获取到的信息写入到struct input_absinfo 对象中。struct input_absinfo 结构体如下所示:

获取触摸屏支持的最大触摸点数:

其它宏定义不再介绍,读者可以自行测试。

获取触摸屏支持的最大触摸点数

本例程源码对应的路径为:开发板光盘->11Linux C 应用编程例程源码->17_input->read_slot.c

编译示例代码,将其拷贝到开发板 Linux 系统的用户家目录下,执行该应用程序:

所以从打印结果可知,我们这个屏是一个 5 点触摸屏。

单点触摸应用程序

通过上面的详细介绍,详细大家应该知道如何去编写一个触摸屏的应用程序了,本小节我们编写一个单点触摸应用程序,获取一个触摸点的坐标信息,并将其打印出来。

ALPHA/Mini 开发板配套使用的触摸屏均支持多点触摸,这里我们把它当成单点触摸设备来使用,编写一个程序读取一个触摸点,示例代码如下所示:

本例程源码对应的路径为:开发板光盘->11Linux C 应用编程例程源码->17_input->read_ts.c

程序中首先校验传参,通过传参的方式将触摸屏设备文件路径传入到程序中,main()函数中定义了 4 个变量:

⑴、变量 x 表示触摸点的 X 坐标;

⑵、变量 y 表示触摸点的 Y 坐标;

⑶、变量 down 表示手指状态时候按下、松开还是滑动,down=1 表示手指按下、down=0 表示手指松开、down=-1 表示手指滑动;

⑷、变量 valid 表示数据是否有效,valid=1 表示有效、valid=0 表示无效;有效指的是我们检测的信息发生了更改,譬如程序中只检测了手指的按下、松开动作以及坐标值的变化。

接着调用 open()打开触摸屏设备文件得到文件描述符 fd;在 for 循环之前,首先对 xydownvalid这 4 个变量进行初始化操作。在 for 循环读取触摸屏上报的数据,将读取到的数据存放在 struct input_event数据结构中。在 switch…case 语句中对读取到的数据进行解析,获取 BTN_TOUCH 事件的 value 数据,判断触摸屏是按下还是松开状态,获取 ABS_X ABS_Y 事件的 value 变量,得到触摸点的 X 轴坐标和 Y 轴坐标。

当上报同步事件时,表示数据已经完整,接着对我们得到的数据进行分析、打印坐标信息。

编译应用程序:

将编译得到的可执行文件拷贝到开发板 Linux 系统的用户家目录下,准备进行测试。

笔者使用的是 4.3 800*480 LCD 屏,执行单点触摸应用程序,程序执行之后,接着用一个手指按下触摸屏、松开以及滑动操作,串口终端将会打印出相应的信息:

当手指点击触摸屏时会打印"按下(X, Y)",松开时会打印"松开",手指在触摸屏上滑动时会打印"移动(X, Y)"等信息。大家可以自己动手测试,对代码不理解的,可以对照测试结果进行对比。

多点触摸应用程序

介绍完单点触摸应用程序之后,再来看看多点触摸应用程序该如何编写,前面已经详细给大家介绍了多点触摸设备的事件上报流程。

本例程源码对应的路径为:开发板光盘->11Linux C 应用编程例程源码->17_input->read_mt.c

示例代码中申明了 struct ts_mt 数据结构,用于描述多点触摸情况下每一个触摸点的信息。

首先来看下 main()函数,定义了 max_slots 变量,用于指定触摸屏设备的支持的最大触摸点数,通过:

ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot)

获取到触摸屏该信息。

接着根据 max_slots 变量的值,为 mt 指针申请内存:

mt = calloc(max_slots, sizeof(struct ts_mt));

for( ; ; )循环中调用 ts_read()函数,该函数是自定义函数,用于获取触摸屏上报的数据,第一个参数表示文件描述符 fd、第二个参数表示触摸屏支持的最大触摸点数、第三个参数则是 struct ts_mt 数组,ts_read()函数会将获取到的数据存放在数组中,mt[0]表示 slot<0>数据、mt[1]表示 slot<1>的数据依次类推!

在内部的 for 循环中,则对获取到的数据进行分析,判断数据是否有效,并根据 id 判断手指的动作,在单点触摸应用程序中,我们是通过 BTN_TOUCH 事件来判断手指的动作;而在多点触摸应用中,我们需要通过 id 来判断多个手指的动作。

关于自定义函数 ts_read()就不再介绍了,代码的注释已经描述很清楚了!

接着编译应用程序,将编译得到的可执行文件拷贝到开发板 Linux 系统的用户家目录下,执行应用程序,接着可以用多个手指触摸触摸屏、松开、滑动等操作:

每一个不同的 slot 表示不同的触摸点,譬如 slot<0>表示触摸点 0slot<1>表示触摸点 1 以此类推!

鼠标应用编程

本小节是笔者留给各位读者的一个作业,交给大家去完成,通过本章内容的介绍相信大家可以独立完成,ALPHA/Mini 开发板出厂系统支持 USB 鼠标,直接将一个 USB 鼠标插入到开发板的 USB HOST 接口即可,在终端会打印驱动加载信息。

相关文章:

Linux输入设备应用编程

本章学习输入设备的应用编程&#xff0c;首先要知道什么是输入设备&#xff1f;输入设备其实就是能够产生输入事件的设备就称为输入设备&#xff0c;常见的输入设备包括鼠标、键盘、触摸屏、按钮等等&#xff0c;它们都能够产生输入事件&#xff0c;产生输入数据给计算机系统。…...

【Vulkan入门】03-创建Device

目录 先叨叨git信息关键代码VulkanEnv::CreateDevice() 编译并运行程序题外话 先叨叨 在上篇已经选择了一个合适的PhysicalDevice。 本篇要为这个PhysicalDevice创将一个Device。Device可以理解为APP与PhysicalDevice之间的代理。 所有APP与PhysicalDevice之间交互的资源都通过…...

【jvm】C2编译器

目录 1. 说明2. 编译流程3. 使用与配置4. 性能优化与监控5. 局限性 1. 说明 1.JVM&#xff08;Java虚拟机&#xff09;C2编译器是Java编译过程中的重要环节&#xff0c;专门用于将Java字节码编译成高效的本地机器代码&#xff0c;以提升Java程序的执行效率。2.特点&#xff1a…...

使用 Acme.sh 自动生成和续签免费 SSL 证书(含通配符支持)

Acme.sh 是一个开源的脚本&#xff0c;能够从 ZeroSSL、Let’s Encrypt 等证书颁发机构&#xff08;CA&#xff09;获取免费的 HTTPS 证书。该脚本特别简单易用&#xff0c;并且支持多种验证方式。下面将详细介绍使用 Acme.sh 生成、安装和更新证书的各个步骤。 Github地址 使用…...

Android 图形系统之四:Choreographer

Choreographer 是 Android 系统中负责帧同步的核心组件&#xff0c;它协调输入事件、动画和绘制任务&#xff0c;以确保界面以固定频率&#xff08;通常是每 16ms&#xff0c;一帧&#xff09;流畅渲染。通过管理 VSYNC 信号和调度任务&#xff0c;Choreographer 是实现流畅 UI…...

CAP定理和BASE理论

CAP定理 CAP定理&#xff0c;也称为布鲁尔定理&#xff08;Brewer’s Theorem&#xff09;&#xff0c;是分布式系统设计中的一个基本原理。它指出在分布式系统中&#xff0c;一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;和分区容…...

笔记软件:我来、思源笔记、Obsidian、OneNote

最近wolai的会员到期了&#xff0c;促使我更新了一下笔记软件。 首先&#xff0c;wolai作为一个笔记软件&#xff0c;我觉得有很多做得不错的方面&#xff08;否则我也不会为它付费2年了&#xff09;&#xff0c;各种功能集成得很全&#xff08;公式识别这个功能我写论文的时候…...

试探互联网如何工作?

Reading&#xff1a; How_does_the_Internet_workhow-does-internet-work Watching&#xff1a;How the Internet Works in 5 Minutes Outline: 互联网通过全球互联的计算机和服务器网络工作&#xff0c;通过标准化协议进行通信。数据被分解成数据包&#xff0c;并使用互联…...

【c++笔试强训】(第三十篇)

目录 爱丽丝的⼈偶&#xff08;贪⼼构造&#xff09; 题目解析 讲解算法原理 编写代码 集合&#xff08;排序&#xff09; 题目解析 讲解算法原理 编写代码 爱丽丝的⼈偶&#xff08;贪⼼构造&#xff09; 题目解析 1.题目链接&#xff1a;登录—专业IT笔试面试备考平…...

微信小程序购物车全选反选功能以及合计

微信小程序基于Vant Weapp的购物车功能实现 1、单选 使用微信小程序原生表单组件checkbox和checkbox-group 注意&#xff1a;checkbox原生不支持bind:change事件&#xff0c;checkbox-group支持 <checkbox-group bindchange"handleCheck"><checkbox val…...

vue-qr在线生成二维码组件(vue2版本)

在对于二维码生成中有许多组件&#xff0c;下面介绍关于自定义比较高的vue-qr组件&#xff0c;能自定义设置背景颜色、背景图片、背景Gif图、实点和空白区的颜色、中心Logo的图片和边距。 依赖下载 注意&#xff1a; 直接npm下载最新版 有些项目可能运行会抱错 这时候你可以降…...

大语言模型技术相关知识-笔记整理

系列文章目录 这个系列攒了很久。主要是前段之间面试大语言模型方面的实习&#xff08;被拷打太多次了&#xff09;&#xff0c;然后每天根据面试官的问题进行扩展和补充的这个笔记。内容来源主要来自视频、个人理解以及官方文档中的记录。方便后面的回顾。 文章目录 系列文章…...

SCP命令实现Linux中的文件传输

CP命令的主要作用是实现Linux与Linux系统之间的文件传输。 SCP命令时基于SSH协议,所以两台服务器的sshd服务必须处于开启状态,否则无法完成上传与下载操作。 #1.上传文件 scp linux本地文件路径 远程用户名@linux主机地址:远程路径 #2.下载文件 scp 远程用户名@linux主机地址…...

linux环境中后台运行java程序

在生产环境&#xff0c;我们通常需要让java进程后台运行&#xff0c;并且即使会话关闭&#xff0c;进程也依然存在。 使用的命令&#xff1a; nohup java -jar xxx.jar -> aaa.log 2>&1 & 详细介绍下上面这条命令 &#xff08;1&#xff09;nohup&#xff1a;…...

Go学习:变量

目录 1. 变量的命名 2. 变量的声明 3. 变量声明时注意事项 4. 变量的初始化 5. 简单例子 变量主要用来存储数据信息&#xff0c;变量的值可以通过变量名进行访问。 1. 变量的命名 在Go语言中&#xff0c;变量名的命名规则 与其他编程语言一样&#xff0c;都是由字母、数…...

在Unity编辑模式下运行Mono中的方法

[ExecuteAlways] 最简单的方法当然是直接给Mono加上[ExecuteAlways]修饰&#xff0c;这样Mono中的Awake&#xff0c;Update等等都可以在编辑模式下按照原本的时机运行。 [ExecuteAlways] public class TestScript : MonoBehaviour {void TestMethod(){Debug.Log("TestMe…...

Y20030028 JAVA+SSM+MYSQL+LW+基于JAVA的考研监督互助系统的设计与实现 源代码 配置 文档

基于JAVA的考研监督互助系统 1.项目描述2. 课题开发背景及意义3.项目功能4.界面展示5.源码获取 1.项目描述 随着高等教育的普及和就业竞争的加剧&#xff0c;越来越多的学生选择继续深造&#xff0c;参加研究生入学考试。考研人数的不断增加&#xff0c;使得考研过程中的学习监…...

MATLAB期末复习笔记(下)

目录 五、数据和函数的可视化 1.MATLAB的可视化对象 2.二维图形的绘制 3.图形标识 4.多子图绘图 5.直方图的绘制 &#xff08;1&#xff09;分类 &#xff08;2&#xff09;垂直累计式 &#xff08;3&#xff09;垂直分组式 &#xff08;4&#xff09;水平分组式 &…...

「Mac畅玩鸿蒙与硬件37」UI互动应用篇14 - 随机颜色变化器

本篇将带你实现一个随机颜色变化器应用。用户点击“随机颜色”按钮后&#xff0c;界面背景会随机变化为淡色系颜色&#xff0c;同时显示当前的颜色代码&#xff0c;页面还会展示一只猫咪图片作为装饰&#xff0c;提升趣味性。 关键词 UI互动应用随机颜色生成状态管理用户交互…...

MySql:理解数据库

目录 一、什么是数据库 第一层理解 第二层理解 第三层理解 二、Linux下的数据库 三、基本认识 登录数据库时&#xff0c; mysql -u root -h 127.0.0.1 -P 3306 -p -h指定MySql服务器所在主机&#xff0c;若在本地则为回环地址。-P表示目标主机上MySql服务端口号 一般简单…...

学习笔记051——SpringBoot学习2

文章目录 Spring Boot 原理1、SpringBootConfiguration2、ConfigurationProperties3、ComponentScan4、EnableAutoConfiguration Spring Boot 原理 Spring Boot 可以自动读取配置文件&#xff0c;将项目所需要的组件全部自动加载到 IoC 容器中&#xff0c;包括两部分 开发者自…...

crush rule 20 type does not match pool

问题 kubectl describe CephObjectStore ceph-objectstoreEvents:Type Reason Age From Message---- ------ ---- ---- -------Warning ReconcileFailed 14m …...

BA是什么?

目录 1.EKF的步骤 一、问题定义与模型建立 二、线性化处理 三、应用卡尔曼滤波 四、迭代与收敛 五、结果评估与优化 注意事项 2.BA问题的步骤 一、问题定义与数据准备 二、构建优化模型 三、选择优化算法 四、执行优化过程 五、结果评估与优化 六、应用与验证 1.…...

通过docker 搭建jenkins环境;

一、官网简介使用安装说明: How to use this image docker run -p 8080:8080 -p 50000:50000 jenkins This will store the workspace in /var/jenkins_home. All Jenkins data lives in there - including plugins and configuration. You will probably want to make that …...

10-高级主题

第10节 高级主题 在这一节中&#xff0c;我们将探讨一些 Python 中的高级主题&#xff0c;包括装饰器、生成器、上下文管理器、元类以及常用的设计模式。这些高级特性能够帮助你编写更强大、更灵活的代码。 10.1 装饰器 装饰器是一种特殊类型的函数&#xff0c;可以修改其他函数…...

harbor常见问题及解决方法分享

harbor常见问题及解决方法分享 参考自《harbor权威指南》。 1. harbor配置文件不生效 问题现象 无论是在生产环境下还是在测试环境下&#xff0c;都会有对配置文件进行修改的场景。很多用户在停掉Harbor容器后&#xff0c;都会修改配置文件然后启动Harbor&#xff0c;发现配…...

行列式与线性方程组解的关系

线性方程组是数学中一个重要的概念&#xff0c;它描述了多个变量之间的线性关系。行列式作为方阵的一个特殊值&#xff0c;对于判断线性方程组解的存在性和唯一性有着重要的作用。本文将探讨行列式与线性方程组解之间的关系&#xff0c;并区分齐次和非齐次方程组的情况。 齐次…...

四、自然语言处理_02RNN基础知识笔记

1、RNN的定义 RNN&#xff08;Recurrent Neural Network&#xff0c;循环神经网络&#xff09;是一种专门用于处理序列数据的神经网络架构&#xff0c;它与传统的前馈神经网络&#xff08;Feedforward Neural Network&#xff09;不同&#xff0c;主要区别在于它能够处理输入数…...

Spring 容器管理 Bean

Bean是什么 Bean 是指 Java 中的可重用软件组件,容器会根据提供的元数据来创建和管理这些 Bean,也包括它们之间的依赖关系。Spring 容器对 Bean 并没有太多的要求,无须实现特定接口或依赖特定库,只要是最普通的 Java 对象即可,这类对象也被称为 POJO(Plain Old Java Obj…...

SpringBoot开发——Spring Boot3.4 强大的结构化日志记录

文章目录 1. 简介2. 实战案例2.1 环境依赖2.2 快速入门2.3 输出到文件2.4 添加附加字段2.5 自定义日志格式总结1. 简介 日志记录是应用故障排除中早已确立的部分,也是可观测性的三大支柱之一,另外两个是指标和追踪。在生产环境中,没有人喜欢盲目行事,而当故障发生时,开发…...