imx6ull/linux应用编程学习(3) 输入设备应用编程(上)(按键)

0.概念

        输入设备:可以产生输入事件的设备

        Linux系统设计了一个兼容所有输入设备的框架,就是input子系统,其直接向应用层提供了一套统一的接口,其在/dev/input目录下。

          流程:如果要读取输入设备,一般遵循以下流程。

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

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

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

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

        如果无数据读时,程序会进入阻塞状态(休眠),直到产生输入设备,才会被唤醒去读取设备。

        当程序打开设备文件,发起读(read)操作,其实每一次都是获取一个struct input_event 结构体类型数据。

struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};

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

             按键类事件:

#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 键
……

        相对位移事件:

#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)

        绝对位移事件:

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
......

        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 值而定!

          数据同步:
         在进行那么多操作后,程序需要读到完整数据,而这则就需要用到数据同步。

        内核将本轮需要上报、发送给接收者的数据全部上报完毕后,接着会上报一个同步事件,以告知应用程序本轮数据已经完整、 可以进行同步了,可以实现数据完整性。

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

#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)

        上报的同步事件通常是 SYN_REPORT, 而 value 值通常为 0。

1.输入结构体数据读取实验

        任务:从上可知,输入设备调用 read()会读取到一个 struct input_event 类型数据,因此该任务需要将读取到的struct input_event 类型数据中的每一个元素打印出来。

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

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
int main(int argc, char *argv[])
{
struct input_event in_ev = {0};
int fd = -1;
/* 校验传参 */
if (2 != argc) {
fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);
exit(-1);
}
/* 打开文件 */
if (0 > (fd = open(argv[1], O_RDONLY))) {
perror("open error");
exit(-1);
}
for ( ; ; ) {
/* 循环读取数据 */
if (sizeof(struct input_event) !=
read(fd, &in_ev, sizeof(struct input_event))) {
perror("read error");
exit(-1);
}
printf("type:%d code:%d value:%d\n",
in_ev.type, in_ev.code, in_ev.value);
}
}

程序理解:

1.struct input_event in_ev = {0};将结构体的所有成员初始化为零。可以确保结构体的所有成员都被初始化为已知的值,避免使用未初始化成员导致的未定义行为,而input_event 结构体在上面有提到。

2.

 if (0 > (fd = open(argv[1], O_RDONLY))) 

打开输入的第二个参数文件路径,并设为只读,所有你需要在终端输入你要打开的路径。

3.

 for ( ; ; ) {

        /* 循环读取数据 */
        if (sizeof(struct input_event) !=
            read(fd, &in_ev, sizeof(struct input_event))) {
            perror("read error");
            exit(-1);
        }

        printf("type:%d code:%d value:%d\n",
                in_ev.type, in_ev.code, in_ev.value);
    }

if (sizeof(struct input_event) != read(fd, &in_ev, sizeof(struct input_event))) 

        这是判断读取操作是否失败或读取的数据不完整。sizeof(struct input_event)是获取 struct input_event 结构体的大小(以字节为单位)。

     read(fd, &in_ev, sizeof(struct input_event))

  • fd:文件描述符,通常是一个已经打开的文件或设备。
  • &in_ev:指向 in_ev 变量的指针,表示读取的数据将被存储到这个变量中。
  • sizeof(struct input_event):读取的字节数,等于 struct input_event 的大小

为什么用这个语句判断?因为read函数读取失败返回-1,而读取成功的话会返回读取到的字节数。
           最后打印出结构体的值。

  上机测试:

首先在开发板执行cat /proc/bus/input/devices指令,可以看到设备信息:

后回到ubuntu上,

对read_input进行工具链编译,不会配置${CC}环境的可以看我另外一篇文章:

Linux环境变量 ${CC}的配置方法-CSDN博客

编译完后多了read_input文件

后连上开发板,使用scp -r 指令实现文件传输,利用mobaxterm传到开发板上。

 scp -r book@192.168.5.12:/home/book/project/APP/app/17_input/read_input /home/root/app

scp -r 【用户名】@【ubuntu的ip地址】:【unbuntu上要传入文件的路径】  【开发板要存入的路径】

执行成功后,对其进行操作:./read_input /dev/input/event2,按下黄色按键可以发现有数据传入

可见上面的type、code、value信息,

type=1,说明是上报的是按键类事件EV_KEY(上面type介绍哪里有图),cpde=114 对应的是键盘上的 KEY_VOLUMEDOWN 按键,这儿开发板对应的是那个黄色按键

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

如果长按,value则上报2。

2.按键应用实验

任务:如果是按下,则上报 KEY_A 事件时, value=1;如果是松开,则 value=0;如果是长按,则 value=2。接下来编写按键应用程序,读取按键状态并将结果打印出来。

加上注释的源码:

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

int main(int argc, char *argv[])
{
    struct input_event in_ev = {0}; // 定义并初始化input_event结构体
    int fd = -1; // 文件描述符
    int value = -1; // 按键值

    /* 校验传参 */
    if (2 != argc) {
        fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);
        exit(-1); // 如果参数个数不对,打印用法信息并退出
    }

    /* 打开文件 */
    if (0 > (fd = open(argv[1], O_RDONLY))) {
        perror("open error");
        exit(-1); // 打开文件失败,打印错误信息并退出
    }

    for ( ; ; ) {

        /* 循环读取数据 */
        if (sizeof(struct input_event) !=
            read(fd, &in_ev, sizeof(struct input_event))) {
            perror("read error");
            exit(-1); // 读取数据失败,打印错误信息并退出
        }

        // 判断事件类型是否为按键事件
        if (EV_KEY == in_ev.type) {
            switch (in_ev.value) {
            case 0:
                printf("code<%d>: 松开\n", in_ev.code); // 按键松开事件
                break;
            case 1:
                printf("code<%d>: 按下\n", in_ev.code); // 按键按下事件
                break;
            case 2:
                printf("code<%d>: 长按\n", in_ev.code); // 按键长按事件
                break;
            }
        }
    }
}

测试:

工具链编译:

输入 ./read_key /dev/input/event2

code值可以查找到对应的按键

  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值