1.中断顶半部,中断底半部分概念
中断顶半部:响应中断,启动底半部(作用相当于裸机设置标志 )。
一般用来做响应,以及耗时短的,比较紧急的事件,不太紧急的事件就把它拆出来写在中断底半部。
中断底半部:发生中断时应该执行主体代码。耗时的代码。这部分代码会在适合时候,被内核执行。
只要延后执行不会出问题的代码都 可以放在中断底半部分处理。
2. 中断相关内核 API 接口。
中断注册函数: 这个函数一旦注册一中断,其实已经默认使能该中断。
int request_irq(unsigned int irq, //中断编号
irq_handler_t handler, //指向中断服务函数的指针
unsigned long flags, //该中断的特征,标志(如触发边沿等)
const char *name, //中断名,随便。
void *dev) //该中断惟一的标识指针,只要是惟一就可以了。
功能:注册一个中断
参数:
irq:中断编号 ,不是看芯片手册 ,而是由linux内核定义好的 中断号,通常使用宏来定义。
不同的芯片中断数量,不一样,对应的外设也不相同,所以中断号定义一般是存放的具体芯片相关的目录中。一般定义一般在:arch\构架\mach-芯片型号\include\mach\Irqs.h
部分定义:
#define EXYNOS4_IRQ_EINT0 IRQ_SPI(16)
#define EXYNOS4_IRQ_EINT1 IRQ_SPI(17)
#define EXYNOS4_IRQ_EINT2 IRQ_SPI(18)
#define EXYNOS4_IRQ_EINT3 IRQ_SPI(19)
#define EXYNOS4_IRQ_EINT4 IRQ_SPI(20)
对于外部中断:一般不直接使用上面的EXYNOS4_IRQ_EINTX 宏,因为外部中断取得中断号一般使用一个 gpio_to_irq 这个函数取得它对应中断号。
要编写中断编程,使用定义 好中断号需要包含#include
handler:处理者,就是中断服务函数,是一个函数指针。是一个指向 返回值类型为 irqreturn_t (Irqreturn.h (include\linux)),
/**
* enum irqreturn
* @IRQ_NONE 表示中断没有被处理,比较少用,只会在共享中断中出现
* @IRQ_HANDLED 表示中断被正确处理了,常用。
* @IRQ_WAKE_THREAD 表示去唤醒中断处理者的线程。比较少用。
*/
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
}; 有两形式参数,分别为int和void*的函数指针。
typedef irqreturn_t (*irq_handler_t)(int, void *);
补全参数:typedef irqreturn_t (*irq_handler_t)(int irq, void *dev);
中断服务调用时候,传递的实际参数就是 request_irq 时的第1个参数(irq)和最后一个参数(dev)。
所在,中断服务程序模板:
irqreturn_t isr_name(int irq,void *dev)
{
//只会共享中断中出现下面的判断。一般应用中比较少使用共享中断。
if(不是本设备产生中断信号)
return IRQ_NONE;
.....
return IRQ_HANDLED;
}
flags: 中断标志,特征,(如触发边沿等),其值由内核源码定义。只能使用内核源码中定义好的宏。
定义在Interrupt.h (include\linux)
#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 //设置触发方式 为低电平
以上还有别名:
IRQ_TYPE_NONE 对应IRQF_TRIGGER_NONE
* IRQ_TYPE_EDGE_RISING - rising edge triggered
* IRQ_TYPE_EDGE_FALLING - falling edge triggered
* IRQ_TYPE_EDGE_BOTH - rising and falling edge triggered
* IRQ_TYPE_LEVEL_HIGH - high level triggered
* IRQ_TYPE_LEVEL_LOW - low level triggered
组合:
IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)
以下的标志仅用于外部中断,可以组合使用(只要硬件支持,不冲突,可使用 | 符号组合,如 IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING)。
以下标志可以用于外部中断和内部中断
#define IRQF_DISABLED 0x00000020 //指明该中断是独占中断,只能注册一次
#define IRQF_SAMPLE_RANDOM 0x00000040 //对内核产生产生随机数有帮助,这个值实际中没太多价值。
#define IRQF_SHARED 0x00000080 //指明该中断是共享中断,可以注册多次
name:中断名, 随便。用来给内核记录下这个号已经被使用了。注册后会在,会产生 /proc/irq/中断名/ 目录,目录中会有一些和中断相关的文件。
dev:在内核所有注册的中断函数的dev参数,代表该中断惟一的标识,只要是惟一就可以了。
1)如果 flags 指定为共享中断(IRQF_SHARED),则不能为NULL,并且保证惟一性。
很多人认为指定为共享中断时,dev 用来标识发生中断时具体是哪一个中断.
实际上,这个dev并不具体标识具体 是哪一个中断的能力。这个参数真正的用途是用来注销共享中断中的具体哪一个中断。
Linux下共享中断,只要注册了,当发生中断,会把所关联的中断服务函数运行一次。
当注销一个共享中断时候,
2)如果 flags 指定为独占中断(IRQF_DISABLED),此参数可以是NULL
Linux 下共享中断的意义:可以多次调用 request_irq 注册同一个中断号。
Linux 下独占中断的意义:同一个中断号只能被request_irq 注册一次。
注销函数:void free_irq(unsigned int irq,void * dev_id)
irq: 要注销的中断号
dev_id:其实就是要 注册时候使用的dev参数,在共享中断必不可少。不能传递NULL。
为了防止在注销时同时发生中断,调用时候,先禁止中断。
禁止中断:void disable_irq_nosync(unsigned int irq);
void disable_irq(unsigned int irq);
参数:irq,要禁止的中断对应 的编号
注意:在中断服务程序中不能使用 disable_irq 这个函数,否则内核崩溃,可以使用 disable_irq_nosync。
disable_irq:函数调用后,函数不会马上返回,而等待中断程序执行完成才返回,在中断调用会导致死锁。
disable_irq_nosync:调用后,函数马上返回。
使能中断:
void enable_irq(unsigned int irq);
参数:irq,要使能的中断对应 的编号