(八)linux下外设中断的使用(基于按键触发)

一、linux下的外设中断使用

1.1、嵌入式开发版

使用的是正点原子linux开发版

1.2、思路

主要是结合之前的定时器来处理按键中断
先是通过按键触发按键中断,之后在按键中断里面触发10ms的定时器中断,然后等待定时器的10ms在接着去处理。
这里借用上半部和下半部处理,在中断里面我们要求使用快进快出,所以这是上半部,下半部则是我们处理相关的地方。

1.3、设备树的修改

主要是在设备树节点中增加按键节点。

	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,
	……
}

1.4、整体驱动代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/atomic.h>

#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() 该函数。

通过一个释放标志和按键值,在应用读取时候通过释放标志来确定是否返回有效值。

1.5、应用代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "stdlib.h"
#include <linux/ioctl.h>


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;
}

应用代码很简单,只是读取判断是否是有效值,否则就不是按键按下,之后将按键值打印出来即可。

1.6、Makefile

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基本上不变,除了依赖。

1.7、最终结果

在这里插入图片描述

二、个人理解

其实在这里应用程序采用不断的读取方式,好像有点浪费资源,看下一篇能否使用阻塞方式来读取按键的值。

发布了29 篇原创文章 · 获赞 0 · 访问量 426

猜你喜欢

转载自blog.csdn.net/weixin_42547950/article/details/104270960