裸板中的中断处理
1.硬件处理
(1)cpu强制进入中断模式
(2)cpsr ->spsr
(3)lr = pc -4
(4)cpu强制跳转到中断向量表
2.软件处理
初始化中断
(1)设置中断管脚
(2)设置触发方式(电平、边沿 EINT0/1/2)
(3)设置中断模式 (IRQ/FIQ)INTMOD
(4)使能中断,开屏蔽位 INTMSK,EINTMASK,CPSR
(5)设置优先级
中断处理
(1)计算返回地址 lr = lr -4
(2)保存现场
(3)执行中断处理函数
(4)恢复现场
linux的中断机制
1. 中断号的申请和中断处理函数的注册
static inline int __must_check
request_irq
(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
irq: 中断号 定义在 arch/arm/mach-s5pv210/include/mach/irqs.h
arch/arm/plat-samsung/include/plat/irqs.h
中断号 : 表示中断的类型,对中断源的分类
handler : 中断处理函数
typedef irqreturn_t (*irq_handler_t)(int, void *);
中断处理函数是函数指针类型
IRQF_DISABLED : 如果进入中断处理函数后,会禁止其他中断使能
IRQF_SHARED - allow sharing the irq among several devices
: 共享中断,几个设备公用一个中断号
如果是gpio引发的中断源 ,要使用一下的宏
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010
name : 中断号的名称
dev_id : 在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
2. 中断函数的实现:
3.中断的释放:
void free_irq(unsigned int irq, void *dev_id)
2. 中断函数的实现:
typedef irqreturn_t (*irq_handler_t)(int, void *);
3.中断的释放:
void free_irq(unsigned int irq, void *dev_id)
irq : 要释放的中断号
dev_id : 要释放中断的名称,一般设置为NULL
中断处理函数中:
1. 不能执行有可能睡眠的操作
2. 不能使用慢速的函数进行处理,比如printk(),copy_to_user
3. 实现中断处理有一个原则,就是尽可能快地处理并返回
中断处理分成2个部分,一个快速处理部分,一个是慢速处理部分
中断的顶半部 : 用来做快速处理的,处理完成后调用底半部后,退出中断
中断的底半部 : 一段安全时间后,系统空闲了,调用底半部部分进行处理
底半部的机制:tasklet
1.依赖的头文件
<linux/interrupt.h>
2. 定义一个tasklet结构体
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
struct tasklet_struct mytasklet;
3. 初始化这个结构体 并注册处理函数
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)
参数:
t : struct tasklet_struc 结构体指针
func : 函数指针类型的变量,底半部的处理函数
data : 是给函数指针传参的
tasklet_init(&hello_device.tlet, jit_tasklet_fn, (unsigned long)&hello_device);
jit_tasklet_fn: 这个函数就是底半部的调用函数,这个函数会在中断处理后的一段安全时间后执行
4. 调用tasklet底半部的处理函数
tasklet_schedule()
static inline void tasklet_schedule(struct tasklet_struct *t)
t : 是要进行调度的tasklet
tasklet_schedule(&dev->tlet);
工作队列
:
1. 依赖头文件<linux/workqueue.h>
还可以是这个头文件: #include <linux/interrupt.h>,因为在这个头文件内已经包含了<linux/workqueue.h>
2. 定义一个结构体
struct work_struct my_wq;
3. 初始化工作队列并将其与处理函数绑定
INIT_WORK(&my_wq, (void *) my_wq_func);
my_wq : 结构体的地址
my_wq_func : 工作队列的处理函数
4. 调用工作队列的底半部处理函数
schedule_work(&dev->my_wq);
tasklet与工作队列
1. 工作队列的使用方法和tasklet非常相似
2. tasklet运行于中断上下文 (中断上下文是由硬件触发的处理函数)
工作队列运行与进程的上下文(是应用程序调用内核的处理机制)
3. tasklet处理函数中不能睡眠,而工作队列处理函数中允许睡眠