Linux设备驱动之中断---tasklet使用示例

前几篇文章介绍了中断的原理、基本框架、上下部使用之工作队列、线程化中断,这篇文章介绍另一种方法—线程化中断 tasklet。 此方法运行在中断上下文,不可休眠,延时。

我使用的硬件是imx6q,原理都一样。原来的beep引脚是output模式,电路上把它改成了按键。

下面程序的文件名为interrupt_tasklet.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/gpio.h>

#include <asm/io.h>
#include <asm/irq.h>



#define IMX_GPIO_NR(bank, nr)               (((bank) - 1) * 32 + (nr)) //平台相关
#define CYNO_GPIO_BEEP_NUM                  IMX_GPIO_NR(6,10) //本程序使用的gpio口

//定义gpio引脚的结构体
static struct pin_desc{
    int irq;
    unsigned char *name;
    unsigned int pin;
};

//实例化一个具体的引脚
static struct pin_desc beep_desc = {
    0,
    "beep_num",
    CYNO_GPIO_BEEP_NUM
};

//生命tasklet触发函数,也就是中断下半部函数
void beep_tasklet_func(unsigned long data);

//生命一个tasklet,名字为beep_tasklet,并且关联触发函数
DECLARE_TASKLET(beep_tasklet, beep_tasklet_func, 0);

int flag = 0;

//中断下半部函数实现
void beep_tasklet_func(unsigned long data){

    flag++;

    printk(KERN_INFO "-------\n");

    if(flag >= 60){
        printk(KERN_INFO "%s : flag = %d\n", __func__, flag);
        flag = 0;
    }
}


/*
无论什么时候调用tasklet_schedule
一定是上半部代码执行结束,再执行下半部代码
*/
static irqreturn_t beep_interrupt_handler(int irq, void *dev_id)
{
    printk(KERN_INFO "%s : top tasklet func\n", __func__);
    tasklet_schedule(&beep_tasklet);    //触发下半部代码
    printk(KERN_INFO "%s : bottom tasklet func\n", __func__);
    return IRQ_HANDLED;
}

//模块加载执行
static int interrupt_tasklet_init(void)
{
    int ret;

    printk(KERN_INFO "%s\n", __func__);
    //申请gpio
    if(gpio_request(beep_desc.pin ,beep_desc.name)){
        printk(KERN_ERR "%s : request gpio %d error\n", __func__, beep_desc.pin);
        goto err_gpio_request;
    }
    //设置gpio方向为输入
    gpio_direction_input(beep_desc.pin);
    //动态获取irq端口号
    beep_desc.irq = gpio_to_irq(beep_desc.pin);
    printk(KERN_INFO "%s : the irq num is %d\n", __func__, beep_desc.irq);
    //申请中断,并设置触发方式为下降沿,设置中断处理函数(上半部)    
    ret = request_irq(beep_desc.irq, beep_interrupt_handler , IRQF_TRIGGER_FALLING , beep_desc.name , &beep_desc);
    if(ret){
        printk(KERN_ERR "%s : request_irq is error\n", __func__);
        goto err_request_irq;
    }

    printk("%s : init end\n", __func__);

    return 0;

//处理错误
err_request_irq:
    free_irq(beep_desc.irq, &beep_desc);

err_gpio_request:
    gpio_free(beep_desc.pin);
    return -1;
}

//驱动卸载执行
static void interrupt_tasklet_exit(void)
{
    printk("%s\n", __func__);
    free_irq(beep_desc.irq, &beep_desc);
    gpio_free(beep_desc.pin);
}

module_init(interrupt_tasklet_init);
module_exit(interrupt_tasklet_exit);

MODULE_AUTHOR("xiaolei");
MODULE_DESCRIPTION("interrupt tasklet use");
MODULE_LICENSE("GPL");

tasklet运行在终端上下文,不可延时、睡眠。 我个人的理解是如果中断出发后处理事情的时间比较短可以使用此方法。 如果时间比较长,建议使用线程化中断或者work_queue。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值