嵌入式linux 中断

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

        2,在linux内核中我们想使用某个中断是需要申请的,request_irq函数用于申请中断,request_irq函数可能会导致睡眠,所以办呢用在禁止睡眠的代码段中,request_irq函数会激活(使能)中断,不需要我们使能了

        irq:要申请的中断号

简单总结一下中断有关的设备树信息
1,#interrupt-cells,指定中断源cells个数
2,interrupt-controller,表示当前节点为中断控制器
3,interrupts,指定中断号,触发方式等
4,interrupt-parent指定父中断,也就是中断控制器

//设备树中需要配置中断源信息  interrupts = <中断类型,中断号,标志>
//中断类型分为SPI中断(0~987)和PPI中断(0~15)
//需要使用那个中断,设置为那个中断号
//上身沿触发(1)下降沿触发(2)高电平触发(4)低电平触发(8)

irqnum = irq_of_parse_and_map(imx6uirq.nd, index);    //index(索引号)可能有多个中断信息
irqnum = gpio_to_irq(imx6uirq..gpio);                 //GPIO引脚也可以用这个函数获得中断号

        handler:中断处理函数,中断发生以后会执行此中断处理函数

static irqreturn_t key0_handler(int irq, void *dev_id)

       返回值类型(irqreturn_t)定义如下

         flags:中断标志,下面的图就是几个常用的中断标志

        dev:传递给中断处理函数的参数(通常为对应设备的结构体指针),中断处理函数的第二个参数

//申请中断
request_irq(imx6uirq.irqnum, imx6uirq.handler, 
		              IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, imx6uirq.name,&imx6uirq);

//释放中断
free_irq(imx6uirq.irqnum,&imx6uirq);

        3,中断使能与中断禁止

        下面的disable_irq函数要等到当前正在处理的中断处理函数执行完才返回,因此使用者保证不会产生新的中断,并且确保所有已经开始执行的中断处理程序已经全部退出。

void enable_irq(unsigned int irq)
void disable_irq(unsigned int irq)    //irq(中断号)

        下面的disable_irq_nosync函数调用后立即返回,不会等待当前中断处理程序执行完毕。

void disable_irq_nosync(unsigned int irq)

        上面的三个中断处理函数都是使能或禁止某一个中断,当我们需要关闭当前处理器的整个中断系统,可以使用下面的函数。 

local_irq_enable()
local_irq_disable()

        上面的中断处理函数,当任务A想要关闭一段时间的中断,但是任务B想要关闭的时间短点,然后打开终端,也就会导致任务A关闭的时间不够,就会出现一系列的问题,为了解决上面的问题,可以使用下面的函数,flags会将中断状态保存下来。

local_irq_save(flags)
local_irq_restore(flags)

上半部与下半部

        上半部:上半部就是中断处理函数,那些处理比较快,不会占用很长时间的处理就可以放在上半部完成

        下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去处理,这样中断处理函数就会快进快出。

tasklet

        tasklet是利用软中断来实现的另一种下半部机制,在软中断(可以不使用)和tasklet之间,我们选择tasklet。

         在使用tasklet之前,我们需要定义一下结构体,在驱动入口函数里面将其初始化,初始化需要配置tasklet处理函数。我们本身想要把中断里面的处理过程搬运出来,放在这个处理函数里面就可以,那中断怎么知道我们把处理过程调度到tasklet处理函数里面呢?所以我们需要在中断里面调用tasklet_schedule 函数就能使 tasklet 在合适的时间运行。

/* 定义 taselet */
struct tasklet_struct testtasklet;


/* tasklet 处理函数 */
void testtasklet_func(unsigned long data)
{
     /* tasklet 具体处理内容 */
}


/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id) {
 ......
 /* 调度 tasklet */
 tasklet_schedule(&testtasklet);
 ......
}


/* 驱动入口函数 */
static int __init xxxx_init(void) 
{
 ......
 /* 初始化 tasklet */
tasklet_init(&testtasklet, testtasklet_func, data);

}

工作队列

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

         在使用工作队列之前,我们需要定义一下结构体,在驱动入口函数里面将其初始化,初始化需要配置工作队列处理函数。我们本身想要把中断里面的处理过程搬运出来,放在这个处理函数里面就可以,那中断怎么知道我们把处理过程调度到工作队列处理函数里面呢?所以我们需要在中断里面调用schedule_worker 函数就能使 工作队列在合适的时间运行。

/* 定义工作(work) */
struct work_struct testwork;


/* work 处理函数 */
void testwork_func_t(struct work_struct *work)
{
     /* tasklet 具体处理内容 */
}


/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id) {
 ......
 /* 调度 work */
 schedule_work(&testwork);
 ......
}


/* 驱动入口函数 */
static int __init xxxx_init(void) 
{
 ......
 /* 初始化 work */
 INIT_WORK(&testwork, testwork_func_t);

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值