赞
踩
处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生
这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件
异常事件处理完成之后再返回到被异常打断的点继续执行程序
不同的处理器对异常的处理的流程大体相似,但是不同的处理器在具体实现的机制上有所不同;比如处理器遇到哪些事件认为是异常事件遇到异常事件之后处理器有哪些动作、处理器如何跳转到异常处理程序如何处理异常、处理完异常之后又如何返回到被打断的程序继续执行等我们将这些细节的实现称为处理器的异常处理机制
概念
导致异常产生的事件称为异常源
ARM异常源
FIQ 快速中断请求引脚有效
IRQ 外部中断请求引脚有效
Reset 复位电平有效
Software Interrupt 执行swi指令
Data Abort 数据终止
Prefetch Abort 指令预取终止
Undefined Instruction 遇到不能处理的指令
异常模式
在ARM的基本工作模式中有5个属于异常模式,即ARM遇到异常后会切
换成对应的异常模式
ARM产生异常后的动作(自动完成)
1.拷贝CPSR中的内容到对应异常模式下的SPSR_<mode>
2.修改CPSR的值
2.1.修改中断禁止位禁止相应的中断
2.2.修改模式位进入相应的异常模式 产生异常
2.3.修改状态位进入ARM状态
3.保存返回地址到对应异常模式下的LR_<mode>
4.设置PC为相应的异常向量(异常向量表对应的地址)
异常向量表
> 异常向量表的本质是内存中的一段代码
> 表中为每个异常源分配了四个字节的存储空间
> 遇到异常后处理器自动将PC修改为对应的地址
> 因为异常向量表空间有限一般我们不会再这里
写异常处理程序,而是在对应的位置写一条跳
转指令使其跳转到指定的异常处理程序的入口
注:ARM的异常向量表的基地址默认在0x00地址
但可以通过配置协处理器来修改其地址
ARM异常返回的动作(自己编写)
1.将SPSR_<mode>的值复制给CPSR
使处理器恢复之前的状态
2.将LR_<mode>的值复制给PC
使程序跳转回被打断的地址继续执行
注:整个过程CPSR保存的永远是当前程序运行状态 , SPSR只是异常时对原来的CPSR进行备份
CPSR:当前程序状态寄存器
- .text
- .global _start
- _start:
- /*
- * Vector table 异常向量表
- */
- b reset
- b .
- b .
- b .
- b .
- b .
- /*
- * 从异常向量表再跳转到IRQ的异常处理程序
- */
- 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的异常处理程序
- */
- irq_handler:
- /*
- * 因为产生IRQ异常后ARM自动保存到LR中的返回地址是被IRQ打断的指令
- * 的下一条再下一条指令的地址,所以我们需要人为的去修正一下
- */
- sub lr, lr, #4
- /*
- * 因为IRQ模式下使用的R0-R12寄存器和USER模式下使用的是同一组
- * 所以在处理异常之前需要先将之前寄存器中的值压栈保护
- */
- stmfd sp!, {r0-r12,lr}
- /*
- * 跳转到do_irq处理异常
- */
- bl do_irq
- /*
- * 异常返回
- * 1.将R0-R12寄存器中的值出栈,使其恢复到被异常打断之前的值
- * 2.将SPSR寄存器中的值恢复到CPSR,使CPU的状态恢复到被异常打断之前
- * 3.将栈中保存的LR寄存器的值出栈给PC,使程序跳转回被异常打断的点继续执行
- */
- ldmfd sp!,{r0-r12,pc}^ // '^'意思是自动将SPSR中的值赋值给
-
- _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

- #include "exynos_4412.h"
-
- unsigned int Flags = 0;
-
- void Delay(unsigned int Time)
- {
- while(Time--);
- }
-
- //IRQ异常处理
- void do_irq(void)
- {
- unsigned int IrqNum = 0;
- /*从中断控制器中获取当前中断的中断号*/
- IrqNum = CPU0.ICCIAR & 0x3FF;
-
- /*根据中断号处理不同的中断*/
- switch(IrqNum)
- {
- case 0:
- //0号中断的处理程序
- break;
- case 1:
- //1号中断的处理程序
- break;
- /*
- * ... ...
- */
- case 58: //[EINT 10]
- Delay(1000000);
- Flags++;
- printf("key3 pressed.\n");
- /*清除GPIO控制器中GPX1_2的中断挂起标志位*/
- EXT_INT41_PEND = (1 << 2);
- /*将当前中断的中断号写回到中断控制器中,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其它中断*/
- CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | (58);
- break;
- /*
- * ... ...
- */
- case 159:
- //159号中断的处理程序
- break;
- default:
- break;
-
- }
-
- }
-
- void LED2_ON() {
- GPX2.DAT = GPX2.DAT | (1 << 7);
- }
-
- void LED2_OFF() {
- GPX2.DAT = GPX2.DAT & (~(1 << 7));
- }
-
- int main()
- {
-
- /******************************/
- //初始化
- /******************************/
- /*外设层次 - 让外部的硬件控制器产生一个中断信号发送给中断控制器*/
- /*将GPX1_1设置成中断功能*/
- GPX1.CON = GPX1.CON | (0xF << 8);
- /*设置GPX1_1的中断触发方式为下降沿触发*/
- EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 8)) | (0x2 << 8);
-
- /*使能GPX1_2的中断功能*/
- EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 2));
-
-
-
- /******************************/
- //中断控制器层次
- /******************************/
- /*中断控制器层次 - 让中断控制器接收外设产生的中断信号并对其进行管理然后再转发给CPU处理*/
- /*全局使能中断控制器使其能接收外设产生的中断信号并转发到CPU接口*/
- ICDDCR = ICDDCR | 1;
-
- /*在中断控制器中使能57号中断,使中断控制器接收到57号中断后能将其转发到CPU接口*/
- ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 26);
- /*选择由CPU0来处理57号中断*/
- ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0X01 << 16);
-
- /*使能中断控制器和CPU0之间的接口,使中断控制器转发的中断信号能够到达CPU0*/
- CPU0.ICCICR = CPU0.ICCICR | 1;
-
- /*初始化GPX2_7为输出功能 为LED2做准备*/
- GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);
-
- while(1)
- {
- switch(Flags % 2) {
- case 0:
- LED2_ON();
- printf("LED2_ON\n");
- Delay(1000000);
- break;
- case 1:
- LED2_OFF();
- printf("LED2_OFF\n");
- Delay(1000000);
- break;
- }
- }
-
- return 0;
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。