IGKBoard(imx6ull)-Input设备编程之按键控制

news2024/7/6 20:03:13

文章目录

    • 1- input子系统介绍
    • 2- input事件目录
      • (1)struct input_event 结构体
      • (2)type(事件类型):
      • (3)code(事件编码)
      • (4)value(事件的值)
    • 3- input事件设备名
      • (1)查看event编号对应的硬件设备方法一
      • (2)查看event编号对应的硬件设备方法二(按键检测)
    • 4- 编程进行按键测试
      • (1)源码
      • (2)linux运行
      • (3)开发板运行


1- input子系统介绍

Input子系统是Linux对输入设备提供的统一驱动框架。如按键、键盘、触摸屏和鼠标等输入设备的驱动
方式是类似的,当出现按键、触摸等操作时,硬件产生中断,然后CPU直接读取引脚电平,或通过SPI、
I2C等通讯方式从设备的寄存器读取具体的按键值或触摸坐标,然后把这些信息提交给内核。
使用Input子系统驱动的输入设备可以通过统一的数据结构提交给内核,该数据结构包括输入的时间、
类型、代号以及具体的键值或坐标,而内则通过/dev/input目录下的文件接口传递给用户空间。

Linux系统下的输入系统框架如下图所示:
事件传送的方向:硬件驱动层–>子系统核心–>事件处理层–>用户空间

  • ==硬件驱动层:==输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容

  • ==子系统核心层:==承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理,链接其他两个层之间的纽带与桥梁, 向下提供驱动层的接口, 向上提供事件处理层的
    接口。

  • ==事件处理层:==主要和用户空间进行交互,将硬件驱动层传来的事件报告给用户程序
    在这里插入图片描述
    在这里插入图片描述
    假设用户程序直接访问 /dev/input/event1 设备节点,或者使用tslib访问触摸屏设备节点,数据的流向
    如下:

  • 应用程序open()打开输入设备文件后调用read()读数据,此时输入设备没有事件发生,则读不到数
    据阻塞;

  • 用户操作设备(如按下设备、点击触摸屏),硬件上产生中断;

  • 输入系统驱动层对应的驱动程序处理中断:读取到数据,转换为标准的输入事件(一个struct
    input_event结构体),向核心层汇报。

  • 核心层可以决定把输入事件转发给上面哪个handler来处理。从handler的名字来看,它就是用来处
    输入操作的。比如:evdev_handler、kbd_handler、joydev_handler等。

  • 他们作用就是把核心层的数据返回给正在读取的APP,当APP正在调用read()系统调用等待数据
    时,evdev_handler会把它唤醒,这样APP就可以返回数据。


2- input事件目录

各层之间通信的基本单位是事件, 任何一个输入设备的动作都可以抽象成一种事件, 如键盘的按下,触摸屏的按下, 鼠标的移动等。
事件有三种属性: 类型(type), 编码(code),值(value), input 子系统支持的所有事件都定义在 input.h 中, 包括所有支持的类型, 所属类型支持的编码等。
应用程序空间在从input设备read()读取数据时,它的每个数据元素是 struct input_event(一个事件) 结构体类型,该结构体在Linux内核源码中其定义在include/uapi/linux/input.h文件中,而应用程序空间则定义
/usr/include/linux/input.h 文件中。

(1)struct input_event 结构体

该结构体的定义如下:

struct input_event {
struct timeval time;//该变量用于记录事件产生的时间戳。表示“自系统启动以来过了多少时间”,由秒和微秒long 类型 32bit)组成。
__u16 type;//类型
__u16 code;//编码
__s32 value;//值
};
typedef long __kernel_long_t;
typedef __kernel_long_t __kernel_old_time_t;
typedef __kernel_long_t __kernel_suseconds_t;
//Linux内核源码: include/uapi/linux/time.h
//应用编程头文件: /usr/include/linux/time.h
struct timeval {
__kernel_old_time_t tv_sec; /* seconds */
__kernel_suseconds_t tv_usec; /* microseconds */
};

(2)type(事件类型):

输入设备的事件类型。系统常用的默认类型有EV_KEY、 EV_REL和EV_ABS,分别用于表示按键状态改变事件、相对坐标改变事件及绝对坐标改变事件。其类型定义如下:

#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 /* LED */
#define EV_SND 0x12 /* sound(声音) */
#define EV_REP 0x14 /* 重复事件 */
#define EV_FF 0x15 /* 压力事件 */
#define EV_PWR 0x16 /* 电源事件 */
#define EV_FF_STATUS 0x17 /* 压力状态事件 */

