中断处理流程深度剖析
中断概念:
CPU在工作的过程中,经常需要与外设进行交互,交互的方式包括“轮询方式”,“中断方式”。
1、轮询方式:
CPU不断地查询设备的状态。该方式实现比较简单,但CPU利用率很低,不适合多任务的系统。
2、中断方式:
CPU在告知硬件开始一项工作后,CPU就去做别的事去了,当硬件完成了该项工作后,向CPU发送一个信号,告知CPU它已经完成了这项工作。
中断生命周期:
中断源:
在中断的生命周期中,中断源的作用是负责产生中断信号。
S3C2440支持60个中断源;
S3C6410支持64个中断源;
S5PV210支持93个中断源;
中断处理:
非向量方式(2440开发板)
向量方式(6410和210开发板)
6410开发板按键中断
总流程:
1、按键初始化,将GPIO端口设置为中断源的方式。
2、初始化中断控制器
3、中断处理程序
1、按键初始化,将GPIO端口设置为中断源的方式
按键电路原理图:
配置GPIO口的中断功能:
代码举例:
#define GPNCON (volatile unsigned long*)0x7f008830
#define GPN0_INTERRUPT (0b10<<(0*2))
#define GPN1_INTERRUPT (0b10<<(1*2))
#define GPN2_INTERRUPT (0b10<<(2*2))
#define GPN3_INTERRUPT (0b10<<(3*2))
#define GPN4_INTERRUPT (0b10<<(4*2))
#define GPN5_INTERRUPT (0b10<<(5*2))
void button_init()//使能6个按键的中断
{
*(GPNCON) |= GPN0_INTERRUPT;
*(GPNCON) |= GPN1_INTERRUPT;
*(GPNCON) |= GPN2_INTERRUPT;
*(GPNCON) |= GPN3_INTERRUPT;
*(GPNCON) |= GPN4_INTERRUPT;
*(GPNCON) |= GPN5_INTERRUPT;
}
2、初始化中断控制器 和 中断处理程序
采用向量方式中断。
注意:需要初始化irq模式下的栈sp指针:
init_stack:
msr cpsr_c, #0xd2
ldr sp, =0x53000000 @初始化r13_irq,用于中断
msr cpsr_c, #0xd3
ldr sp, =0x54000000 @4是任意指定的内存位置,指向内存64M的位置
mov pc, lr
代码举例:
/*中断寄存器*/
#define EXT_INT_0_CON *((volatile unsigned int *)0x7f008900)
#define EXT_INT_0_MASK *((volatile unsigned int *)0x7f008920)
#define EXT_INT_0_PEND *((volatile unsigned int *)0x7f008924)
#define VIC0INTENABLE *((volatile unsigned int *)0x71200010)
#define EINT0_VECTADDR *((volatile unsigned int *)0x71200100)
#define EINT5_VECTADDR *((volatile unsigned int *)0x71200104)
#define VIC0ADDRESS *((volatile unsigned int *)0x71200f00)
#define VIC1ADDRESS *((volatile unsigned int *)0x71300f00)
void key1_handle()
{
//保护现场
__asm__(
"sub lr, lr, #4\n"
"stmfd sp!, {r0-r12, lr}\n"
:
:
);
lightLED();
/* 清除中断 */
EXT_INT_0_PEND = ~0x0;
VIC0ADDRESS = 0;
VIC1ADDRESS = 0;
//恢复现场
__asm__(
"ldmfd sp!, {r0-r12, pc}^ \n"
:
:
);
}
void key6_handle()
{
//保护现场
__asm__(
"sub lr, lr, #4\n"
"stmfd sp!, {r0-r12, lr}\n"
:
:
);
offLED();
/* 清除中断 */
EXT_INT_0_PEND = ~0x0;
VIC0ADDRESS = 0;
VIC1ADDRESS = 0;
//恢复现场
__asm__(
"ldmfd sp!, {r0-r12, pc}^ \n"
:
:
);
}
void init_irq()
{
EXT_INT_0_CON = (0b010)|(0b010<<8);//配置为下降沿触发
EXT_INT_0_MASK = 0; //取消屏蔽外部中断
VIC0INTENABLE |= (0b1)|(0b10); //使能外部中断
//用户按下key时,CPU就会自动的将VIC0VECTADDR0的值赋给
//VIC0ADDRESS并跳转到这个地址去执行
EINT0_VECTADDR = (int)key1_handle;
EINT5_VECTADDR = (int)key6_handle;
//开启总中断
__asm__(
"mrc p15,0,r0,c1,c0,0\n"
"orr r0,r0,#(1<<24)\n"
"mcr p15,0,r0,c1,c0,0\n"
"mrs r0,cpsr\n"
"bic r0, r0, #0x80\n"
"msr cpsr_c, r0\n"
:
:
);
}