linux 中断

1.Linux 内核提供了完善的中断框架,我们只需要申请中断,然后注册中断处理函数即可。

2.Linux 中断 API 函数:

1)中断号
每个中断都有一个中断号,通过中断号即可区分不同的中断,有的资料也把中断号叫做中断线。在 Linux 内核中使用一个 int 变量表示中断号。

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

比如 I.MX6ULL开发板上的 KEY0 使用 GPIO1_IO18,按下 KEY0 以后为低电平,因此可以设置为下降沿触发,也就是将 flags 设置为 IRQF_TRIGGER_FALLING。表 51.1.1.1 中的这些标志可以通过“|”来实现多种组合。

3) free_irq 函数
使用中断的时候需要通过 request_irq 函数申请,使用完成以后就要通过 free_irq 函数释放掉相应的中断。如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断。

free_irq函数原型如下所示:
void free_irq(unsigned int irq,   //要释放的中断
                     void *dev)            //如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉。

4)  中断处理函数
使用 request_irq 函数申请中断的时候需要设置中断处理函数,

中断处理函数格式如下所示:
irqreturn_t (*irq_handler_t) (int, void *)

第一个参数是要中断处理函数要相应的中断号。

第二个参数是一个指向 void 的指针,也就是个通用指针,需要与 request_irq 函数的 dev 参数保持一致。用于区分共享中断的不同设备,dev 也可以指向设备数据结构。

中断处理函数的返回值为 irqreturn_t 类型。

5)中断使能与禁止函数
常用的中断使用和禁止函数如下所示:
void enable_irq(unsigned int irq)   //使能指定的中断
void disable_irq(unsigned int irq)  //禁止指定的中断
 irq 就是要禁止的中断号。 disable_irq函数要等到当前正在执行的中断处理函数执行完才返回,因此使用者需要保证不会产生新的中断,并且确保所有已经开始执行的中断处理程序已经全部退出。

在这种情况下,可以使用另外一个中断禁止函数:
void disable_irq_nosync(unsigned int irq)  //函数调用以后立即返回,不会等待当前中断处理程序执行完毕。

3.上半部与下半部

上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可以放在上半部完成。
下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行,这样中断处理函数就会快进快出。

4.上半部与下半部中断的选择:

参考点:
①、如果要处理的内容不希望被其他中断打断,那么可以放到上半部。
②、如果要处理的任务对时间敏感,可以放到上半部。
③、如果要处理的任务与硬件有关,可以放到上半部
④、除了上述三点以外的其他任务,优先考虑放到下半部。
上半部处理很简单,直接编写中断处理函数就行了。

5.Linux 内核提供了多种下半部机制,看下下半部机制。

5.1.1)Linux 内核使用结构体 softirq_action 表示软中断,

softirq_action结构体定义在文件 include/linux/interrupt.h 中,
内容如下:
struct softirq_action  //softirq_action 结构体
{
    void (*action)(struct softirq_action *);
};

5.1.2)在 kernel/softirq.c 文件中一共定义了 10 个软中断,如下所示:

static struct softirq_action softirq_vec[NR_SOFTIRQS]; //softirq_vec 数组

NR_SOFTIRQS 是枚举类型,定义在文件 include/linux/interrupt.h 中,定义如下:
softirq_vec 数组

enum
{
    HI_SOFTIRQ=0,    /* 高优先级软中断 */
    TIMER_SOFTIRQ,   /* 定时器软中断 */
    NET_TX_SOFTIRQ,  /* 网络数据发送软中断 */
    NET_RX_SOFTIRQ,  /* 网络数据接收软中断 */
    BLOCK_SOFTIRQ,
    BLOCK_IOPOLL_SOFTIRQ,
    TASKLET_SOFTIRQ,  /* tasklet 软中断 */
    SCHED_SOFTIRQ,    /* 调度软中断 */
    HRTIMER_SOFTIRQ,  /* 高精度定时器软中断 */
    RCU_SOFTIRQ,      /* RCU 软中断 */
    NR_SOFTIRQS
};

 5.1.3)要使用软中断,必须先使用 open_softirq 函数注册对应的软中断处理函数。

5.2  tasklet
tasklet 是利用软中断来实现的另外一种下半部机制,在软中断和 tasklet 之间,建议大家使用 tasklet。

5.3 工作队列
工作队列是另外一种下半部执行方式,工作队列在进程上下文执行,工作队列将要推后的工作交给一个内核线程去执行,因为工作队列工作在进程上下文,因此工作队列允许睡眠或重新调度。因此如果你要推后的工作可以睡眠那么就可以选择工作队列,否则的话就只能选择软中断或 tasklet。

6.中断有关的设备树属性信息:
①、 #interrupt-cells,      指定中断源的信息 cells 个数。
②、 interrupt-controller,表示当前节点为中断控制器。
③、 interrupts,              指定中断号,触发方式等。
④、 interrupt-parent,    指定父中断,也就是中断控制器。

7.硬件按键选择:

8.设备树节点设置

/{

    gpiokey_irq {
        compatible = "atkalpha-gpiokey";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_key_irq>;        /* pinctrl_key_irq:复用功能节点名字 */
        key-gpio = <&gpio4 14 GPIO_ACTIVE_LOW>;
        interrupt-parent = <&gpio4>;          /* 设置 KEY2 的 GPIO 中断控制器为 gpio4。 */
        interrupts = <14 IRQ_TYPE_EDGE_BOTH>; /* 中断标号: FALLING RISING (上升沿和下降沿同时有效)  */
        status = "okay";        
    };

}


&iomuxc {
  imx6ul-evk {
		/* w: key2_irq实验 */
		pinctrl_key_irq: keygrp {               
            fsl,pins = <
                MX6UL_PAD_NAND_CE1_B__GPIO4_IO14         0x000010B0 /* key2_irq */
            >;
        };
   };
}

9.包含头文件:#include <linux/interrupt.h>

10定时器初始化:

 ret = request_irq(gpio 对应的中断号,     

                              中断处理函数,                                                                                               

                              中断触发方式, 

                              中断处理函数参数);

11.中断处理函数实现:

static irqreturn_t key0_handler(int irq, void *dev_id)
{
    int value = 0;
	struct imx6uirq_dev *dev = dev_id;	
    dev->timer.data = (volatile long)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;
}

12.按键按下触发中断的效果:

 13.查看系统中中断内容有那些:cat /proc/interrupts

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值