CPU一上电就处于 ARM 状态。
如果对于arm的CPU的工作模式和状态有疑惑: ** ARM处理器的寄存器,ARM与Thumb状态,7中运行模式
如果对arm刚上电的工作模式和状态有疑惑: **arm 上电或复位时的状态,模式
ARM异常中断处理概述
1、中断的概念
中断是一个过程,是CPU在执行当前程序的过程中因硬件或软件的原因插入了另一段程序运行的过程。
因硬件原因引起的中断过程的出现是不可预测的,即随机的,而软中断是事先安排的。
2、中断源的概念
我们把可以引起中断的信号源称之为中断源。
arm920T中有 60 个 中断源 :
(1) 外部 子中断源 20 个:
(2 ) 拥有 15 个内部 子 中断源的 :
(4)摄像头接口:
3、中断优先级的概念
ARM处理器中有7种类型的异常,按优先级从高到低的排列如下:
复位异常(Reset)、
数据异常(Data Abort)、
快速中断异常(FIQ)、
外部中断异常(IRQ)、
预取异常(Prefetch Abort)、
软件中断(SWI)、
未定义指令异常(Undefined instruction)。
ARM异常的优先级
ARM体系异常种类细说:
1.复位异常
当处理器的复位引脚有效时,系统产生复位异常中断,程序跳转到复位异常中断处理程序处执行。
复位异常中断通常用在下面两种情况下。
*系统上电。
*系统复位。
当复位异常时,系统执行下列伪操作:
R14_svc = UNPREDICTABLE value //任意值
SPSR_svc = UNPREDICTABLE value //任意值
CPSR[4∶0] = 0b10011 /*进入管理模式*/
CPSR[5] = 0 /*处理器进入ARM状态*/
CPSR[6] = 1 /*禁止快速中断*/
CPSR[7] = 1 /*禁止外设中断*/
If high vectors configured then
PC = 0xffff0000
Else
PC = 0x00000000
复位异常中断处理程序的主要功能:
*设置异常中断向量表。
*初始化数据栈和寄存器。
*初始化存储系统,如系统中的MMU等。
*初始化关键的I/O设备。
*使能中断。
*处理器切换到合适的模式。
*初始化C变量,跳转到应用程序执行。
2.未定义指令异常
当ARM处理器执行协处理器指令时,它必须等待一个外部协处理器应答后,才能真正执行这条指令。
若协处理器没有响应,则发生未定义指令异常。
3.软中断SWI
软中断异常发生时,处理器进入特权模式,执行一些特权模式下的操作系统功能。
4.预取指令异常
预取指令异常是由系统存储器报告的。当处理器试图去取一条被标记为预取无效的指令时,
发生预取异常。
5.数据访问中止异常
数据访问中止异常是由存储器发出数据中止信号,它由存储器访问指令Load/Store产生。
当数据访问指令的目标地址不存在或者该地址不允许当前指令访问时,处理器产生数据访问中止异常。
6.外部中断IRQ
当处理器的外部中断请求引脚有效,而且CPSR寄存器的I控制位被清除时,处理器产生外部中断IRQ异常。
系统中各外部设备通常通过该异常中断请求处理器服务。
7.快速中断FIQ
当处理器的快速中断请求引脚有效且CPSR寄存器的F控制位被清除时,处理器产生快速中断请求FIQ异常。
当异常发生时 , 处理器会把PC设置为一个特定的存储器 地址。
这一地址放在被称为向量表(vector table)的特定地址范围内。
向量表的入口是一些 跳转指令 ,跳转到专门处理某个异常或中断的子程序。
ARM Generic Interrupt Controller : ARM GIC : arm 通用中断控制器
如果对 通用中断控制器有疑惑的,请看: ***arm 通用中断控制器
中断的状态:
Inactive(不激活) 没有被激活或挂起的中断
Pending(挂起) 这个中断可以被硬件识别或是由软件产生的,正处於等待处理器处理。
Active(激活) 这个中断被处理器从 通用中断控制器中的 中断源中识别了出来,并且正在处理这个中断,且没有处理完成。
Active and pending(激活和挂起) 处理器正在处理某个中断,且通用中断控制器有一个挂起的中断,
这两个中断是属于同一个中断源的
中断流程:
进入异常
在异常发生后,ARM内核会作以下工作:
1.在LR中保存下一条指令的地址(即返回地址)
2.将CPSR复制到适当的SPSR中;
3.将CPSR模式位强制设置为与异常类型相对应的值;
4.原来无论是ARM状态或THUMB 状态,都进入ARM状态;
5.屏蔽 快速中断和外部中断。
6.强制PC从相关的异常向量处取指。
进入异常过程:
1. 程序在系统模式下运行 用户程序,假定当前处理 器状态为Thumb状态、允 许IRQ中断;
2. 用户程序运行时发生 I RQ中断,硬件完成以下 动作:
* 置位I位(禁止IRQ中断)
* 清零T位(进入ARM状态)
* 设置MOD位,切换处理器 模式至IRQ模式
* 将下一条指令的地址存入 IRQ模式的LR寄存器
* 将CPSR寄存器内容存入 I RQ模式的SPSR寄存器
* 将跳转地址存入PC,实 现跳转
中断响应步骤:
1、保护断点,
2、寻找中断入口,根据不同的中断源所产生的中断,查找不同的入口地址。
3、执行中断处理程序。
4、中断返回
具体实现如下:
1.判断处理器状态
当异常发生时, 处理器自动切换到ARM状态 ,所以在异常处理函数中要判断
在异常发生前处理器是ARM状态还是Thumb状态。这可以通过检测SPSR的T位来判断。
2.向量表
每一个异常发生时总是从异常向量表开始跳转。最简单的一种情况是向量表里面的
每一条指令直接跳向对应的异常处理函数。
异常处理向量表如下:
利用跳转指令B建立异常向量表
ENTRY ;汇编程序开始
b reset ;跳入reset处理程序
b HandleUndef ;跳到未定义处理程序
b HandSWI ;跳到软中断处理程序
b HandPrefetchAbt ;跳到预取指令处理程序
b HandDataAbt ;跳回数据访问中止处理程序
b HandNoUsed ;保留
b HandleIRQ ;跳入中断处理程序
b HandleFIQ ;跳回快速中断处理标签
注意:跳转指令 B的跳转范围为±32MB ,但很多情况下不能保证所有的异常处理函数都定位在向量的32MB范围内,
需要更大范围的跳转,而且由于向量表空间的限制,只能由一条指令完成。
具体实现方法有下面两种。
(1)MOV PC,#imme_value
这种办法将目标地址直接赋值给PC。但这种方法受格式限制不能处理任意立即数。
这个立即数由一个8位数值循环右移偶数位得到。
(2)LDR PC,[PC+offset]
把目标地址先存储在某一个合适的地址空间,然后把这个存储器单 元的32位数据传送给PC来实现跳转。
这种方法对目标地址值没有要求。但是存储目标地址的存储器单元必须在当前指令的 ±4KB 空间范围内。
退出异常
当异常结束时,异常处理程序必须:
1.将LR中的值减去偏移量后存入PC,偏移量根据 异常的类型而有所不同;
2.将SPSR的值复制回CPSR;
3.清零在入口置位的中断禁止标志。
注:恢复CPSR的动作会将T、 F和I位自动恢复为 异常发生前的值。
退出异常过程
在异常处理结束后,异常 处理程序完成以下动作:
* 将SPSR寄存器的值复制回 CPSR寄存器;
* 将LR寄存的值减去一个常 量后复制到PC寄存器,跳 转到被中断的用户程序。
从异常处理程序中返回
当一个ARM异常处理返回时,一共有3件事情需要处理
*通用寄存器的恢复
*状态寄存器的恢复
*PC指针的恢复
具体操作如下:
1.恢复被中断程序的处理器状态
PC和CPSR的恢复 可以通过一条指令来实现,下面是3个例子。
MOVS PC,LR
SUBS PC,LR,#4
LDMFD SP!,{PC}^
这几条指令是普通的数据处理指令,特殊之处在于它们把程序计数器寄存器PC作为目标寄存器,
并且带了特殊的后缀“S“或“^”。其中 “S“或“^”的作用 就是使指令在执行时,
同时完成从SPSR到CPSR的拷贝 ,达到恢复状态寄存器的目的。
2.异常的返回地址
注意 :异常返回时,另一个非常重要的问题就是返回地址的确定。前面提到过,
处理器进入异常时会有一个保存LR的动作,但是 该保持值并不一定是正确中断的返回地址 。
以一个简单的指令执行流水状态图来对此加以说明,如图所示, 3级流水线示例:
总结IRQ中断处理过程
中断寄存器:
1,中断控制器中有 5 个中断控制寄存器:
(1),源挂起寄存器,SOURCE PENDING (SRCPND) REGISTER
中断控制寄存器INTCON共有 32 位,每一位对应着一个中断源,
当中断源发出中断请求的时候,就会置位源挂起寄存器的相应位。反之,中断的挂起寄存器的值为0。
该寄存器中的每一位对应特定的中断源,
当该位的值为: 0 ,表示这个中断源中 没有中断被请求 。
1 ,表示该中断源中 有 中断被请求
1.1,子源挂起寄存器(SUB SOURCE PENDING (SUBSRCPND) REGISTER )
(2),中断模式寄存器,INTERRUPT MODE (INTMOD) REGISTER
中断模式寄存器INTMOD共有 32 位,每一位对应着一个中断源,
当中断源的模式位设置为 1 时,对应的中断会由ARM920T内核以 FIQ模式 来处理。
相反,当模式位设置为 0 时,中断会以 IRQ模式 来处理。 默认为IRQ模式。
注意 :如果中断模式为 FIQ模式 ,INTPND和INTOFFSET寄存器是 无效 的,
也就是说,这 两个寄存器只在IRQ模式下有效 。
该寄存器中的每一位对应特定的中断源,
当该位的值为: 0,表示这个中断源中的中断为IRQ模式。
1,表示该中断源中的中断为FIQ模式 。
(3),中断屏蔽寄存器,INTERRUPT MASK (INTMSK) REGISTER
这个寄存器有32位,分别对应一个中断源。
当中断源的屏蔽位设置为 1 时,CPU 不响应 该中断源的中断请求,
反之,等于 0 时CPU能 响应 该中断源的中断请求。
该寄存器中的每一位对应特定的中断源,
当该位的值为: 0,表示这个中断源没有被屏蔽,其中的中断可以被响应。
1,表示该中断源被屏蔽,该中断源中的中断不可 以被响应 。
3.1,子中断屏蔽寄存器(INTERRUPT SUB MASK (INTSUBMSK) REGISTER )
(4),中断优先权寄存器,PRIORITY REGISTER (PRIORITY)
CPU某个时刻只能对一个中断源进行中断处理,
如果现在有3个中断同时发生了,那CPU要按什么顺序处理这个3个中断呢?
这正是引入优先级判断的原因所在,通过优先级判断,CPU可以按某种顺序逐个处理中断请求。
arm920t 的优先级判断分为两级。
①ARBITER6所控制的REQ0,1,2,3,4,5实际上对应ARBITER0,1,2,3,4,5
②REQ0在任何情况下具有最高优先级,REQ5具有最低优先级;
对ARBITER1-4的仲裁组来说(仲裁组组中的不同的中断),在任何情况下:对应的仲裁组中的中断REQ0具有最高优先级,
中断REQ5具有最低优先级,而 ARBITER0,ARBITER5仲裁组中并没有中REQ0,REQ5。
对ARBITER6仲裁组来说(不同的中断仲裁组),在任何情况下:ARBITER0具有最高优先级,
即仲裁组中的中断优先级是最高的,比别的仲裁组中的中断优先级都要高。ARBITER5具有最低优先级,同理。
1,当 ARB_MODEx = 0:
ARB_SELx 仲裁组中的中断的优先级 不会 被自动改变,
如:ARB_SEL0 bit[8:7] = 00 优先级次序:REQ1>REQ2>REQ3>REQ4(即:EINT0>EINT1>EINT2>EINT3);
bit[8:7] = 01 优先级次序:REQ2>REQ3>REQ4<REQ1(即:EINT1>EINT2>EINT3<EINT0);
ARB_SEL1 bit[10:9] = 00 优先级次序:REQ0> REQ1>REQ2>REQ3>REQ4>REQ5
(即:EINT4_7>EINT8_23>INT_CAM>Nbatt_FLT>INT_TICK>INT_WDT/AC97);
( 即:EINT8_23>INT_CAM>Nbatt_FLT>INT_TICK>INT_WDT/AC97<EINT4_7);
其他的ARB_SELx同理
2,当 ARB_MODEx = 1:
ARB_SELx 仲裁组中的中断被触发后,组中的中断的优先级 会 被自动改变。
1, REQ0和 REQ5因为优先级固定而不参与轮转
2,当 REQ1 中断被处理之后,该中断仲裁组中的中断优先级 ARB_SELx 会变为 01b。
3,当 REQ2 中断被处理之后,该中断仲裁组中的中断优先级 ARB_SELx 会变为 10b。
4,当 REQ3 中断被处理之后,该中断仲裁组中的中断优先级 ARB_SELx 会变为 11b。
5,当 REQ4 中断被处理之后,该中断仲裁组中的中断优先级 ARB_SELx 会变为 00b。
如:ARB_SEL0 bit[8:7] = 00 优先级次序:REQ1>REQ2>REQ3>RE Q4(即:EINT0>EINT1>EINT2>EINT3);
假设 REG2 (即 EINT1)中断被触发后,当cpu处理完REG2(EINT1)的中断处理函数之后,ARB_SEL0 中的值为 bit[8:7] = 10,
优先级次序:REQ3>RE Q4<REQ1>REQ2(即:EINT2>EINT3<EINT0>EINT1);
其他的 ARB_SELx 同理。
(5),中断挂起寄存器,INTERRUPT PENDING (INTPND) REGISTER
中断挂起寄存器INTPND共有32位,每一位对应着一个中断源, 当中断请求被响应的时候,相应的位会被设置为1。
在某一时刻只有一个位能为1,因此在中断服务子程序中可以通过判断INTPND来判断哪个中断正在被响应,
在中断处理函数退出前,需要清除源挂起寄存器中对应的位,以及 中断挂起寄存器中对应的位。
5.1,IRQ偏移寄存器(INTERRUPT OFFSET (INTOFFSET) REGISTER )
中断偏移寄存器给出INTPND寄存器中哪个是IRQ模式的中断请求。
INTOFFSET的值对应INTPND中的中断挂起位,取值范围为:
2,外部中断寄存器
1,外部中断控制寄存器(EXTINTn)
arm920t有 24 个外部中断有几种中断触发方式,EXTINTn配置外部中断的触发类型是电平触发、边沿触发及触发的极性。
8 个外部中断可以由多种信号触发方式所请求。EXTINT 寄存器为外部中断配制信号触发方式为电平触发或边沿触发,
同时还配制信号触发极性。 为了确认电平中断,由于噪声滤波必须保持 EXTINTn引脚上有效逻辑电平至少 40ns
EXTINT0:
EXTINT1:
EXTINT2:
1.1,外部中断滤波寄存器(EINTFLTn (External Interrupt Filter Register n) )
为了确认 电平中断 ,由于噪声滤波必须保持 EXTINTn引脚上有效逻辑电平至少 4 0ns 。
2,外部中断屏蔽寄存器(EINTMASK)
EXTMASK[23:4]分别对应外部中断23~4,等于1,对应的中断被屏蔽;等于0,允许外部中断。EXTMASK[3:0]保留。
3,外部中断挂起寄存器 (EINTPEND )
中断挂起寄存器INTPND共有 32 位,前4位保留(因为EINT0—EINT3对应的挂起位在寄存器INTPND中),
4~23位对应着一个中断源,当中断请求被响应的时候,相应的位会被设置为1 。
在中断服务子程序中可以通过判断 EINTPND 来判断哪个中断在提起申请。
arm920t中断相关各寄存器关系:
测试实例:
点击(此处)折叠或打开
- @******************************************************************************
- @ File:head.S
- @ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数
- @******************************************************************************
-
.extern main
-
.text
-
.global _start
- _start:
- @******************************************************************************
- @ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
- @******************************************************************************
- b Reset
-
- @ 0x04: 未定义指令中止模式的向量地址
- HandleUndef:
- b HandleUndef
- @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
- handleSWI:
- b handleSWI
-
-
- @ 0x0c: 指令预取终止导致的异常的向量地址
- HandlePrefetchAbort:
- b HandlePrefetchAbort
-
-
- @ 0x10: 数据访问终止导致的异常的向量地址
- HandleDataAbort:
- b HandleDataAbort
- @ 0x14: 保留
- HandleNotUsed:
- b HandleNotUsed
-
- @ 0x18: 中断模式的向量地址
- b HandleIRQ
-
-
- @ 0x1C: 快中断模式的向量地址
- HandleFIQ:
- b HandleFIQ
- @ CPU刚上电或是复位之后,系统处于 arm 状态,管理(SVC)模式。
- Reset:
- ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈
- bl disable_watch_dog @ 关闭 WATCHDOG,否则CPU会不断重启
-
- @ 在arm汇编中,唯一能更改cpsr的指令就是msr, cpsr_c是CPSR的低8位,
- @ 0xd2 = (110 10010)b,可参见CPSR各bit的定义,
- @ 可知CPSR的低5位是工作模式位,如果定义为10010就是进入IRQ(中断模式),
- @ 于是这条指令的意义就是进入中断模式
- msr cpsr_c, #0xd2 @ 进入 中断(IRQ) 模式
- ldr sp, =3072 @ 设置中断模式的栈指针,中断模式是有自己的堆栈寄存器的,就是r13。
-
- msr cpsr_c, #0xd3 @ 重新进入 管理(SVC) 模式。
- ldr sp, =4096 @ 设置 管理(SVC) 模式下的栈指针
- @ 其实复位之后,CPU就处于管理模式,
- @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略
- bl init_led @ 初始化 LED 的GPIO管脚
- bl init_irq @ arm中中断使能前的初始化配置
-
- msr cpsr_c, #0x5f @ 进入 系统(sys)模式,使能 IRQ 中断
-
- ldr lr, =halt_loop @ 设置返回地址
- ldr pc, =main @ 调用 main 函数
- halt_loop:
- b halt_loop
-
-
- HandleIRQ:
- sub lr, lr, #4 @ 计算返回地址,irq 模式,lr - 4,指向发送异常是正在执行的指令
- stmdb {r0-r12,lr} @ 保存使用到的寄存器,现场保护
- @ 注意,此时的sp是中断模式的sp
- @ 初始值是上面设置的3072
-
- ldr lr, =int_return @ 设置调用ISR即EINT_Handle函数后的返回地址
- ldr pc, =EINT_Handle @ 调用中断服务函数,在interrupt.c中
-
- int_return:
- ldmia {r0-r12,pc}^ @ 中断返回,现场恢复, ^表示将spsr的值复制到cpsr
如果对cpsr_c 有疑惑的请看: *arm中的 CPSR_c是什么
如果对cpsr 有疑惑的请看: * CPSR 和 SPSR
如果对arm上电或复位,cpu的状态和模式,请看:
**arm 上电或复位时的状态,模式
如果对 stmdb,ldmia 指令有疑惑的请看: **arm汇编指令之数据块传输(LDM,STM)详见
如果对arm中的 r0 - r15 的作用有疑惑的请看: *arm中R0-R15寄存器的作用
中断初始化:
点击(此处)折叠或打开
- void EINT_Handle(void)
-
{
- unsigned long oft = INTOFFSET; //获取中断偏移,即那个中断被触发
- switch( oft )
- {
- // S2被按下
- case 0:
- {
- GPFDAT |= (0x7<<4); // 所有LED熄灭
- GPFDAT &= ~(1<<4); // LED1点亮
- break;
- }
- // S3被按下
- case 2:
- {
- GPFDAT |= (0x7<<4); // 所有LED熄灭
- GPFDAT &= ~(1<<5); // LED2点亮
- break;
- }
- // K4被按下
- case 5:
- {
- GPFDAT |= (0x7<<4); // 所有LED熄灭
- GPFDAT &= ~(1<<6); // LED4点亮
- break;
- }
- default:
- break;
- }
- //清中断
- if( oft == 5 )
- EINTPEND = (1<<11); // EINT8_23合用IRQ5,写“1”清零此位
- SRCPND = 1<<oft;
- INTPND = 1<<oft;
- }
中断处理函数:
点击(此处)折叠或打开
- void init_irq(void)
-
{
- //@设置GPIO为中断
- // S2,S3对应的2根引脚设为中断引脚 EINT0,ENT2
- GPFCON &= ~(GPF0_msk | GPF2_msk);
- GPFCON |= GPF0_eint | GPF2_eint;
- // S4对应的引脚设为中断引脚EINT11
- GPGCON &= ~GPG3_msk;
- GPGCON |= GPG3_eint;
- //@设置外部中断属性,子中断属性
- // 对于EINT11,需要在EINTMASK寄存器中使能它
- EINTMASK &= ~(1<<11);
- //@设置优先级
- //@使能中断
- // EINT0、EINT2、EINT8_23使能
- INTMSK &= (~(1<<0)) & (~(1<<2)) & (~(1<<5));
- }