BL
BL
当执行BL指令时,指令执行过程中处理器内部就会将PC寄存器的值拷贝到
LR寄存器,然后再将LR寄存器中的值自减4, 所以LR寄存器中保存的就是
BL指令下一条指令的地址
该时刻PC=N+8 LR=N+4
IRQ中断
IRQ中断
当执行一条指令时产生了一个IRQ中断,执行这条指令过程中处理器不会保
存返回地址,而是执行完成后才会保存,但执行完成后PC的值又会自动增4,
所以对于IRQ来说LR中保存的是被中断打断的指令的下下条指令的地址
该时刻PC=N+12 LR=N+8
因此,产生irq中断时,CPU存储的的LR寄存器中的地址不对,需要认为减去0x4
汇编
.text
.global _start
_start:
/*
* Vector table
*/
b reset
b .
b .
b .
b .
b .
b irq_handler
b .
reset:
/*
* Set vector address in CP15 VBAR register
*/
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
/*
* Set the cpu to SVC32 mode, Disable FIQ/IRQ
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr ,r0
/*
* Defines access permissions for each coprocessor
*/
mov r0, #0xfffffff
mcr p15, 0, r0, c1, c0, 2
/*
* Invalidate L1 I/D
*/
mov r0, #0 @Set up for MCR
mcr p15, 0, r0, c8, c7, 0 @Invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @Invalidate icache
/*
* Set the FPEXC EN bit to enable the FPU
*/
mov r3, #0x40000000
fmxr FPEXC, r3
/*
* Disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @Clear bits 13 (--V-)
bic r0, r0, #0x00000007 @Clear bits 2:0 (-CAM)
orr r0, r0, #0x00001000 @Set bit 12 (---I) Icache
orr r0, r0, #0x00000002 @Set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @Set bit 11 (Z---) BTB
mcr p15, 0, r0, c1, c0, 0
/*
* Initialize stacks
*/
init_stack:
/*svc mode stack*/
msr cpsr, #0xd3
ldr sp, _stack_svc_end
/*undef mode stack*/
msr cpsr, #0xdb
ldr sp, _stack_und_end
/*abort mode stack*/
msr cpsr,#0xd7
ldr sp,_stack_abt_end
/*irq mode stack*/
msr cpsr,#0xd2
ldr sp, _stack_irq_end
/*fiq mode stack*/
msr cpsr,#0xd1
ldr sp, _stack_fiq_end
/*user mode stack, enable FIQ/IRQ*/
msr cpsr,#0x10
ldr sp, _stack_usr_end
/*Call main*/
b main
irq_handler:
//修正返回地址
/*因为产生IRQ异常后自动保存的LR中的返回地址是被irq打断指令的
下一条再下一条指令的地址,所以需要人为的去修复一下*/
sub lr,lr,#4
//因为IRQ模式下使用的R0-R12寄存器和user模式下使用的是同一组
//所以在处理异常之前需要将之前的user模式下中寄存器的值压栈保护
stmfd sp!,{r0-r12,lr}
//处理异常
bl do_irq
//异常返回
/*1.将r0-r12寄存器中的值出栈,使其恢复到异常打断之前的值
2.将spsr寄存器中的值恢复给cpsr,使CPU的状态恢复到被异常打断之前的状态*/
// 3.将栈中的LR寄存器中的值出栈给pc,实现程序的返回 lr----->pc 程序返回
ldmfd sp!,{r0-r12,pc}^
// ^表示出栈的同时spsr的值回复给了cpsr中
_stack_svc_end:
.word stack_svc + 512
_stack_und_end:
.word stack_und + 512
_stack_abt_end:
.word stack_abt + 512
_stack_irq_end:
.word stack_irq + 512
_stack_fiq_end:
.word stack_fiq + 512
_stack_usr_end:
.word stack_usr + 512
.data
stack_svc:
.space 512
stack_und:
.space 512
stack_abt:
.space 512
stack_irq:
.space 512
stack_fiq:
.space 512
stack_usr:
.space 512
C程序
#include"exynos_4412.h"
void Delay (unsigned int Time )
{
while(Time--);
}
int main()
{
/*产生一个中断信号*/
/*1.属于外设层次,让外部的硬件控制器能产生一个中断信号并发送给中断控制器*/
/*将GPX1_1设置成中断功能*/
GPX1.CON = GPX1.CON |( 0xF << 4);
/*设置GPX1_1中断的触发方式---下降沿触发*/
EXT_INT41_CON = EXT_INT41_CON & (~(0x7<<4))|(0x2<<4);
/*使能GPX1_1的中断功能*/
EXT_INT41_MASK = EXT_INT41_MASK & (~(1<<1));
/*2.中断控制器层次--让中断控制器接收外设发来的中断信号并对其进行管理然后转发给合适的CPU去处理*/
ICDDCR = ICDDCR |1;/*全局使能中断控制器,使其能够接收外部设备产生的中断信号并转发给CPU接口*/
ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 <<25);
/*在中断控制器中使能57号中断,使其中断控制器在接收到57号中断后能将其进一步转发到CPU接口*/
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~ (0xFF<<8)) |(0x01<<8);
/*选择cpu0来处理57号中断*/
CPU0.ICCICR = CPU0.ICCICR | 1;
/*将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/
GPX2.CON =GPX2.CON &( ~ (0xF <<28)) | (0x1 << 28);
while(1)
{
GPX2.DAT = GPX2.DAT | (1<<7);
Delay(1000000);
GPX2.DAT = GPX2.DAT &( ~(1<<7));
Delay(1000000);
}
return 0;
}
排队寄存器,结束中断处理程序:
区分是那种irq异常源的代码:(共160种中断)
//IRQ异常处理
void do_irq(void)
{
unsigned int IrqNum = 0;
/*从中断控制器中获取当前中断的中断号*/
IrqNum = CPU0.ICCIAR & 0x3FF;
/*根据中断号处理不同的中断*/
switch(IrqNum)
{
case 0:
//0号中断的处理程序
break;
case 1:
//1号中断的处理程序
break;
/*
* ... ...
*/
case 57:
printf("Key2 Pressed\n");
/*清除GPIO控制器中GPX1_1的中断挂起标志位*/
EXT_INT41_PEND = (1 << 1);
/*将当前中断的中断号写回到中断控制器中,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其它中断*/
CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | (57);
break;
/*
* ... ...
*/
case 159:
//159号中断的处理程序
break;
default:
break;
}
}