[imx6ull]input设备编程-按键控制


一、input子系统

1.input子系统介绍

Input 子系统是管理输入的子系统, 和 pinctrl 和 gpio 子系统一样, 都是 Linux 内核针对某一类设备而创建的框架。

Input子系统是Linux对输入设备提供的统一驱动框架。如按键、键盘、触摸屏和鼠标等输入设备的驱动方式是类似的,当出现按键、触摸等操作时,硬件产生中断,然后CPU直接读取引脚电平,或通过SPI、I2C等通讯方式从设备的寄存器读取具体的按键值或触摸坐标,然后把这些信息提交给内核。

使用Input子系统驱动的输入设备可以通过统一的数据结构提交给内核,该数据结构包括输入的时间、类型、代号以及具体的键值或坐标,而内则通过/dev/input目录下的文件接口传递给用户空间。

2.input子系统框架

在这里插入图片描述

3.input事件结构

应用程序空间在从input设备read()读取数据时,它的每个数据元素是struct input_event 结构体类型,该结构体在Linux内核源码中其定义在 include/uapi/linux/input.h 文件中,而应用程序空间则定义在 /usr/include/linux/input.h 文件中。

struct input_event结构体:

struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};
  • time:该变量用于记录事件产生的时间戳。表示“自系统启动以来过了多少时间”,由秒和微秒(long 类型 32bit)组成。
  • type:输入设备的事件类型。系统常用的默认类型有EV_KEY、 EV_REL和EV_ABS,分别用于表示按键状态改变事件、相对坐标改变事件及绝对坐标改变事件。
  • code:事件代号,表示该类事件下的哪一个事件。例如 在EV_KEY事件类型中,code的值常用于表示键盘上具体的按键,比如数字键1、2、3,字母键A、B、C里等。
  • value :事件的值。对于EV_KEY事件类型,当按键按下时,该值为1;按键松开时,该值为0。

3.input事件设备名

首先我们可以/dev/input路径下看见有许多event节点,每一个event文件都对应相应的设备,不过event事件的编号与对应的设备并不是固定的,因为编号是系统检测到设备的先后顺序安排的。

在这里插入图片描述

查看对应的硬件设备的方法有多种,如下:

(1)查看by-path中的时间编号的对应的硬件设备

ls -l /dev/input/by-path/

在这里插入图片描述

(2)通过/sys/class/input目录查看

上一个方法目录下的文件实际上都是链接,/dev下的设备都是通过/sys导出的,所以也可以通过/sys/class/input目录查看。

cat /sys/class/input/event1/device/name

在这里插入图片描述

(3)通过/proc/bus/input/devices文件查看

cat /proc/bus/input/devices

在这里插入图片描述

其中:

  • I:id of the device 设备ID
  • 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 位图

(4)使用evtest工具查看事件编号对应的具体的硬件设备

在开发input子系统驱动时,常常会使用 evtest 工具进行测试,它列出了系统当前可用的事件文件,并且列出了这些事件对应的设备名。

在这里插入图片描述

二、按键测试

1.测试程序源代码

keypad.c

/*********************************************************************************
 *      Copyright:  (C) 2023 Deng Yonghao<dengyonghao2001@163.com>
 *                  All rights reserved.
 *
 *       Filename:  keypad.c
 *    Description:  This file keypad test process
 *                 
 *        Version:  1.0.0(2023年03月16日)
 *         Author:  Deng Yonghao <dengyonghao2001@163.com>
 *      ChangeLog:  1, Release initial version on "2023年03月16日 10时00分36秒"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <libgen.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <linux/kd.h>
#include <linux/keyboard.h>

#if	0
struct input_event
{
	struct timeval time;
	__u16 type;
	__u16 code;
	__s32 value;
}
#endif

#define EV_RELEASED			0
#define	EV_PRESSED			1

#define	BUTTON_CNT			10

/*单个C文件并没有对应.h文件,则直接在C文件中声明函数*/
void usage(char	*name);

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

