中断: CPU在执行过程中, 硬件发生状态变化时, 打断CPU程序的执行,先做硬件状态变化时需要处理,再接着执行原程序.
中断其实就是由硬件主动来告诉我们它的状态发生改变了, 而不需要一直轮询硬件状态 .
arm里有异常向量表,当发生中断异常,不是执行0x18就是执行0xffff0018上的代码指令.也就是不管是什么中断,都是由在这地址上的代码来处理.
arm里只有一个中断异常功能,都由SOC里的中断控制器来扩展中断线(用于实现多个硬件的中断功能)和管理中断线的功能.
在手册里的P206. GIC的图表里可以看到, 只有PA组,PG组的IO口有中断功能.
除此之外还有很多控制器的中断号.
内部中断:指SOC里的控制器的中断线只在芯片内部,不会引出芯片外的中断.
外部中断:指SOC里会引出芯片外的中断线, 也就是指有中断功能的GPIO.
内部中断什么情况下中断CPU已经是固定的,但外部中断什么情况下中断CPU是由我们来指定在GPIO什么电平状态下产生中断.
一个GPIO的电平状态: 高电平, 低电平, 上升沿, 下降沿, 双边沿
GPIO_INT --> GIC ---> arm IRQ
在linux内核里已实现好arm的中断异常处理,芯片厂商已驱动好中断控制器,定义好中断号(在arch/arm/mach-sunxi/include/mach/irqs.h).
因我们用最多的是外部中断, 可以根据IO口来获取指定的中断号(gpio_to_irq(int gpio)).
我们只需使用一个函数来请求使用中断即可.
#include <linux/interrupt.h>
int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags, const char * name, void *dev)
//irq为中断号, handler为中断处理函数, flags可设触发方式或中断方式,
name为中断设备名字(cat /proc/interrupts), dev用于中断处理函数执行的参数
//中断处理函数的原形:
irqreturn_t (*irq_handler_t)(int irqno, void *arg);
//irqreturn_t是返回值类型,通常是IRQ_HANDLED表示已处理中断了.
// irqno表示当前发生中断的中断号, arg为request_irq时的dev参数.
//中断触发方式:
#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_DISABLED 0x00000020 //当中断处理函数执行时不会被打断
#define IRQF_SAMPLE_RANDOM 0x00000040 //帮助产生随机数种子
#define IRQF_SHARED 0x00000080 //共享中断,即一个中断号,多个处理函数. 但一般都是一个中断号一个处理函数的.
可通过”cat /proc/interrupts”查看系统里所用的中断信息(如中断触发次数等)
/
PA(7)接烟雾传感器.感应到烟雾时输出低电平,正常高电平.
中断的触发方式使用下降沿.
test.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#define DETECT_IO GPIOA(7) //PA(7)
irqreturn_t irq_func(int irqno, void *arg)
{
printk("smoke detected ...\n");
return IRQ_HANDLED;
}
static int __init test_init(void)
{
int ret;
ret = request_irq(gpio_to_irq(DETECT_IO), irq_func, IRQF_TRIGGER_FALLING, "detector", NULL);
return ret;
}
static void __exit test_exit(void)
{
free_irq(gpio_to_irq(DETECT_IO), NULL);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
内核还提供了中断的管理函数, 可用于临时性的关闭或开启中断功能:
void disable_irq(unsigned int irq) //关闭指定中断号的中断功能,调用函数会堵塞直到关闭为止
//注意不可以在中断处理函数里调用此函数
void disable_irq_nosync(unsigned int irq) //关闭指定中断号的中断功能,调用此函数不会堵塞。可在中断处理函数里调用此函数.
void enable_irq(unsigned int irq) //开始指定中断号的中断功能
local_irq_enable(); // cpsr[7] = 0, 恢复arm核心的中断功能;
local_irq_disable(); //关闭arm核心里的中断功能, cpsr[7] = 1
local_irq_save(flags); //备份cpsr寄存器内容到flags变量后, 关闭arm核心里的中断功能
local_irq_restore(flags); //恢复arm核心的中断功能, cpsr = flags