Linux设备驱动开发之input子系统(按键)

电路原理图

        由上图可知,当按下按键管脚电平为低电平。底板原理图参考之前的按键开发图片,设备树也没有改变。

设备树

 

驱动代码参考

        该驱动代码在之前的按键驱动代码基础上应用内核input子系统,更具通用性和兼容性。

#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/input.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>

#define KEY_NAME "key_dev"
#define KEY_NUM   1

typedef struct KEY{

	struct input_dev *input_dev;
	struct device_node *node;
	int key_gpio;
	int key_irq;
	struct timer_list time;
	int key_status;
}KEY_DEV;

static KEY_DEV  key_dev;

static irqreturn_t key_interupt_handler(int irq, void *data)
{
	KEY_DEV *dev = (KEY_DEV *)data;
	if(gpio_get_value(dev->key_gpio)){
		dev->key_status = KEY_UP;
	}else{
		dev->key_status = KEY_DOWN;
	}
	mod_timer(&dev->time, jiffies + msecs_to_jiffies(20));
	return IRQ_HANDLED;
}

static void timer_handler(unsigned long arg){
	KEY_DEV *dev = (KEY_DEV *)arg;
	if(gpio_get_value(dev->key_gpio) && (dev->key_status == KEY_UP)){
		input_report_key(dev->input_dev, KEY_0, 0); //上报按键值
		input_sync(dev->input_dev); //同步事件
	}else if(!gpio_get_value(dev->key_gpio) && dev->key_status == KEY_DOWN){
		input_report_key(dev->input_dev, KEY_0, 1); //上报按键值
		input_sync(dev->input_dev); //同步事件
	}
	return;

}
static int __init keydev_init(void)
{
	int ret;
	//从设备树找到key节点
	key_dev.node = of_find_node_by_path("/key0");
	if(!key_dev.node){
		printk(KERN_ERR "Couldn't find key device node\n");
		goto error;
	}

	//从设备树获取GPIO
	key_dev.key_gpio = of_get_named_gpio(key_dev.node, "key-gpios", 0);
	if (!gpio_is_valid(key_dev.key_gpio))
		goto error;

	//向内核申请GPIO
	ret = gpio_request(key_dev.key_gpio, KEY_NAME);
	if (ret) {
		pr_err("Requesting KEY GPIO failed!\n");
		goto error;
	}

	gpio_direction_input(key_dev.key_gpio);

	key_dev.key_irq =  irq_of_parse_and_map(key_dev.node, 0);
	if (key_dev.key_irq == NO_IRQ) {

		printk(KERN_ERR "Invalid IRQ \n");
		goto err_irq_map;
	}

	//向内核申请中断,上升沿与下降沿触发
	ret = request_irq(key_dev.key_irq, key_interupt_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, KEY_NAME, &key_dev);
	if (ret) {
		printk(KERN_WARNING " failed to register interrupt handler for key\r\n");
		goto err_request_irq;
	}

	//开启定时器,后面用于按键消抖
	init_timer(&key_dev.time);
	key_dev.time.function = timer_handler;
	key_dev.time.expires = ~0;
	key_dev.time.data = (unsigned long)&key_dev;
	add_timer(&key_dev.time);

	key_dev.input_dev = input_allocate_device();
	if (!key_dev.input_dev){
		printk(KERN_ERR"Failed to allocate device for device \n");
		goto error_allocate_dev;
	}


	//设置事件和事件值
	__set_bit(EV_KEY, key_dev.input_dev->evbit);
	__set_bit(EV_REP, key_dev.input_dev->evbit);
	__set_bit(KEY_0, key_dev.input_dev->keybit);


	//注册input_dev
	ret = input_register_device(key_dev.input_dev);
	if (ret) {
		printk(KERN_ERR "Can't register input device: %d\n", ret);
		goto err_register_input_device;
	}
	return 0;

err_register_input_device:
	input_free_device(key_dev.input_dev);
error_allocate_dev:
	del_timer(&key_dev.time);
	free_irq(key_dev.key_irq, &key_dev);

err_request_irq:

err_irq_map:
	gpio_free(key_dev.key_gpio);
error:
	return -EFAULT;
} 


static void __exit keydev_exit(void)
{
	input_unregister_device(key_dev.input_dev);
	input_free_device(key_dev.input_dev);
	del_timer(&key_dev.time);
	free_irq(key_dev.key_irq, &key_dev);
	gpio_free(key_dev.key_gpio);
} 


module_init(keydev_init);
module_exit(keydev_exit);
MODULE_LICENSE("GPL");


 应用层测试代码参考

 

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

struct input_event event;

int main(int argc, char *argv[])
{
	int fd;
	int ret;

	if(argc != 2){
		fprintf(stderr, "Usage: ./%s </dev/input/eventx>", argv[0]);
		return EXIT_FAILURE;
	}

	fd = open(argv[1], O_RDONLY);
	if(fd < 0){
		perror("open dev_file");
		return EXIT_FAILURE;
	} 

	while(1){
		ret = read(fd, &event, sizeof(struct input_event));
		if(ret < 0){
			perror("read file error");
			close(fd);
			return EXIT_FAILURE;
		}else if(ret == sizeof(struct input_event) && event.type == EV_KEY){
			if(event.value == 1){
				printf("key down\n");
			}else if(event.value == 0){
				printf("key up\n");
			}else if(event.value == 2){
				printf("key down keep\n");
			}
		}

	}

	close(fd);
	return 0;
}

 Makefile文件参考

ARCH = arch
CROSS_COMPILE = arm-linux-gnueabihf
CC = ${CROSS_COMPILE}-gcc
KERNELDIR := /home/linux_0
CURRENT_PATH := $(shell pwd)
	obj-m := input_key.o

build: kernel_modules

kernel_modules:

	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
	$(CC) -o key_test key_test.c
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
	@rm key_test -f

 开发板验证

        将生成的ko文件与测试文件拷贝到开发板进行功能验证:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值