(3)code(事件编码)

表示该类事件下的哪一个事件。例如 在EV_KEY事件类型中,code的值常用于表示键盘上具体的按键,比如数字键1、2、3,字母键A、B、C里等。查看定义:

#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
#define KEY_3 4
#define KEY_4 5
#define KEY_5 6
#define KEY_6 7
#define KEY_7 8
#define KEY_8 9
#define KEY_9 10
#define KEY_0 11
......
#define BTN_TRIGGER_HAPPY39 0x2e6
#define BTN_TRIGGER_HAPPY40 0x2e7

(4)value(事件的值)

对于EV_KEY事件类型,当按键按下时,该值为1;按键松开时,该值为0。


3- input事件设备名

查看==/dev/input==可以看到很多event*节点,事件编号与设备的联系不是固定的,它通常按系统检测到设
备的先后顺序安排event文件的编号。例如:在IGKBoard开发板上查看/dev/input文件夹下,有2个
event事件编号。
该目录下的文件实际上都是链接,如event1对应的就是访问开发板的按键的事件设备。

root@igkboard:~# ls /dev/input/
by-path  event0  event1

下面提供2个方法查看event编号对应的具体的硬件设备:

(1)查看event编号对应的硬件设备方法一

查看==/proc/bus/input/devices==文件查看事件编号对应的具体的硬件设备

root@igkboard:~# cat /proc/bus/input/devices
I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="20cc000.snvs:snvs-powerkey"
P: Phys=snvs-pwrkey/input0
S: Sysfs=/devices/platform/soc/2000000.bus/20cc000.snvs/20cc000.snvs:snvs-powerkey/input/input0
U: Uniq=
H: Handlers=kbd event0 
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0

I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="keys"
P: Phys=gpio-keys/input0
S: Sysfs=/devices/platform/keys/input/input1
U: Uniq=
H: Handlers=kbd event1 
B: PROP=0
B: EV=100003
B: KEY=10000000

可以看到keys对应的就是event1(其H: Handlers=kbd event1 ),下面是每一个设备信息中的I、N、
P、S、U、H、B对应的含义:

  • I:设备ID(id of the device)
  • N:设备名称(name of the device)
  • P:系统层次结构中设备的物理路径(physical path to the device in the system hierarchy)
  • S:文件系统的路径(sysfs path 位于sys)
  • U:设备的唯一标识码(unique identification code for the device)
  • H:与设备关联的输入句柄列表(list of input handles associated with the device)
  • B:位图(bitmaps)

B位图(bitmaps) :
PROP:设备属性
EV:设备支持的事件类型
KEY:此设备具有的键/按钮
MSC:设备支持的其他事件
LED:设备上的指示灯

我们来解释一下第一个设备中的“B:EV=3”:3的二进制是110,即bit1,bit2使能,表示设备支持1,2这两类事件,通过查看struct input_event中type的事件类型可以知道是EV_KEY 0x01 /* 按键事件 */ 以及EV_REL 0x02 /* 相对坐标事件 */

(2)查看event编号对应的硬件设备方法二(按键检测)

使用evtest工具查看事件编号对应的具体的硬件设备。
在开发input子系统驱动时,常常会使用 evtest 工具进行测试,它列出了系统当前可用的/dev/input/event0~1输入事件文件,并且列出了这些事件对应的设备名。

具体操作:evtest执行命令,因为我这个开发板event1对应的就是访问开发板的按键的事件设备,所以我输入1,然后按住用户按键然后松手,就会打印相关信息。
在这里插入图片描述
具体如下所示:

root@igkboard:~# evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0:      20cc000.snvs:snvs-powerkey
/dev/input/event1:      keys
Select the device event number [0-1]: 1
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100
Input device name: "keys"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 28 (KEY_ENTER)
Key repeat handling:
  Repeat type 20 (EV_REP)
    Repeat code 0 (REP_DELAY)
      Value    250
    Repeat code 1 (REP_PERIOD)
      Value     33
Properties:
Testing ... (interrupt to exit)
Event: time 1677379264.035611, type 1 (EV_KEY), code 28 (KEY_ENTER), value 1
Event: time 1677379264.035611, -------------- SYN_REPORT ------------
Event: time 1677379264.224893, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0
Event: time 1677379264.224893, -------------- SYN_REPORT ------------

