RK3588是一款低功耗、高性能的处理器,适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用,RK3588支持8K视频编解码,内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP,内置NPU,支持INT4/INT8/INT16/FP16混合运算能力,支持安卓12和、Debian11、Build root、Ubuntu20和22版本登系统。了解更多信息可点击迅为官网
【粉丝群】824412014
【实验平台】:迅为RK3588开发板
【内容来源】《iTOP-3588开发板系统编程手册》
【全套资料及网盘获取方式】联系淘宝客服加入售后技术支持群内下载
【视频介绍】:【强者之芯】 新一代AIOT高端应用芯片 iTOP -3588人工智能工业AI主板
第18章 输入设备应用编程
18.1输入设备介绍
输入设备是指可以接收用户输入的设备,例如键盘、鼠标、触摸屏等。这些设备可以通过Linux内核提供的输入子系统与应用程序进行交互。输入子系统是一个内核模块,它负责管理输入设备和应用程序之间的通信。
在Linux中,每个输入设备都被表示为一个字符设备文件。这些文件通常位于/dev/input目录下,如下图所示:
当输入设备产生一个事件时,它会将事件数据写入该设备的输入队列中。应用程序可以通过读取该设备的输入队列来获得有关事件的信息。事件包括按键按下、松开、鼠标移动、触摸屏触摸等。
18.2 input子系统
input子系统是一种负责管理输入设备的子系统,它提供了一组用于读取和控制输入设备的API和机制,包括设备注册、事件读取和处理等。在Linux中,所有的输入设备都被视为一个事件源,例如键盘、鼠标、触摸屏、游戏手柄等。这些输入设备会产生各种各样的事件,例如按键按下、松开、鼠标移动、滚轮滚动等。输入子系统可以将这些事件收集起来,交给用户空间的应用程序处理,以实现各种不同的功能。
input子系统的核心是input驱动程序,它是Linux内核中的一个模块,主要负责与硬件进行通信,从硬件读取输入事件,并将其转换为标准的输入事件格式,并向内核的输入子系统发送事件。每个输入设备都需要有一个相应的input驱动程序进行管理和处理。例如,对于USB鼠标,需要加载相应的hid驱动程序,对于触摸屏,需要加载相应的触摸屏驱动程序。
除了驱动程序,input子系统还包括了一个输入事件处理程序,负责将输入事件传递给应用程序进行处理。输入事件处理程序会将所有的输入事件存储在一个FIFO队列中,并向用户空间的应用程序发送相应的信号。在用户空间,可以通过读取/dev/input/eventX文件,其中X是输入设备的编号,来获取输入事件。如下图所示:
18.3读取输入设备
如果要读取某个输入设备的数据对应的流程如下所示:
步骤 | 描述 |
1 | 应用程序打开/dev/input/eventX设备文件,X为对应的输入设备编号。 |
2 | 应用程序使用read函数从设备文件中读取数据。如果设备上没有数据可读,则read函数会一直阻塞等待,直到数据可用。 |
3 | 读取的数据为一个input_event结构体,其包含事件类型、事件码和事件值三个字段,用于描述输入设备发生的事件信息。事件类型指示事件的分类,例如按键事件、鼠标事件、触摸屏事件等;事件码指示事件的具体类型,例如按下键盘上的某个键、移动鼠标等;事件值表示事件的取值,例如按键按下/释放、鼠标移动的距离、触摸屏触点的坐标等。 |
4 | 应用程序根据读取到的事件数据进行处理,例如根据事件类型和事件码判断是哪个键被按下、鼠标移动的距离等,并进行相应的操作。 |
5 | 在使用完输入设备后,使用close系统调用关闭输入设备文件。 |
每一步骤实现的具体代码,在之后的实验小节中会进行演示。
在第二步中,通过read函数进行读取输入设备数据的读取,每一次 read 操作获取的都是一个 struct input_event 结构体类型数据,struct input_event 是 Linux 内核中用于描述输入事件的结构体类型。它定义在 <linux/input.h> 头文件中,用于在内核和用户空间之间传递输入事件的信息。在用户空间中,应用程序通过读取输入设备文件获得 struct input_event 数据,并解析其中的事件类型、事件码和事件值等信息,以进行相应的处理。结构体struct input_event 定义如下:
struct input_event {
struct timeval time;
__u16 type;// 类型
__u16 code;// 具体事件
__s32 value;// 对应的取值
};
下面对struct input_event结构体的三个字段及其相应的宏进行详细的讲解:
type:type 用于描述发生了哪一种类型的事件(对事件的分类),Linux 系统所支持的输入事件类型如下所示:
#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
#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)
以上这些宏定义同样在<linux/input.h>头文件中,所以在应用程序中需要包含该头文件;一种输入设备 通常可以产生多种不同类型的事件,譬如点击鼠标按键(左键、右键,或鼠标上的其它按键)时会上报按键类事件,移动鼠标时则会上报相对位移类事件。
code:code 表示该类事件中的哪一个具体事件,以上列举的每一种事件类型中都包含了一系列具体事件,譬如一个键盘上通常有很多按键,而 code变量则告知应用程序是哪一个按键发生了输入事件。每一种事件类型都包含多种不同的事件,以按键类事件为例,对应的具体事件如下所示:
#define KEY_RESERVED 0
#define KEY_ESC 1 //ESC 键
#define KEY_1 2 //数字 1 键
#define KEY_2 3 //数字 2 键
#define KEY_3 4 //数字 3 键
#define KEY_4 5 //数字 4 键
#define KEY_5 6 //数字 5 键
#define KEY_6 7 //数字 6 键
#define KEY_7 8 //数字 7 键
#define KEY_8 9 //数字 8 键
#define KEY_9 10 //数字 9 键
#define KEY_0 11 //数字 0 键
#define KEY_MINUS 12 //减号键
#define KEY_EQUAL 13 //加号键
#define KEY_BACKSPACE 14 //回退键
................................
对于其他输入事件的code值,可以查看input-event-codes.h 头文件(该头文件被<linux/input.h>所包含)。
value:内核每次上报事件都会向应用层发送一个数据 value,对 value 值的解释随着 code 的变化而变化。譬如对于按键事件来说,如果 value 等于 1,则表示按键按下;value 等于 0 表示按键松开,如果 value 等于 2则表示按键长按。而在绝对位移事件中(type=3),如果 code=0(触摸点 X 坐标 ABS_X),那么 value 值就等于触摸点的 X 轴坐标值;如果 code=1(触摸点 Y 坐标 ABS_Y),此时value 值便等于触摸点的 Y 轴坐标值。
18.4按键应用编程
18.4.1编写应用程序
本小节代码在配套资料“iTOP-3588开发板\03_【iTOP-RK3588开发板】指南教程\03_系统编程配套程序\68”目录下,如下图所示:
实验要求:
打印开发板按键按下后的type、code、value值,并打印按键的状态(按下、弹起和长按)及对应的功能。
实验步骤:
首先进入到ubuntu的终端界面输入以下命令来创建 demo68_input.c文件,如下图所示:
vim demo68_input.c
然后向该文件中添加以下内容:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <linux/input.h>
int main(int argc, char *argv[])
{
int fd, ret;
struct input_event in_ev = {0}; //初始化 input_event 结构体
fd = open(argv[1], O_RDONLY); //打开输入设备文件
if (fd < 0)
{ //文件打开失败
printf("文件打开失败\n");
return -1;
}
while (1)
{
ret = read(fd, &in_ev, sizeof(struct input_event)); //读取数据
if (ret < 0)
{ //读取失败
printf("读取数据失败\n");
}
// 打印读取到的事件信息
printf("type:%d code:%d value:%d\n", in_ev.type, in_ev.code, in_ev.value);
if (EV_KEY == in_ev.type)
{ //检测到按键事件
switch (in_ev.value)
{
case 0: //松开按键
printf("code<%d>: 松开", in_ev.code);
break;
case 1: //按下按键
printf("code<%d>: 按下", in_ev.code);
break;
case 2: //长按按键
printf("code<%d>: 长按", in_ev.code);
break;
}
// 根据按键码打印按键功能
switch (in_ev.code)
{
case KEY_MENU:
printf(": HOME键\n");
break;
case KEY_BACK:
printf(": BACK键\n");
break;
case KEY_VOLUMEUP:
printf(": 音量增加键\n");
break;
case KEY_VOLUMEDOWN:
printf(": 音量减小键\n");
break;
// 其他按键码对应的功能在这里添加
default:
printf(": 未知键\n");
break;
}
}
}
close(fd); //关闭输入设备文件
return 0;
}
第23行对输入设备的信息进行读取,第30行打印读取到的type、code和value值,第34行-45行为一个switch循环,根据获取到的value进行按键状态的判断,第48行-第66行为一个switch循环,根据获取到的code值对按下的按键的功能进行判断。
保存退出之后,使用以下命令设置交叉编译器环境,并对demo68_input.c进行交叉编译,编译完成如下图所示:
export PATH=/usr/local/arm64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH
aarch64-none-linux-gnu-gcc -o demo68_input demo68_input.c
最后将交叉编译生成的 demo68_input文件拷贝到/home/nfs共享目录下即可。
18.2.2开发板测试
Buildroot系统启动之后,首先使用以下命令进行nfs共享目录的挂载(其中192.168.1.7为作者ubuntu的ip地址,需要根据自身ubuntu的ip来设置),如下图所示:
mount -t nfs -o nfsvers=3,nolock 192.168.1.7:/home/nfs /mnt
nfs共享目录挂载到了开发板的/mnt目录下,进入到/mnt目录下,如下图所示:
然后使用以下命令对接入设备信息进行查看,找到按键输入设备如下图所示:
cat /proc/bus/input/devices
从上述信息可以看到按键输入事件为event4,然后在/mnt目录下使用以下命令进行测试,然后分别按下开发板的BACK、VOL+、BOL-和HOME按键如下图所示:
./demo68_input /dev/input/event4
可以看到按键的按下抬起状态,按键的功能值,都被打印了出来,至此,关于板载按键输入设备的测试就完成了。