电路原理图
由上图可知,当按下按键管脚电平为低电平。底板原理图参考之前的按键开发图片,设备树也没有改变。
设备树
驱动代码参考
该驱动代码在之前的按键驱动代码基础上应用内核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文件与测试文件拷贝到开发板进行功能验证: