Linux 中断实验

该文详细介绍了Linux内核中断的概念,包括上半部和下半部处理,然后讲解了如何通过设备树添加按键硬件,编写中断驱动,特别是按键中断处理函数的编写和初始化。此外,文中还展示了如何利用定时器进行按键消抖处理,以提高中断处理的准确性。最后,给出了一个简单的测试应用程序来验证驱动的功能。
摘要由CSDN通过智能技术生成

目录

一、Linux 中断简介

上半部与下半部

二、添加设备树

三、编写驱动

1、定义宏

 2、编写一个key结构体

3、imx6uirq设备添加成员 

​编辑4、按键中断处理函数

5、按键初始化

6、在驱动入口添加初始化

7、 驱动出口函数

 代码如下

四、利用定时器进行消抖处理

1、添加定时器

 2、修改中断处理函数

3、添加定时器处理函数

4、在按键初始化函数最后添加初始化定时器

 5、驱动出口函数添加删除定时器

五、编写测试app

1、添加原子操作

2、初始化原子变量

3、按下和释放操作

4、读函数操作

总体代码如下


一、Linux 中断简介

在 Linux 内核中要想使用某个中断是需要申请的,使用完成以后就要释放掉相应的中断,申请中断的时候需要设置中断处理函数,还有设置使能中断与禁止

上半部与下半部

有些资料中也将上半部和下半部称为顶半部和底半部。

上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可以放在上半部完成。
下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行,这样中断处理函数就会快进快出。
至于哪些代码属于上半部,哪些代码属于下半部并没有明确的规定,如果要处理的内容不希望被其他中断打断、处理的任务对时间敏感或处理的任务与硬件有关,那么可以放到上半部,其余以外的其他任务,优先考虑放到下半部

二、添加设备树

打开原理图找到KEY

 可以看到KEY0是接到UART1 CTS上

打开参考手册,找到IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B

看到是可以复用为GPIO1_IO18的

根据之前key驱动实验编写的key节点,添加 184-185两行

184行, 指定父中断,也就是中断控制器,因为key复用为GPIO1_IO18,所以为gpio1

185行,设置中断源,指定中断号,触发方式。因为复用GPIO1_IO18,所以为18,

        IRQ_TYPE_EDGE_BOTH 定义在文件 include/linux/irq.h 中

 IRQ_TYPE_EDGE_BOTH 表示上升沿和下降沿同时有效,相当于KEY0 按下和释放都会触发中断

添加完成之后编译设备树,用此设备树启动

三、编写驱动

导入模板,修改makefile

1、定义宏

 2、编写一个key结构体

irq_desc为中断描述符,这里是key,所以用irq_keydesc 

3、imx6uirq设备添加成员 

在imx6uirq设备中使用,用数组的形式主要是方便添加多个按键

4、按键中断处理函数

实现key结构体中的按键中断处理函数

71行,中断处理函数格式如下

irqreturn_t (*irq_handler_t) (int, void *)

 第一个参数是要中断处理函数要相应的中断号。第二个参数是一个指向 void 的指针,也就是个通用指针,用于区分共享中断的不同设备,也可以指向设备数据结构。中断处理函数的返回值为 irqreturn_t 类型。

73行,dev_id指向dev的设备,也就是imx6uirq

74行,获取gpio的值

5、按键初始化

 

 88行,通过路径查找设备树节点

93-94行,通过循环来进行获取关于节点属性

100-103行,通过循环初始化名字数组;sprintf()用来作格式化的输出;申请使用gpio

109行,设置gpio输出

111行和113行作用一样,都是获取中断号,前者只能利用gpio获取,后者利用通过属性索引获取

116行,设置key0的中断处理函数

117行,设置key0的按键值

120行,在 Linux 内核中要想使用某个中断是需要申请的, request_irq 函数用于申请中断,request_irq函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用 request_irq 函数。 request_irq 函数会激活(使能)中断,所以不需要我们手动去使能中断, request_irq 函数原型如下:

int request_irq(unsigned int irq
                        irq_handler_t handler,
                        unsigned long flags,
                        const char * name,
                        void * dev):

irq:要申请中断的中断号。
handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志。这里使用上升沿和下降沿触发

name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
dev: 如果将 flags 设置为 IRQF_SHARED 的话, dev 用来区分不同的中断,一般情况下将dev 设置为设备结构体, dev 会传递给中断处理函数 irq_handler_t 的第二个参数。
返回值: 0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经
被申请了
 

6、在驱动入口添加初始化

7、 驱动出口函数

 代码如下

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>

#define IMX6UIRQ_CNT 1
#define IMX6UIRQ_NAME "imx6uirq"
#define KEY_NUM 1       /* 按键数量 	*/
#define KEY0VALUE 0X01  /* KEY0按键值 	*/
#define INVAKEY 0XFF    /* 无效的按键值 */

/*key结构体*/
struct irq_keydesc{
    int gpio;  /*io编号*/
    int irqnum; /*中断号*/
    unsigned char value; /*键值*/
    char name[10]; /*名字*/
    irqreturn_t(*handler) (int ,void *); /*中断处理函数*/
};

struct imx6uirq_dev{
    dev_t devid;
    int major;
    int minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    struct irq_keydesc irqkey[KEY_NUM];
}imx6uirq;
static int imx6uirq_open(struct inode *inode, struct file *filp){
    filp->private_data = &imx6uirq;
    return 0;
}
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}
static ssize_t imx6uirq_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}
static int imx6uirq_release(struct inode *inode, struct file *filp){
     return 0;
}

static const  struct file_operations imx6uirq_fops = {
    .owner	=   THIS_MODULE,
    .open   =   imx6uirq_open,
    .read   =   imx6uirq_read,
    .write  =   imx6uirq_write,
    .release =  imx6uirq_release,
};
/*按键中段处理函数*/
static irqreturn_t key0_handler(int irq,void *dev_id){
    int value = 0;
    struct imx6uirq_dev * dev = dev_id;
    value = gpio_get_value(dev->irqkey[0].gpio);
    if(value == 0){
        /*按下*/
    printk("KEY0 push\r\n");
    }else if(value == 1){
        /*释放*/
        printk("KEY0 release\r\n");
    }
    return IRQ_HANDLED;
}
/*按鍵初始化*/
static int keyio_init(struct imx6uirq_dev *dev){
    int i,ret =0 ;
    /*按鍵初始化*/
    dev->nd = of_find_node_by_path("/key");
    if(dev->nd == NULL){
        ret = -EINVAL;
        goto fail_nd;
    }
    for(i=0;i<KEY_NUM;i++){
        dev->irqkey[i].gpio = of_get_named_gpio(dev->nd,"key-gpios",i);
        if(dev->irqkey[i].gpio < 0){
            ret = -EINVAL;
            goto fail_gpio;
        }
    }
    for(i=0;i<KEY_NUM;i++){
        memset(dev->irqkey[i].name,0,sizeof(dev->irqkey[i].name));
        sprintf(dev->irqkey[i].name,"KEY%d",i);
        ret = gpio_request(dev->irqkey[i].gpio , dev->irqkey[i].name);
        if(ret){
            ret = -EBUSY;
            printk("IO %d can't request\r\n",dev->irqkey[i].gpio);
            goto fail_request;
        }
        gpio_direction_input(dev->irqkey[i].gpio);
        /*获取中断号*/
        dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);
#if 0
        dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd,i);
 #endif
    }
    dev->irqkey[0].handler = key0_handler;
    dev->irqkey[0].value   = KEY0VALUE;
        /*按键中断初始化*/
    for(i=0;i<KEY_NUM;i++){
        ret = request_irq(dev->irqkey[i].irqnum,dev->irqkey[i].handler,
                            IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING,
                            dev->irqkey[i].name,&imx6uirq);
        if(ret){
            printk("irq %d request failed!\r\n",dev->irqkey[i].irqnum);
            goto fail_irq;
        }
    }
    return 0;
fail_irq:
    for(i=0;i<KEY_NUM;i++){
        gpio_free(dev->irqkey[i].gpio);
    }
fail_request:
fail_gpio:
fail_nd:
    return ret;
}

static int __init imx6uirq_init(void){
    int ret = 0;
    imx6uirq.major = 0;
    if(imx6uirq.major){
        imx6uirq.devid =MKDEV(imx6uirq.major,0);
        ret = register_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT,IMX6UIRQ_NAME);
        if(ret < 0){
            goto fail_devid;
        }
    }else{
        ret = alloc_chrdev_region(&imx6uirq.devid,0,IMX6UIRQ_CNT,IMX6UIRQ_NAME);
        if(ret < 0){
            goto fail_devid;
        }
        imx6uirq.major = MAJOR(imx6uirq.devid);
        imx6uirq.minor = MINOR(imx6uirq.devid);
        printk("imx6uirq major = %d, minor = %d\r\n", imx6uirq.major, imx6uirq.minor);
    }
    imx6uirq.cdev.owner = THIS_MODULE;
    cdev_init(&imx6uirq.cdev, &imx6uirq_fops);
    ret = cdev_add(&imx6uirq.cdev,imx6uirq.devid,IMX6UIRQ_CNT);
    if(ret){
        goto fail_cdevadd;
    }
    imx6uirq.class = class_create(THIS_MODULE,IMX6UIRQ_NAME);
    if(IS_ERR(imx6uirq.class)){
        ret = PTR_ERR(imx6uirq.class);
        goto fail_class;
    }
    imx6uirq.device = device_create(imx6uirq.class,NULL,imx6uirq.devid,NULL,IMX6UIRQ_NAME);
    if(IS_ERR(imx6uirq.device)){
        ret = PTR_ERR(imx6uirq.device);
        goto fail_device;
    }

    /*初始化IO*/
    ret = keyio_init(&imx6uirq);
    if(ret < 0){
        goto fail_keyinit;
    }

    return 0;
fail_keyinit:
    device_destroy(imx6uirq.class,IMX6UIRQ_CNT);
fail_device:
    class_destroy(imx6uirq.class);
fail_class:
    cdev_del(&imx6uirq.cdev);
fail_cdevadd:
    unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);
fail_devid:
    return ret;
}
static void __exit imx6uirq_exit(void){
    int i=0;
    /*释放中断*/
    for(i=0;i<KEY_NUM;i++){
        free_irq(imx6uirq.irqkey[i].irqnum,&imx6uirq);
    }
    /*释放IO*/
    for(i=0;i<KEY_NUM;i++){
        gpio_free(imx6uirq.irqkey[i].gpio);
    }
    device_destroy(imx6uirq.class,imx6uirq.devid);
    class_destroy(imx6uirq.class);
    cdev_del(&imx6uirq.cdev);
    unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);
    printk("imx6uirq_exit\r\n");
}

module_init(imx6uirq_init);
module_exit(imx6uirq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ba che kai qi lai");

编译到开发板上,按下按键0,就会打印相关内容,释放也会,注意此处没做消抖

四、利用定时器进行消抖处理

1、添加定时器

 2、修改中断处理函数

 在进入到中断处理后,定时器处理函数会周期延时20毫秒作消抖处理

3、添加定时器处理函数

消抖后判断是否按下按键

4、在按键初始化函数最后添加初始化定时器

 5、驱动出口函数添加删除定时器

加载驱动进行测试之后,按键按下和释放都会相对准确,在按下和释放速度过快就会打印相同信息的情况

五、编写测试app

1、添加原子操作

2、初始化原子变量

3、按下和释放操作

4、读函数操作

总体代码如下

驱动

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>

#define IMX6UIRQ_CNT 1
#define IMX6UIRQ_NAME "imx6uirq"
#define KEY_NUM 1       /* 按键数量 	*/
#define KEY0VALUE 0X01  /* KEY0按键值 	*/
#define INVAKEY 0XFF    /* 无效的按键值 */

/*key结构体*/
struct irq_keydesc{
    int gpio;  /*io编号*/
    int irqnum; /*中断号*/
    unsigned char value; /*键值*/
    char name[10]; /*名字*/
    irqreturn_t(*handler) (int ,void *); /*中断处理函数*/
};

struct imx6uirq_dev{
    dev_t devid;
    int major;
    int minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    struct irq_keydesc irqkey[KEY_NUM];
    struct timer_list timer;
    atomic_t keyvalue;
    atomic_t releasekey;
}imx6uirq;
static int imx6uirq_open(struct inode *inode, struct file *filp){
    filp->private_data = &imx6uirq;
    return 0;
}
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    unsigned char keyvalue;
    unsigned char releasekey;
    struct imx6uirq_dev *dev = filp->private_data;
    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);
    if(releasekey){/*有效按键*/
        if(keyvalue & 0x80){
            keyvalue &= ~0x80;
            ret=__copy_to_user(buf,&keyvalue,sizeof(keyvalue));
        }else{
            goto data_error;
        }
        atomic_set(&dev->releasekey,0);/*按下标志清零*/
    }else{
         goto data_error;
    }
    return ret;
data_error:
    return -EINVAL;
}
static ssize_t imx6uirq_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}
static int imx6uirq_release(struct inode *inode, struct file *filp){
     return 0;
}

static const  struct file_operations imx6uirq_fops = {
    .owner	=   THIS_MODULE,
    .open   =   imx6uirq_open,
    .read   =   imx6uirq_read,
    .write  =   imx6uirq_write,
    .release =  imx6uirq_release,
};
/*按键中断处理函数*/
static irqreturn_t key0_handler(int irq,void *dev_id){
    
    struct imx6uirq_dev * dev = dev_id;
    dev->timer.data = (volatile unsigned long)dev_id;
    mod_timer(&dev->timer,jiffies + msecs_to_jiffies(20));/*10ms*/

    return IRQ_HANDLED;
}
/*定时器处理函数*/
static void timer_func(unsigned long arg){
    int value = 0;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;
    value = gpio_get_value(dev->irqkey[0].gpio);
    if(value == 0){
        /*按下*/
        atomic_set(&dev->keyvalue,dev->irqkey[0].value);

    }else if(value == 1){
        /*释放*/
        atomic_set(&dev->keyvalue,0x80 | (dev->irqkey[0].value));
        atomic_set(&dev->releasekey,1);
    }

}
/*按鍵初始化*/
static int keyio_init(struct imx6uirq_dev *dev){
    int i,ret =0 ;
    /*按鍵初始化*/
    dev->nd = of_find_node_by_path("/key");
    if(dev->nd == NULL){
        ret = -EINVAL;
        goto fail_nd;
    }
    for(i=0;i<KEY_NUM;i++){
        dev->irqkey[i].gpio = of_get_named_gpio(dev->nd,"key-gpios",i);
        if(dev->irqkey[i].gpio < 0){
            ret = -EINVAL;
            goto fail_gpio;
        }
    }
    for(i=0;i<KEY_NUM;i++){
        memset(dev->irqkey[i].name,0,sizeof(dev->irqkey[i].name));
        sprintf(dev->irqkey[i].name,"KEY%d",i);
        ret = gpio_request(dev->irqkey[i].gpio , dev->irqkey[i].name);
        if(ret){
            ret = -EBUSY;
            printk("IO %d can't request\r\n",dev->irqkey[i].gpio);
            goto fail_request;
        }
        gpio_direction_input(dev->irqkey[i].gpio);
        /*获取中断号*/
        dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);
#if 0
        dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd,i);
 #endif
    }
    dev->irqkey[0].handler = key0_handler;
    dev->irqkey[0].value   = KEY0VALUE;
        /*按键中断初始化*/
    for(i=0;i<KEY_NUM;i++){
        ret = request_irq(dev->irqkey[i].irqnum,dev->irqkey[i].handler,
                            IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING,
                            dev->irqkey[i].name,&imx6uirq);
        if(ret){
            printk("irq %d request failed!\r\n",dev->irqkey[i].irqnum);
            goto fail_irq;
        }
    }
    /*初始化定时器*/
    init_timer(&imx6uirq.timer);
    imx6uirq.timer.function = timer_func;

    return 0;
fail_irq:
    for(i=0;i<KEY_NUM;i++){
        gpio_free(dev->irqkey[i].gpio);
    }
fail_request:
fail_gpio:
fail_nd:
    return ret;
}

static int __init imx6uirq_init(void){
    int ret = 0;
    imx6uirq.major = 0;
    if(imx6uirq.major){
        imx6uirq.devid =MKDEV(imx6uirq.major,0);
        ret = register_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT,IMX6UIRQ_NAME);
        if(ret < 0){
            goto fail_devid;
        }
    }else{
        ret = alloc_chrdev_region(&imx6uirq.devid,0,IMX6UIRQ_CNT,IMX6UIRQ_NAME);
        if(ret < 0){
            goto fail_devid;
        }
        imx6uirq.major = MAJOR(imx6uirq.devid);
        imx6uirq.minor = MINOR(imx6uirq.devid);
        printk("imx6uirq major = %d, minor = %d\r\n", imx6uirq.major, imx6uirq.minor);
    }
    imx6uirq.cdev.owner = THIS_MODULE;
    cdev_init(&imx6uirq.cdev, &imx6uirq_fops);
    ret = cdev_add(&imx6uirq.cdev,imx6uirq.devid,IMX6UIRQ_CNT);
    if(ret){
        goto fail_cdevadd;
    }
    imx6uirq.class = class_create(THIS_MODULE,IMX6UIRQ_NAME);
    if(IS_ERR(imx6uirq.class)){
        ret = PTR_ERR(imx6uirq.class);
        goto fail_class;
    }
    imx6uirq.device = device_create(imx6uirq.class,NULL,imx6uirq.devid,NULL,IMX6UIRQ_NAME);
    if(IS_ERR(imx6uirq.device)){
        ret = PTR_ERR(imx6uirq.device);
        goto fail_device;
    }

    /*初始化IO*/
    ret = keyio_init(&imx6uirq);
    if(ret < 0){
        goto fail_keyinit;
    }
    /*初始化原子变量*/
    atomic_set(&imx6uirq.keyvalue,INVAKEY);
    atomic_set(&imx6uirq.releasekey,0);
    return 0;
fail_keyinit:
    device_destroy(imx6uirq.class,IMX6UIRQ_CNT);
fail_device:
    class_destroy(imx6uirq.class);
fail_class:
    cdev_del(&imx6uirq.cdev);
fail_cdevadd:
    unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);
fail_devid:
    return ret;
}
static void __exit imx6uirq_exit(void){
    int i=0;
    /*释放中断*/
    for(i=0;i<KEY_NUM;i++){
        free_irq(imx6uirq.irqkey[i].irqnum,&imx6uirq);
    }
    /*释放IO*/
    for(i=0;i<KEY_NUM;i++){
        gpio_free(imx6uirq.irqkey[i].gpio);
    }
    del_timer_sync(&imx6uirq.timer);
    device_destroy(imx6uirq.class,imx6uirq.devid);
    class_destroy(imx6uirq.class);
    cdev_del(&imx6uirq.cdev);
    unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);
    printk("imx6uirq_exit\r\n");
}

module_init(imx6uirq_init);
module_exit(imx6uirq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ba che kai qi lai");

APP

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


#define CLOSE_CMD _IO(0XEF ,1)  /*关闭命令*/
#define OPEN_CMD _IO(0XEF,2)    /*打开命令*/
#define SETPERIOD_CMD _IO(0XEF,3) /*设置周期*/


int main(int argc, char *argv[])
{
    int fd,ret;
    char *filename;
    unsigned char data;
    
    /*判断命令行输入参数是否正确*/
    if(argc != 2){
        printf("error usage!\r\n");
        return -1;
    }
    /*用指针指向文件*/
    filename = argv[1];
    /*打开文件*/
    fd = open(filename , O_RDWR);
    if(fd < 0){
        printf("file open failed\r\n",filename);
        return -1;
    }
    /*循环读取*/
    while(1){
        ret = read(fd,&data,sizeof(data));
        if(ret<0){

        }else{
            if(data)
                printf(" key vaule = %d\r\n",data);
        }
    }
   
    /*关闭文件*/
    close(fd);

    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值