温州市网站建设公司,电销卡代理加盟,嵌入式开发技术,114网站制作一、基本概念 按键、鼠标、键盘、触摸屏等都属于输入(input)设备#xff0c;Linux 内核为此专门做了一个叫做 input子系统的框架来处理输入事件。本质属于字符设备。 1. input子系统结构如下#xff1a; input 子系统分为 input 驱动层、input 核心层、input 事件处理层Linux 内核为此专门做了一个叫做 input子系统的框架来处理输入事件。本质属于字符设备。 1. input子系统结构如下 input 子系统分为 input 驱动层、input 核心层、input 事件处理层最终给用户空间提供可访问的设备节点。
1驱动层 输入设备的具体驱动程序比如按键驱动程序向内核层报告输入内容。
2核心层 a.承上启下为驱动层提供输入设备注册和操作接口 b.通知事件层对输入事件进行处理。 3事件层 主要和用户空间进行交互。 2. input 子系统的所有设备主设备号都为 13在drivers/input/input.c文件核心层中可以看到 在使用 input 子系统处理输入设备的时候就不需要去注册字符设备了我们只需要向系统注册一个 input_device 即可。
二、input驱动编写流程 1.注册 input_dev input_dev 结构体表示 input设备此结构体定义在 include/linux/input.h 文件中结构体中包含了各种事件输入类型如evbit[BITS_TO_LONGS(EV_CNT)]存放着不同事件对应的值可选的输入事件类型定义在input/uapi/linux/input.h 文件中比如常见的输入事件类型有同步事件、按键事件、重复事件等。 注册过程 a.申请input_dev结构体变量 struct input_dev *input_allocate_device(void) b.初始化input_dev的事件类型以及事件值。 c.向Linux系统注册input_dev设备 input_register_device(struct input_dev *dev) d.卸载驱动的时候要注销该设备并释放前面申请的input_dev。 void input_unregister_device(struct input_dev *dev) void input_free_device(struct input_dev *dev) 2.上报输入事件 首先是 input_event 函数此函数用于上报指定的事件以及对应的值
void input_event(struct input_dev *dev, //需要上报的 input_devunsigned int type, //上报的事件类型比如 EV_KEYunsigned int code, //事件码也就是我们注册的按键值比如 KEY_0、KEY_1 等等
int value //事件值比如 1 表示按键按下0 表示按键松开
)
input_event 函数可以上报所有的事件类型和事件值Linux 内核也提供了其他的针对具体事件的上报函数这些函数其实都用到了 input_event 函数。 当我们上报事件以后还需要使用 input_sync 函数来告诉 Linux 内核 input 子系统上报结束input_sync 函数本质是上报一个同步事件。
三、实验内容 利用input子系统进行按键输入实验。
1.思路 input子系统在input.h文件中已经注册了字符设备所以我们在写驱动的时候不需要再注册字符设备了我们需要做的是从设备树中获取到按键的节点以及gpio、然后初始化gpio为中断模式并申请中断、初始化定时器按键消抖使用完成以上操作后我们再初始化input_dev结构体变量、注册input_dev、设置事件和事件值、注册inpu_dev设备、上报事件。
2.代码
#include linux/types.h
#include linux/kernel.h
#include linux/delay.h
#include linux/ide.h
#include linux/init.h
#include linux/module.h
#include linux/gpio.h
#include linux/init.h
#include linux/module.h
#include linux/errno.h
#include asm/mach/map.h
#include asm/uaccess.h
#include asm/io.h
#include linux/cdev.h
#include linux/device.h
#include linux/of.h
#include linux/of_irq.h
#include linux/of_address.h
#include linux/of_gpio.h
#include linux/timer.h
#include linux/string.h
#include linux/input.h#define IMX6UIRQ_NAME imx6uirq
#define IMX6UIRQ_CNT 1
#define KEY_NUM 1
#define KEY0_VALUE 0X01 /* KEY0 按键值 */
#define INVAKEY 0xff
#define KEYINPUT_NAME keyinput
/*cmd*/
/*
_IO(type,nr) //没有参数的命令_IOR(type,nr,size) //该命令是从驱动读取数据_IOW(type,nr,size) //该命令是从驱动写入数据_IOWR(type,nr,size) //双向数据传输
*/
// #define CLOSE_CMD _IO(0xef, 1)
// #define OPEN_CMD _IO(0xef, 2)
// #define SETPERIOD_CMD _IOW(0xef, 3, int)
struct keydevice_dev
{int gpio; // IOchar name[10]; // IO nameint irqnum; //中断号unsigned char value; /* 按键对应的键值 */irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
};struct imx6uirq_dev
{dev_t devid; /*设备号*/struct cdev cdev;struct class *class;struct device *device;struct device_node *nd;int major;int minor;struct timer_list timer; //int timeperiod; /* 定时周期,单位为 ms */spinlock_t lock; //自旋锁struct keydevice_dev keydecs[KEY_NUM];atomic_t key_value; /* 有效的按键键值 */atomic_t release_key; /* 标记是否完成一次完成的按键*/unsigned char current_keynum; /* 当前的按键号 */struct input_dev *inputdev; /* input 结构体 */
};
struct imx6uirq_dev imx6uirq;/** description : 关闭/释放设备* param - filp : 要关闭的设备文件(文件描述符)* return : 0 成功;其他 失败*/
void timer_function(unsigned long arg)
{struct keydevice_dev *keydecs;struct imx6uirq_dev *dev (struct imx6uirq_dev*)arg;int ret 0;unsigned char num;unsigned char value;num dev-current_keynum;keydecs dev-keydecs[num];value gpio_get_value(keydecs-gpio);/* 读取 IO 值 */if(value 0) /*按键按下*/{// printk(KEY0_PUSH\r\n);/*上报按键值*/input_report_key(dev-inputdev,keydecs-value,1);input_sync(dev-inputdev);}else if(value1)//释放{// printk(KEY0_RELEASE\r\n);/*上报按键值*/input_report_key(dev-inputdev,keydecs-value,0);input_sync(dev-inputdev);}}/* description : 中断服务函数开启定时器延时 10ms* 定时器用于按键消抖。* param - irq : 中断号* param - dev_id : 设备结构。* return : 中断执行结果*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{struct imx6uirq_dev *dev (struct imx6uirq_dev *)dev_id;dev-current_keynum 0;dev-timer.data (volatile long)dev_id;mod_timer(imx6uirq.timer,jiffiesmsecs_to_jiffies(10));//消抖
// printk(irq handler\r\n);return IRQ_RETVAL(IRQ_HANDLED);
}static int keyirq_init(void)
{int ret 0;int i 0;imx6uirq.nd of_find_node_by_path(/key);if (imx6uirq.nd NULL){ret -EINVAL;goto fail_findnd;printk(find node failed);}/* 提取 GPIO */for (i 0; i KEY_NUM; i){ imx6uirq.keydecs[i].gpio of_get_named_gpio(imx6uirq.nd, key-gpios, i);if (imx6uirq.keydecs[i].gpio 0){printk(get gpio %d failed\r\n, i);}printk(imx6uirq.keydecs[%d].gpio %d,i,imx6uirq.keydecs[i].gpio);}/* 初始化 key 所使用的 IO并且设置成中断模式 */for (i 0; i KEY_NUM; i){memset(imx6uirq.keydecs[i].name, 0, sizeof(imx6uirq.keydecs[i].name)); //给数组清0按字节赋值sprintf(imx6uirq.keydecs[i].name, KEY%d, i); //给数组赋值gpio_request(imx6uirq.keydecs[i].gpio, imx6uirq.keydecs[i].name); //申请IOgpio_direction_input(imx6uirq.keydecs[i].gpio); //设置为输入模式imx6uirq.keydecs[i].irqnum irq_of_parse_and_map(imx6uirq.nd, i); //获取中断号printk(gpio %d irqnum%d\r\n, imx6uirq.keydecs[i].gpio, imx6uirq.keydecs[i].irqnum);}/* 申请中断 */imx6uirq.keydecs[0].handler key0_handler;imx6uirq.keydecs[0].value KEY_0;/*根据按键的个数申请中断*/for (i 0; i KEY_NUM; i){ret request_irq(imx6uirq.keydecs[i].irqnum, imx6uirq.keydecs[i].handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,imx6uirq.keydecs[i].name, imx6uirq);if (ret 0){printk(irq %d request failed, imx6uirq.keydecs[i].irqnum);ret -EINVAL;goto fail_request_irq;}}/* 创建定时器 */init_timer(imx6uirq.timer);imx6uirq.timer.function timer_function;/*申请input_dev*/imx6uirq.inputdev input_allocate_device();imx6uirq.inputdev-name KEYINPUT_NAME;__set_bit(EV_KEY,imx6uirq.inputdev-evbit);/*按键事件*/__set_bit(EV_REP,imx6uirq.inputdev-evbit);/*重复事件*/__set_bit(KEY_0,imx6uirq.inputdev-keybit);/* 初始化 input_dev设置产生哪些按键 */// imx6uirq.inputdev-evbit[0]BIT_MASK(EV_KEY)|BIT_MASK(EV_REP);// input_set_capability(imx6uirq.inputdev,EV_KEY,KEY_0);/* 注册输入设备 */ret input_register_device(imx6uirq.inputdev);if(ret){printk(register failed\r\n);return ret;}return 0;fail_findnd:
fail_request_irq:return ret;
} /*驱动入口函数*/
static int __init imx6uirq_init(void)
{ keyirq_init();return 0;
}/*驱动出口函数*/
static void __exit imx6uirq_exit(void)
{int i 0;/* 删除定时器 */del_timer_sync(imx6uirq.timer);/* 释放中断 */for (i 0; i KEY_NUM; i){free_irq(imx6uirq.keydecs[i].irqnum, imx6uirq);}input_unregister_device(imx6uirq.inputdev);input_free_device(imx6uirq.inputdev);printk(imx6uirq_exit !!!\r\n);
}module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE(GPL);
MODULE_AUTHOR(dongdong);
3.代码分析
4.编写测试APP 1 #include stdio.h2 #include unistd.h3 #include sys/types.h4 #include sys/stat.h5 #include sys/ioctl.h6 #include fcntl.h7 #include stdlib.h8 #include string.h9 #include poll.h
10 #include sys/select.h
11 #include signal.h
12 #include fcntl.h
13 #include linux/input.h
14
15 /* 定义一个 input_event 变量存放输入事件信息 */
16 static struct input_event inputevent;
17 /*
18 * description : main主程序
19 * param - argc : argv数组元素个数
20 * param - argv : 具体参数
21 * use: ./timerAPP /dev/gpioled
22 * return : 0 成功;其他 失败
23 */
24 int main(int argc, char *argv[])
25 {
26
27 char *filename;
28 int fd;
29 int ret 0;
30
31 /*打开文件*/
32 filename argv[1];
33
34 if (argc ! 2) //检查输入参数个数
35 {
36 printf(useage error\r\n);
37 return -1;
38 }
39
40 fd open(filename, O_RDWR);
41 if (fd 0)
42 {
43 printf(cant open file %s\r\n, filename);
44 return -1;
45 }
46 /* 循环读取按键值数据 */
47 while (1)
48 {
49 ret read(fd, inputevent,sizeof(inputevent));
50 if(ret0)
51 {
52 printf(读取数据失败\r\n);
53 }
54 else{
55 switch(inputevent.type)
56 {
57 case EV_KEY:
58 if(inputevent.code BTN_MISC)
59 {
60 printf(key press\r\n);
61 printf(key %d %s\r\n,inputevent.code,inputevent.value ? press:release);
62 }
63 else
64 {
65 printf(button %d %s\r\n,inputevent.code,inputevent.value?press:release);
66 }
67 break;
68 /* 其他类型的事件自行处理 */
69 case EV_REL:
70 break;
71 case EV_ABS:
72 break;
73 case EV_MSC:
74 break;
75 case EV_SW:
76 break;
77
78 }
79
80 }
81 }
82 ret close(fd);
83 if (ret 0)
84 {
85 printf(file %s close failed!\r\n, argv[1]);
86 return -1;
87 }
88 return 0;
89
90 }
5.实验结果 按下按键与松开按键 从上图实验结果可以看出inpu_dev结构体的成员变量的值从左到右依次是
此事件发生的时间s、us均为32位)、事件类型16位、事件编码16位、按键值32位 四、也可以用Linux自带的按键驱动
1.make menuconfig配置
- Device Drivers
- Input device support
- Generic input layer (needed for keyboard, mouse, ...) (INPUT [y])
- Keyboards (INPUT_KEYBOARD [y])
-GPIO Buttons 2.修改设备树文件 可以参考Linux内核文档Documentation/devicetree/bindings/input/gpio-keys.txt 参考上述文件修改开发板按键为回车键为LCD实验作准备。 1 gpio-keys {2 compatible gpio-keys;3 #address-cells 1;4 #size-cells 0;5 autorepeat;6 key0 {7 label GPIO Key Enter;8 linux,code KEY_ENTER;9 gpios gpio1 18 GPIO_ACTIVE_LOW;
10 };
11 };
3.最后实验结果