int main(int argc, char **argv)
{
	char				*kbd_dev = "/dev/input/event1"; //可知默认监听按键的设备为event2
	char				kbd_name[256] = "Unknown"; //用于保存获取到的设备名称
	int					kbd_fd = -1; //open()打开文件的文件描述符
	int					rv = 0; //函数的返回值,默认为0
	int					opt; //getopt_long解析命令行参数返回值
	int					size = sizeof(struct input_event);
	fd_set				rds;//用于监听的事件的集合

	struct input_event	ev[BUTTON_CNT];
	
	/* getopt_long参数函数第四个参数的定义,二维数组,每个成员由四个元素组成 */
	/* { 参数名称,是否带参数,flags指针(NULL时将val的数值从getopt_long的返回值返回出去),
            函数找到该选项时的返回值(字符)}*/
	struct option long_options[] = {
		{"device", required_argument, NULL, 'd'},
		{"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}

	};

	/*获取命令行参数的解析返回值*/
	while((opt = getopt_long(argc, argv, "d:h", long_options, NULL)) != -1)
	{
		switch(opt)
		{
			case 'd':
				kbd_dev = optarg;
				break;

			case 'h':
				usage(argv[0]);
				return 0;

			default:
				break;
		}
	}

	if(NULL == kbd_dev)
	{
		/*命令行argv[0]是输入的命令,如 ./keypad*/ 
        usage(argv[0]);
        return -1;
	}

	/* 获取uid 建议以root权限运行确保可以正常运行 */
    if ((getuid ()) != 0)
	{
        printf ("You are not root! This may not work...\n");
	}

    /* 打开按键对应的设备节点,如果错误则返回负数 */
    if ((kbd_fd = open(kbd_dev, O_RDONLY)) < 0)
    {
        printf("Open %s failure: %s", kbd_dev, strerror(errno));
        return -1;
    }

    /* 使用ioctl获取 /dev/input/event*对应的设备名字 */
    ioctl (kbd_fd, EVIOCGNAME (sizeof (kbd_name)), kbd_name);
    printf ("Monitor input device %s (%s) event on poll mode:\n", kbd_dev, kbd_name);

	/*用select监听按键事件*/
	while(1)
	{
		FD_ZERO(&rds);//清空select()的读事件集合
		FD_SET(kbd_fd, &rds);//将按键设备的文件描述符加入到读事件集合中

		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结构体类型*/
			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 usage(char *name)
{
	char				*progname = NULL;
	char				*ptr = NULL;

	ptr = strdup(name);//字符串拷贝函数,该函数内部将调用malloc()来动态分配内存,然后将$name字符串内容拷贝到malloc分配的内存中,这样使用完之后需要free释放内存.
	progname = basename(ptr);//去除该可执行文件的路径名,获取其自身名称(即keypad)

	printf("Usage: %s [-p] -d <device>\n", progname);
    printf(" -d[device  ] button device name\n");
    printf(" -p[poll    ] Use poll mode, or default use infinit loop.\n");
    printf(" -h[help    ] Display this help information\n");

	free(ptr);//释放内存
	return;
}

/*display_button_event函数来解析按键设备上报的数据,并答应按键按下的相关信息*/
void display_button_event(struct input_event *ev, int cnt)
{
	int 							i;
	static struct timeval			pressed_time;//该变量用来存放按键按下的时间,是静态变量
	struct timeval					duration_time;//该变量用来存放按键按下持续时间
												  
	for(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)
		{
			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.Makefile

CC=arm-linux-gnueabihf-gcc
APP_NAME=keypad
​
all:clean
    @${CC} ${APP_NAME}.c -o ${APP_NAME}
​
clean:
    @rm -f ${APP_NAME}

3.运行测试

在这里插入图片描述

成功得到了按下与放开的时间,并计算出了按下按键的持续时间,可见我每一次按下按键都增加了按键按下的持续时间,运行测试成功。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邓永豪

打赏一下,好运来敲门!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值