4.中断分层设计

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4.中断分层设计相关的知识,希望对你有一定的参考价值。

技术分享

技术分享

Linux是如何来处理中断嵌套的:

所谓的中断嵌套就是,当一种中断正在执行的时候,又产生了另外中断。可以是同类型的,也可以是不同类型的。

首先先来分析第一种类型:慢速中断:是指在进行中断处理的时候,中断的总开关是不关闭的。允许其他类型中断产生。

技术分享

快速中断:当中断产生的时候,控制位的IF为被置1,别的中断被禁止发生。这样就会产生我们不想看到的情况:中断丢失。

技术分享

 

这就是中断分层技术解决的问题:

技术分享

所谓的中断分层就是,把一个中断分为了上部分和下部分。上部分是硬件相关的,必须要执行的。下部分是检查,处理工作的。可以先不做。这样就缩短了中断工作的时间。尽量避免了中断丢失的问题。

技术分享

技术分享

技术分享

Work在工作队列里有多个,并不是所有的work都会得到工作,得有daemon线程来调度。

技术分享

 

 

 

 

 

 

技术分享

最重要的是func,一项工作就是一个函数的执行。就是func的执行。

技术分享

技术分享

 

 

 

 

 

Queue.c:

#include <linux/init.h>

#include <linux/module.h>

 

struct workqueue_struct *my_wq;

struct work_struct *work1;

struct work_struct *work2;

MODULE_LICENSE("GPL");

 

void work1_func(struct work_struct *work)

{

printk("this is work1->\n");    

}

void work2_func(struct work_struct *work)

{

printk("this is work2->\n");    

}

int init_que(void)

{    

//1. 创建工作队列

my_wq = create_workqueue("my_que");

      

//2. 创建工作

work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);

INIT_WORK(work1, work1_func);

 

//3. 挂载(提交)工作

queue_work(my_wq,work1);

 

//2. 创建工作

work2 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);

INIT_WORK(work2, work2_func);

 

//3. 挂载(提交)工作

queue_work(my_wq,work2);

      

return 0;

}

void clean_que()

{

    

}

module_init(init_que);

module_exit(clean_que);

这是视频的代码,在编译的时候会报错误:

error: implicit declaration of function ‘kmalloc‘

解决的方法就是加个头文件:<linux/slab.h>就可以了。编译通过:

生成queue.ko驱动文件。

make -C /home/samba/linux-ok6410 M=/home/module/queue modules ARCH=arm CROSS_COMPILE=arm-linux-

make[1]: Entering directory `/home/samba/linux-ok6410‘

CC [M] /home/module/queue/queue.o

/home/module/queue/queue.c:44: warning: function declaration isn‘t a prototype

Building modules, stage 2.

MODPOST 1 modules

CC /home/module/queue/queue.mod.o

LD [M] /home/module/queue/queue.ko

make[1]: Leaving directory `/home/samba/linux-ok6410‘

接下来是拷贝到开发板运行,看结果:

Insmod queue.ko输出:

This is work1->

This is work2->

 

 

 

但是在很多情况下,驱动并不需要自己建立工作队列,只需定义工作,然后提交工作到内核已经定义好的工作队列keventd_wq中。

1.提交工作到默认队列:schedule_work中。

就是修改init_que函数的代码为:

int init_que(void)

{    

//2. 创建工作

work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);

INIT_WORK(work1, work1_func);

 

//3. 挂载(提交)工作

schedule_work(work1);

 

//2. 创建工作

work2 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);

INIT_WORK(work2, work2_func);

 

//3. 挂载(提交)工作

schedule_work(work2);

      

return 0;

}

修改完后执行make,生成.ko文件,拷贝到开发板。输出同样的效果:

[[email protected]]# insmod queue1.ko

this is work1->

this is work2->

接下来是今天最重要的事情,就是把工作队列用到按键的驱动程序里面去,用到按键中断处理程序当中去。接下来就是对前面的驱动程序进行改造。把他改成分成的中断处理的方式:

Nnnkey.c:

#include <linux/module.h>        /* For module specific items */

#include <linux/fs.h>            /* For file operations */

#include <linux/ioport.h>        /* For io-port access */

#include <linux/io.h>            /* For inb/outb/... */

#include <linux/init.h>

#include <linux/miscdevice.h>

#include <linux/interrupt.h>

#include <linux/slab.h>

#define GPNCON 0x7f008830

struct work_struct *work1;

 

void work1_func(struct work_struct *work)

{

printk("<0>key down!\n");    

}

irqreturn_t key_int(int irq, void *dev_id)

{

    //1.检测是否发生了按键中断

    //2.清除已经发生的按键中断

//前面的都是硬件相关的工作,必须在中断里面执行

//下面是硬件无关的工作,我们把它提到中断以外的work1_func函数去处理。

    //3.打印按键值

    schedule_work(work1);

    return 0;

}

 

void key_hw_init()

{

    unsigned int *gpio_config;

    unsigned short data;

    gpio_config = ioremap(GPNCON,4);

    data = readw(gpio_config);

    data &= ~0b11;

    data |= 0b10;

    writew(data,gpio_config);

}

int key_open(struct inode *node, struct file *filp)

{

    return 0;

}

struct file_operations key_fops =

{

    .open = key_open,

};

struct miscdevice key_miscdevice =

{

    .minor = 200,

    .name = "OK6410key",

    .fops = &key_fops,

};

 

static int key_init()

{

    misc_register(&key_miscdevice);

    //硬件初始化

    key_hw_init();

    //注册中断处理程序

    request_irq(IRQ_EINT(0),key_int, IRQF_TRIGGER_FALLING,"OK6410key",0);

    //2. 创建工作

    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);

    INIT_WORK(work1, work1_func);

 

    return 0;

}

 

static void key_exit()

{

    misc_deregister(&key_miscdevice);

    

}

 

module_init(key_init);

module_exit(key_exit);

Makefile:

obj-m := nnnkey.o

KDIR := /home/samba/linux-ok6410

all :

    make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-

clean :

    rm *.order *.symvers *.ko *.o *.mod.c

编译的结果是:

[[email protected] queue]# make

make -C /home/samba/linux-ok6410 M=/home/module/queue modules ARCH=arm CROSS_COMPILE=arm-linux-

make[1]: Entering directory `/home/samba/linux-ok6410‘

CC [M] /home/module/queue/nnnkey.o

/home/module/queue/nnnkey.c:28: warning: function declaration isn‘t a prototype

/home/module/queue/nnnkey.c:53: warning: function declaration isn‘t a prototype

/home/module/queue/nnnkey.c:67: warning: function declaration isn‘t a prototype

Building modules, stage 2.

MODPOST 1 modules

CC /home/module/queue/nnnkey.mod.o

LD [M] /home/module/queue/nnnkey.ko

make[1]: Leaving directory `/home/samba/linux-ok6410‘

以上是关于4.中断分层设计的主要内容,如果未能解决你的问题,请参考以下文章

4.中断分层设计

视频在线率统计——基于驱动总线设备的领域驱动设计方法落地

测试驱动设计和分层架构

T7 平台总线

驱动的分层设计——按键点灯

嵌入式软件架构设计之分层设计