使用的是正点原子linux开发版
主要是结合之前的定时器来处理按键中断
先是通过按键触发按键中断,之后在按键中断里面触发10ms的定时器中断,然后等待定时器的10ms在接着去处理。
这里借用上半部和下半部处理,在中断里面我们要求使用快进快出,所以这是上半部,下半部则是我们处理相关的地方。
主要是在设备树节点中增加按键节点。
key {
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpio1>;
interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
status = "okay";
};
这个节点和之前的设备树驱动gpio节点是一致的只是增加了中断的相关信息,这两行。
interrupt-parent = <&gpio1>;
interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
其中IRQ_TYPE_EDGE_BOTH这个变量来自于 include/linux/irq.h目录下的定义,就是定义为电平跳跃触发。
enum {
IRQ_TYPE_NONE = 0x00000000,
IRQ_TYPE_EDGE_RISING = 0x00000001,
IRQ_TYPE_EDGE_FALLING = 0x00000002,
IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
IRQ_TYPE_LEVEL_HIGH = 0x00000004,
IRQ_TYPE_LEVEL_LOW = 0x00000008,
……
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define INTERRUPT_NAME "interrupt"
#define INTERRUPT_CT 1
struct irq_desc_struct {
unsigned int irq_num;
irqreturn_t (*handler)(int, void *);
};
struct irq_desc_struct irq_desc;
struct interrupt_struct{
int major;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *nd;
int gpio_num;
struct timer_list timer;
atomic_t key_value;
atomic_t key_free;
};
struct interrupt_struct interrupt_dev;
static int interrupt_open(struct inode *inode, struct file *file)
{
file->private_data = &interrupt_dev;
return 0;
}
ssize_t interrupt_read(struct file *file, char __user * buf,size_t len, loff_t * ppos)
{
unsigned char key_value = 0;
unsigned char key_free = 0;
int ret = 0;
struct interrupt_struct *dev = (struct interrupt_struct *)file->private_data;
key_free = atomic_read(&dev->key_free);
if(key_free){
key_value = atomic_read(&dev->key_value);
ret = copy_to_user(buf,&key_value,1);
atomic_set(&interrupt_dev.key_free,0);
}
else {
goto data_error;
}
return 0;
data_error:
return -EINVAL;
}
struct file_operations interrupt_fops = {
.owner = THIS_MODULE,
.open = interrupt_open,
.read = interrupt_read,
};
irqreturn_t key_function(int irq_num, void *dev)
{
struct interrupt_struct *my_dev = (struct interrupt_struct *)dev;
mod_timer(&my_dev->timer,msecs_to_jiffies(10));
return IRQ_RETVAL(IRQ_HANDLED);
}
void timer_function(unsigned long arg)
{
int value;
struct interrupt_struct *dev = (struct interrupt_struct *)arg;
value = gpio_get_value(dev->gpio_num);
if(value == 0){
atomic_set(&dev->key_value,value);
atomic_set(&dev->key_free,1);
// printk("dowm\r\n");
}
else if(value == 1){
atomic_set(&dev->key_value,value);
atomic_set(&dev->key_free,1);
// printk("up\r\n");
}
}
//按键初始化
int key_init(void)
{
int ret = 0;
interrupt_dev.nd = of_find_node_by_path("/key");
if(interrupt_dev.nd == NULL){
printk("no find node\r\n");
return -1;
}
printk("find node\r\n");
interrupt_dev.gpio_num = of_get_named_gpio(interrupt_dev.nd,"key-gpio",0);
if(interrupt_dev.gpio_num < 0){
printk("get gpio num err\r\n");
return -2;
}
printk("gpio num:%d\r\n",interrupt_dev.gpio_num);
gpio_free(interrupt_dev.gpio_num);
ret = gpio_request(interrupt_dev.gpio_num,"key0");
if(ret < 0){
printk("gpio already use\r\n");
return -3;
}
ret = gpio_direction_input(interrupt_dev.gpio_num);
if(ret < 0){
printk("gpio set err\r\n");
gpio_free(interrupt_dev.gpio_num);
return -4;
}
irq_desc.irq_num = irq_of_parse_and_map(interrupt_dev.nd,0);
printk("irq num :%d\r\n",irq_desc.irq_num);
irq_desc.handler = key_function;
// request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
// const char *name, void *dev)
ret = request_irq(irq_desc.irq_num,irq_desc.handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,"key0",(void*)&interrupt_dev);
if(ret < 0){
printk("interrupt request err\r\n");
return -EFAULT;
}
atomic_set(&interrupt_dev.key_value,3);
atomic_set(&interrupt_dev.key_free,0);
init_timer(&interrupt_dev.timer);
interrupt_dev.timer.function = timer_function;
interrupt_dev.timer.data = (unsigned long)&interrupt_dev;
return 0;
}
static int __init interrup_init(void)
{
int ret = 0;
ret = key_init();
if(ret < 0){
printk("key init err\r\n");
goto chrdev_err;
}
interrupt_dev.major = 0;
if(interrupt_dev.major){
interrupt_dev.devid = MKDEV(interrupt_dev.major,0);
ret = register_chrdev_region(interrupt_dev.devid,INTERRUPT_CT,INTERRUPT_NAME);
}
else{
ret = alloc_chrdev_region(&interrupt_dev.devid,0,INTERRUPT_CT,INTERRUPT_NAME);
interrupt_dev.major = MAJOR(interrupt_dev.devid);
}
if(ret < 0){
printk("chrdev err\r\n");
goto chrdev_err;
}
printk("major num:%#x\r\n",interrupt_dev.major);
interrupt_dev.cdev.owner = THIS_MODULE;
cdev_init(&interrupt_dev.cdev,&interrupt_fops);
ret = cdev_add(&interrupt_dev.cdev,interrupt_dev.devid,INTERRUPT_CT);
if(ret < 0){
printk("cdev err\r\n");
goto cdev_err;
}
interrupt_dev.class = class_create(THIS_MODULE,INTERRUPT_NAME);
if(IS_ERR(interrupt_dev.class)){
printk("class err\r\n");
ret = PTR_ERR(interrupt_dev.class);
goto class_err;
}
interrupt_dev.device = device_create(interrupt_dev.class,NULL,interrupt_dev.devid,NULL,INTERRUPT_NAME);
if(IS_ERR(interrupt_dev.device)){
printk("device err\r\n");
ret = PTR_ERR(interrupt_dev.device);
goto device_err;
}
return 0;
device_err:
class_destroy(interrupt_dev.class);
class_err:
cdev_del(&interrupt_dev.cdev);
cdev_err:
unregister_chrdev_region(interrupt_dev.devid,INTERRUPT_CT);
chrdev_err:
return ret;
}
static void __exit interrupt_exit(void)
{
del_timer_sync(&interrupt_dev.timer);
free_irq(irq_desc.irq_num, &interrupt_dev);
gpio_free(interrupt_dev.gpio_num);
device_destroy(interrupt_dev.class,interrupt_dev.devid);
class_destroy(interrupt_dev.class);
cdev_del(&interrupt_dev.cdev);
unregister_chrdev_region(interrupt_dev.devid,INTERRUPT_CT);
}
module_init(interrup_init);
module_exit(interrupt_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gale");
在之前gpio处理那一块,将gpio设置为输入,并且通过 irq_of_parse_and_map() 该函数获取中断号,之后就可以使用 request_irq() 进行申请中断,对应的释放该中断使用 free_irq() 该函数。
通过一个释放标志和按键值,在应用读取时候通过释放标志来确定是否返回有效值。
#include
#include
#include
#include
#include
#include
#include "stdlib.h"
#include
int main(int argc,char *argv[])
{
int ret = 0;
int cmd;
int arg;
int fd;
char *filename;
char readbuff[1];
unsigned char str[100];
if(argc != 2)
{
printf("Error usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("Can't open file %s\r\n", filename);
return -1;
}
while(1){
ret = read(fd,readbuff,1);
if(ret < 0){
}
else {
printf("key value:%d\r\n",readbuff[0]);
}
}
ret = close(fd);
if(ret < 0){
printf("close file err:%s\r\n",filename);
}
else{
}
return 0;
}
应用代码很简单,只是读取判断是否是有效值,否则就不是按键按下,之后将按键值打印出来即可。
KERNELDIR := /home/gale/linux/linux/nxp_alientek_linux
CURRENT_PATH := $(shell pwd)
obj-m := interrupt.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
Makefile基本上不变,除了依赖。
其实在这里应用程序采用不断的读取方式,好像有点浪费资源,看下一篇能否使用阻塞方式来读取按键的值。