SYN_REPORT即为同步事件。同步事件用于实现同步操作、告知接收者本轮上报的数据已
经完整。


4- 编程进行按键测试

(1)源码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/types.h>

#include <linux/kd.h>
#include <linux/keyboard.h>
#include <libgen.h>
#include <getopt.h>

/*此处在头文件中已经被定义了,这里只是让大家好理解
struct input_event
{
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};*/
#define BUTTON_CNT 10

#define EV_RELEASED 0
#define EV_PRESSED 1

void display_button_event(struct input_event *ev, int cnt);

int main(int argc, char * argv[])
{   
    char    *kbd_dev = "/dev/input/event1";//默认的监听按键设备路径,我们想要获取首先需要路径
    int     kbd_fd = -1;//)open()打开监听设备路径的文件描述符
    char    kbd_name[256] = "Unknow";//用于获取设备的名称
    fd_set  rds;//用于监听的事件的集合
    int     rv = 0;//函数的返回值
    int     size = sizeof(struct input_event);
    struct  input_event ev[BUTTON_CNT];

    /*普通用户kato的uid为1000,root用户的uid为0*/
    if((getuid()) != 0)
    {
        printf("You are not root. This program will not run.\n");
        //return 0;//这里为了运行如果不是rootID也可以运行
    }
    
    /*我们需要打开监听设备路径,于是需要文件描述符变量*/
    if((kbd_fd = open(kbd_dev, O_RDONLY)) < 0)
    {
        printf("File  %s opening error: %s\n", kbd_dev,strerror(errno));
        return -1;
    }
    printf("kbd_fd:%d\n", kbd_fd); 
    /*使用ioctl()函数可以获取对应设备的名字*/
    if((ioctl(kbd_fd, EVIOCGNAME(sizeof(kbd_name)), kbd_name)) < 0)
    {
        printf("ioctl get %s name failure: %s\n", kbd_dev, strerror(errno));
        return -2;
    }
    printf("Monitor input device %s(%s) event on poll mode:\n", kbd_dev, kbd_name);

    while(1)
    {
        FD_ZERO(&rds);/*清空select()的读事件集合*/
        FD_SET(kbd_fd, &rds);/*将按键设备的文件描述符加入到读事件集合中*/
        
        /* 
        使用select开启监听并等待多个描述符发生变化,第一个参数最大描述符+1,
        2、3、4参数分别是要监听读、写、异常三个事件的文军描述符集合;
        最后一个参数是超时时间(NULL-->永不超时,会一直阻塞住)
        */
        rv = select(kbd_fd+1, &rds, NULL, NULL, NULL);
        if(rv < 0)
        {   
            printf("Select() system call failure: %s\n", strerror(errno));
            goto CleanUp;
        }
        /*监听到按键发生了事件*/
        else if(FD_ISSET(kbd_fd, &rds))
        {
            /*read读取input数据包,数据包为input_event结构体类型的,ev是结构体类型的数组*/
            if((rv = read(kbd_fd, ev, size*BUTTON_CNT)) < size)
            {
                printf("Reading data from kbd_fd failure: %s\n", strerror(errno));
                break;
            }
            else
            {
                display_button_event(ev, rv/size);
            }
        }
    }
CleanUp:
    close(kbd_fd);

    return 0;
}

void display_button_event(struct input_event *ev, int cnt)
{
    int i;
    
    static struct timeval pressed_time;//按下的时间,timval结构体原型在上面说到过
    struct timeval duration_time;//按下持续的时间

    for(int i=0; i<cnt; i++)
    {
        if(EV_KEY==ev[i].type && EV_PRESSED==ev[i].value)
        {
            pressed_time = ev[i].time;
            printf("Keypad[%d] pressed time: %ld.%ld\n", ev[i].code, pressed_time.tv_sec, pressed_time.tv_usec);
        }

        if(EV_KEY==ev[i].type && EV_RELEASED==ev[i].value)
        {
            //计算时间差的函数,第1个参数-第2个参数的值的结果保存到第3个参数中
            timersub(&ev[i].time, &pressed_time, &duration_time);
            printf("keypad[%d] released time: %ld.%ld\n",ev[i].code, ev[i].time.tv_sec, ev[i].time.tv_usec);
            printf("keypad[%d] duration time: %ld.%ld\n",ev[i].code, duration_time.tv_sec, duration_time.tv_usec);
        }
    }

}

(2)linux运行

