嵌入式LINUX驱动学习之7中断相关(二)底半部机制之tasklet_struct

嵌入式LINUX驱动学习之7中断相关(二)底半部机制之tasklet_struct

一、头文件、函数及说明

//源码位置:include/linux/interrupt.h
struct tasklet_struct
{
    
    
        struct tasklet_struct *next;
        unsigned long state;
        atomic_t count;
        void (*func)(unsigned long);
        unsigned long data;
};
//可以用下面的宏初始化上面的结构体;
//注:data 需要是全局静态变量或静态变量
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
//也可以这样初始化结构体:
struct tasklet_struct tasklet_init_s = {
    
    
    .func = 延迟处理函数,
    .data = 需要处理的数据
}
/*配套函数:tasklet_schedule()
一般有顶半部,tasklet_schedule函数就在顶半部调用,
没有顶半部,就在全局或初始化位置或其它合适的函数中调用都可以,
tasklet_schedule函数只起到登记的作用,即:
这个函数的形参对象可以延迟执行,而形参对象里的的函数才是
最终可执行的实例,也就是tasklet_struct结构体里的成员函数
可以延迟执行
*/
static inline void tasklet_schedule(struct tasklet_struct *t)
{
    
    
        if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
                __tasklet_schedule(t);
}

二、代码举例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
struct btn_src {
    
    
    char *name;
    int   gpio;
};
struct btn_src btn_info[] = {
    
    
    {
    
    
        .name = "LED1_BTN",
        .gpio = PAD_GPIO_A +28
    },
    {
    
    
        .name = "LED2_BTN",
        .gpio = PAD_GPIO_B +30
    },
    {
    
    
        .name = "LED3_BTN",
        .gpio = PAD_GPIO_B +31
    },
    {
    
    
        .name = "LED4_BTN",
        .gpio = PAD_GPIO_B +9
    }
};
struct led_src {
    
    
    char *name;
    int  gpio ;
};
struct led_src led_info[] = {
    
    
    {
    
    
        .name = "LED1",
        .gpio = PAD_GPIO_C +12,
    },
    {
    
    
        .name = "LED2",
        .gpio = PAD_GPIO_C +11,
    },
    {
    
    
        .name = "LED3",
        .gpio = PAD_GPIO_C +7,
    },
    {
    
    
        .name = "LED4",
        .gpio = PAD_GPIO_B +26,
    },
};
struct led_src *g_led_irq;
unsigned long g_data = 255;
/*底半部,处理不重要,耗时较长的任务*/
void tasklet_btn_led_func (unsigned long data){
    
    
    printk("%s,%s状态为:%s,GPIO编号:%lu\n",__func__ , g_led_irq -> name ,\
            gpio_get_value(g_led_irq->gpio) ? "关" : "开", \
            g_data & g_led_irq -> gpio);
}
struct tasklet_struct tasklet_btn_led = {
    
    
    .func = tasklet_btn_led_func,//设置延迟处理函数
    .data = &g_data // 向延迟处理函数传递的数据
};
//也可用下面的宏初化:
//DECLARE_TASKLET(tasklet_btn_led,tasklet_btn_led_func,&g_data);

//顶半部机制,处理紧急、重要的任务,
static irqreturn_t irq_func(int irq,void * argv){
    
    
    struct led_src *led_s = (struct led_src *) argv;
     //调用 tasklet_schedule函数,表示tasklet_btn_led对象可以延迟执行
    tasklet_schedule(&tasklet_btn_led);
    gpio_set_value(led_s->gpio,(1 - gpio_get_value(led_s -> gpio)));
    g_led_irq = argv;
    return IRQ_HANDLED;
}

static int btn_led_init(void){
    
    
    int i =0;
    for(; i < ARRAY_SIZE(btn_info);i++){
    
    
        //按键申请GPIO资源
        gpio_request(btn_info[i].gpio,btn_info[i].name);
        //设置GPIO口为输入
        gpio_direction_input(btn_info[i].gpio);
        //申请中断号
        request_irq(gpio_to_irq(btn_info[i].gpio),\
                     irq_func,IRQF_TRIGGER_LOW,
                     btn_info[i].name,&led_info[i]);
        //LED灯申请中断资源
        gpio_request(led_info[i].gpio,led_info[i].name);
        //设置GPIO口为输出,高电平,即关灯
        gpio_direction_output(led_info[i].gpio,1);
    }
    return 0;
}
static void btn_led_exit(void){
    
    
    int i = 0;
    for(;i < ARRAY_SIZE(btn_info);i++){
    
    
        //释放中断号
        free_irq(gpio_to_irq(btn_info[i].gpio),&led_info[i]);
        //释放按键GPIO资源 
        gpio_free(btn_info[i].gpio);
        //设置LED灯GPIO口为高电平 
        gpio_set_value(led_info[i].gpio,1);
        //释放LED灯的GPIO资源
        gpio_free(led_info[i].gpio);
    }
}

module_init(btn_led_init);
module_exit(btn_led_exit);
MODULE_LICENSE("GPL");

猜你喜欢

转载自blog.csdn.net/weixin_47273317/article/details/107940432