我的linux上event1也是监控按键设备的,所以在linux上也可以运行。后面我们加载到开发板上运行试一下。
在这里插入图片描述

(3)开发板运行

编写Makefile:

CC=arm-linux-gnueabihf-gcc
APP_NAME=key_test

all:clean
	@${CC} ${APP_NAME}.c -o ${APP_NAME}

clean:
	@rm -f ${APP_NAME}

在这里插入图片描述
不会搭建ttfp服务器的可以参考这篇文章:wpa_supplicant无线网络配置imx6ull以及搭建tftp服务器
tftp服务器下载到开发板(192.168.0.134是我虚拟机的IP):

root@igkboard:~# tftp -gr key_test 192.168.0.134
root@igkboard:~# chmod a+x key_test             
root@igkboard:~# ./key_test 
kbd_fd:3
Monitor input device /dev/input/event1(keys) event on poll mode:
Keypad[28] pressed time: 1677401101.420483
keypad[28] released time: 1677401101.524326
keypad[28] duration time: 0.103843
Keypad[28] pressed time: 1677401102.891474
keypad[28] released time: 1677401103.138384
keypad[28] duration time: 0.246910

我们按住用户按键然后放手,就可以看见打印的信息啦!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/374188.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【华为OD机试模拟题】用 C++ 实现 - 九宫格按键输入(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明九宫格按键输入题目输入输出示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高…

webp格式转换成png怎么转

相对于png 图片&#xff0c;webp比png小了45%&#xff0c;但是缺点是你压缩的时候需要的时间更久了&#xff1b;优点是体积小巧&#xff1b;缺点是兼容性不太好, 只有opera,和chrome支持&#xff0c;不仅如此在后期的编辑修改上也很多软件无法打开。所以我们通常要将webp格式转…

9.1 IGMPv1实验

9.4.1 IGMPv1 实验目的 熟悉IGMPv1的应用场景掌握IGMPv1的配置方法实验拓扑 实验拓扑如图9-7所示&#xff1a; 图9-7&#xff1a;IGMPv1 实验步骤 &#xff08;1&#xff09;配置IP地址 MCS1的配置 MCS1的IP地址配置如图9-8所示&#xff1a; 图9-8&#xff1a;MCS1的配置 …

xgboost学习-XGBoost的智慧

文章目录一、选择弱评估器&#xff1a;重要参数booster二、XGB的目标函数&#xff1a;重要参数objective三、求解XGB的目标函数四、参数化决策树 alpha&#xff0c;lambda五、寻找最佳树结构&#xff1a;求解 ω与T六、寻找最佳分枝&#xff1a;结构分数之差七、让树停止生长&a…

redis(10)事务和锁机制

Redis事务定义 Redis 事务是一个单独的隔离操作&#xff1a;事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中&#xff0c;不会被其他客户端发送来的命令请求所打断。 Redis 事务的主要作用就是串联多个命令防止别的命令插队。 Multi、Exec、discard Redis 事务中…

【数据挖掘实战】——应用系统负载分析与容量预测(ARIMA模型)

项目地址&#xff1a;Datamining_project: 数据挖掘实战项目代码 目录 一、背景和挖掘目标 1、问题背景 2、传统方法的不足 2、原始数据 3、挖掘目标 二、分析方法与过程 1、初步分析 2、总体流程 第一步&#xff1a;数据抽取 第二步&#xff1a;探索分析 第三步&a…

【华为OD机试模拟题】用 C++ 实现 - 内存池(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明内存池题目输入输出示例一输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:…

C++【string类用法详细介绍string类模拟实现解析】

文章目录string 类用法介绍及模拟实现一、string介绍二、string类常用接口1. string类对象的常见构造接口2.string类对象的常见容量接口3.string类对象的常见修改接口4. string类对象的常见访问及遍历接口5.string其他接口1.不常用查找接口2.字符替换3.字符串拼接4.字符串排序5…

纯x86汇编实现的多线程操作系统实践 - 第三章 BSP的守护执行

本章我们将详细讲解BSP剩下的执行代码&#xff0c;它们被安排在bp_32.asm文件中。bp_32.asm主要完成以下功能&#xff1a;系统中断初始化加载字符图形数据到内存区域将AP的启动代码和32位保护模式下的代码分别加载到内存中显示主界面以及系统启动信息向所有AP群发启动命令进入守…

linux 解压.gz文件 报错 gzip:stdin:not in gzip format(已解决)

目录 1、问题&#xff1a; 2、分析原因 3、解决办法 1、问题&#xff1a; 在解压一个以【.gz】&#xff08;注意不是.tar.gz&#xff09;结尾的压缩包时&#xff0c;遇到报错 【gzip&#xff1a;stdin&#xff1a;不是gzip格式】 翻译一下问题&#xff1a;【gzip&#xff1a;st…

纯x86汇编实现的多线程操作系统实践 - 第一章 系统整体结构说明

现代CPU都是多核系统&#xff0c;拥有多个执行内核&#xff08;即计算引擎&#xff09;&#xff0c;可并发执行不同的代码。在CPU众多的执行内核中&#xff0c;有一个为主执行内核&#xff08;BSP&#xff09;&#xff0c;在CPU上电后&#xff0c;该主执行内核会率先启动&#…

lighthouse-自定义Gatherer与Audits

这篇文章是Lighthouse的后续&#xff0c;之前介绍了 lighthouse的介绍和基本使用方法 Lighthouse组合Puppeteer检测页面 这两篇文章&#xff0c;在这两篇文章中介绍了lighthouse的整体架构和基本运行的逻辑&#xff0c;lighthouse默认也采集了足够丰富的数据供我们去分析页面的…

都在用 AI 生成美少女,而我却。。。

最近 AI 画画特别的火&#xff0c;你能从网上看到非常多好看的图片&#xff0c;于是我就开始了我的安装之旅&#xff0c;我看到的图是这样的。这样的。还有这样的。然后我就开始了我的 AI 安装生成计划。安装环境首先我们需要安装 Python 环境&#xff0c;因为这个需要显卡&…

NCRE计算机等级考试Python真题(二)

第二套试题1、关于算法的描述&#xff0c;以下选项中错误的是A.算法具有可行性、确定性、有穷性的基本特征B.算法的复杂度主要包括时间复杂度和数据复杂度C.算法的基本要素包括数据对象的运算和操作及算法的控制结构D.算法是指解题方案的准确而完整的描述正确答案&#xff1a; …

Java基础之日志

2.日志 2.1概述【理解】 概述 程序中的日志可以用来记录程序在运行的时候点点滴滴。并可以进行永久存储。 日志与输出语句的区别 输出语句日志技术取消日志需要修改代码&#xff0c;灵活性比较差不需要修改代码&#xff0c;灵活性比较好输出位置只能是控制台可以将日志信息写…

用于C++的对象关系映射库—YB.ORM

1 介绍YB.ORM YB.ORM 旨在简化与关系数据库交互的 C 应用程序的开发。 对象关系映射器(ORM) 通过将数据库表映射到类并将表行映射到应用程序中的对象来工作&#xff0c;这种方法可能不是对每个数据库应用程序都是最佳的&#xff0c;但它被证明在需要复杂逻辑和事务处理的应用程…

不怕被AirTag跟踪?苹果Find My技术越来越普及

苹果的 AirTag 自推出以来&#xff0c;如何有效遏制用户用其进行非法跟踪&#xff0c;是摆在苹果面前的一大难题。一家为执法部门制造无线扫描设备的公司近日通过 KickStarter 平台&#xff0c;众筹了一款消费级产品&#xff0c;可帮助用户检测周围是否存在追踪的 AirTag 等设备…

Spring中的FactoryBean 和 BeanFactory、BeanPostProcessor 和BeanFactoryPostProcessor解析

文章目录FactoryBean 和 BeanFactory后置处理器BeanPostProcessor 和 BeanFactoryPostProcessorBeanPostProcessorBeanFactoryPostProcessorFactoryBean 和 BeanFactory BeanFactory接⼝是容器的顶级接⼝&#xff0c;定义了容器的⼀些基础⾏为&#xff0c;负责⽣产和管理Bean的…

python元编程详解

什么是元编程 软件开发中很重要的一条原则就是“不要重复自己的工作&#xff08;Don’t repeat youself&#xff09;”&#xff0c;也就是说当我们需要复制粘贴代码时候&#xff0c;通常都需要寻找一个更加优雅的解决方案&#xff0c;在python中&#xff0c;这类问题常常会归类…

C++015-C++函数

文章目录C015-C函数函数目标char[]和stringchar[]char*string字符常量与字符串常量字符串的输入题目描述 字符串输出题目描述在线练习&#xff1a;总结C015-C函数 在线练习&#xff1a; http://noi.openjudge.cn/ https://www.luogu.com.cn/ 函数 目标 函数是指一段可以直接被…