引言
中断系统是一个处理器重要的组成部分,中断系统极大的提高了 CPU 的执行效率,在学习
STM32 的时候就经常用到中断。本章就通过与 STM32 的对比来学习一下 Cortex-A7(I.MX6U)
中断系统和 Cortex-M(STM32)中断系统的异同,同时,本章会将 I.MX6U 的一个 IO 作为输入中
断,借此来讲解如何对 I.MX6U 的中断系统进行编程。
9.1 Cortex-A7****中断系统详解
9.1.1 STM32****中断系统回顾
STM32 的中断系统主要有以下几个关键点:
①、中断向量表。
②、NVIC(内嵌向量中断控制器)。
③、中断使能。
④、中断服务函数。
1**、中断向量表**
中断向量表是一个表,这个表里面存放的是中断向量。中断服务程序的入口地址或存放中
断服务程序的首地址成为中断向量,因此中断向量表是一系列中断服务程序入口地址组成的表。
这些中断服务程序(函数)在中断向量表中的位置是由半导体厂商定好的,当某个中断被触发以
后就会自动跳转到中断向量表中对应的中断服务程序(函数)入口地址处。中断向量表在整个程
序的最前面,比如 STM32F103 的中断向量表如下所示:
示例代码 9.1.1.1 STM32F103 中断向量表
1 __Vectors DCD __initial_sp
**;**Top of Stack
2 DCD Reset_Handler **;**Reset Handler
3 DCD NMI_Handler **;**NMI Handler
4 DCD HardFault_Handler **;**Hard Fault Handler
5 DCD MemManage_Handler **;**MPU Fault Handler
6 DCD BusFault_Handler **;**Bus Fault Handler
7 DCD UsageFault_Handler **;**Usage Fault Handler
8 DCD 0 **;**Reserved
9 DCD 0 **;**Reserved
10 DCD 0 **;**Reserved
11 DCD 0 **;**Reserved
12 DCD SVC_Handler **;**SVCall Handler
13 DCD DebugMon_Handler **;**Debug Monitor Handler
14 DCD 0 **;**Reserved
15 DCD PendSV_Handler **;**PendSV Handler
16 DCD SysTick_Handler **;**SysTick Handler
17
18 **;**External Interrupts
19 DCD WWDG_IRQHandler **;**Window Watchdog
20 DCD PVD_IRQHandler **;**PVD through EXTI Line detect
21 DCD TAMPER_IRQHandler **;**Tamper
22 DCD RTC_IRQHandler **;**RTC
23 DCD
FLASH_IRQHandler **;**Flash
24
25 /* 省略掉其它代码 */
26
27DCD DMA2_Channel4_5_IRQHandler **;**DMA2 Channel4 **&l5
28 __Vectors_End
"示例代码 17.1.1.1"就是 STM32F103 的中断向量表,中断向量表都是链接到代码的最
前面,比如一般 ARM 处理器都是从地址 0X00000000 开始执行指令的,那么中断向量表就是
从 0X00000000 开始存放的。"示例代码 17.1.1.1"中第 1 行的"__initial_sp"就是第一条中断
向量,存放的是栈顶指针,接下来是第 2 行复位中断复位函数 Reset_Handler 的入口地址,依
次类推,直到第 27 行的最后一个中断服务函数 DMA2_Channel4_5_IRQHandler 的入口地址,
这样 STM32F103 的中断向量表就建好了。
我们说 ARM 处理器都是从地址 0X00000000 开始运行的,但是我们学习 STM32 的时候
代码是下载到 0X8000000 开始的存储区域中。因此中断向量表是存放到 0X8000000 地址处
的,而不是 0X00000000,这样不是就出错了吗?为了解决这个问题,Cortex-M 架构引入了一
个新的概念------中断向量表偏移,通过中断向量表偏移就可以将中断向量表存放到任意地址
处,中断向量表偏移配置在函数 SystemInit 中完成,通过向 SCB_VTOR 寄存器写入新的中断
向量表首地址即可,代码如下所示:
示例代码 9.1.1.2 STM32F103 中断向量表偏移
1 void SystemInit (void)
2 {
3
RCC->CR |= (uint32_t)0x00000001;
4
5
/* 省略其它代码 */
6
7 #ifdef VECT_TAB_SRAM
8
SCB->**VTOR =SRAM_BASE |VECT_TAB_OFFSET;
9 #else
10
SCB->VTOR =FLASH_BASE |VECT_TAB_OFFSET;
11 #endif
12 }
第 8 行和第 10 行就是设置中断向量表偏移,第 8 行是将中断向量表设置到 RAM 中,第
10 行是将中断向量表设置到 ROM 中,基本都是将中断向量表设置到 ROM 中,也就是地址
0X8000000 处。第 10 行用到了 FALSH_BASE 和 VECT_TAB_OFFSET,这两个都是宏,定义
如下所示:
#define FLASH_BASE ((uint32_t)0x08000000)
#define VECT_TAB_OFFSET 0x0
因此第 10 行的代码就是:SCB->VTOR=0X080000000,中断向量表偏移设置完成。通过上
面的讲解我们了解了两个跟 STM32 中断有关的概念:中断向量表和中断向量表偏移,那么这个
跟 I.MX6U 有什么关系呢?因为 I.MX6U 所使用的 Cortex-A7 内核也有中断向量表和中断向量
表偏移,而且其含义和 STM32 是一模一样的!只是用到的寄存器不同而已,概念完全相同!
2、NVIC(内嵌向量中断控制器)
中断系统得有个管理机构,对于 STM32 这种 Cortex-M 内核的单片机来说这个管理机构叫
做 NVIC,全称叫做 Nested Vectored Interrupt Controller。关于 NVIC 本教程不作详细的讲解,既
然 Cortex-M 内核有个中断系统的管理机构---NVIC,那么 I.MX6U 所使用的 Cortex-A7 内核是
不是也有个中断系统管理机构?答案是肯定的,不过 Cortex-A 内核的中断管理机构不叫做
NVIC,而是叫做 GIC,全称是 general interrupt controller,后面我们会详细的讲解 Cortex-A 内
核的 GIC。
3、中断使能
要使用某个外设的中断,肯定要先使能这个外设的中断,以 STM32F103 的 PE2 这个 IO 为
例,假如我们要使用 PE2 的输入中断肯定要使用如下代码来使能对应的中断:
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
//子优先级 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
上述代码就是使能 PE2 对应的 EXTI2 中断,同理,如果要使用 I.MX6U 的某个中断的话也
需要使能其对应的中断。
4、中断服务函数
我们使用中断的目的就是为了使用中断服务函数,当中断发生以后中断服务函数就会被调
用,我们要处理的工作就可以放到中断服务函数中去完成。同样以 STM32F103 的 PE2 为例,
其中断服务函数如下所示:
/* 外部中断 2 服务程序 */
void EXTI2_IRQHandler(void)
{
/* 中断处理代码 */
}
当 PE2 引脚的中断触发以后就会调用其对应的中断处理函数 EXTI2_IRQHandler,我们可
以在函数 EXTI2_IRQHandler 中添加中断处理代码。同理,I.MX6U 也有中断服务函数,当某个
外设中断发生以后就会调用其对应的中断服务函数。
通过对 STM32 中断系统的回顾,我们知道了 Cortex-M 内核的中断处理过程,那么 Cortex
A 内核的中断处理过程是否是一样的,有什么异同呢?接下来我们带着这样的疑问来学习
Cortex-A7 内核的中断系统。
9.1.2 Cortex-A7****中断系统简介
跟 STM32 一样,Cortex-A7 也有中断向量表,中断向量表也是在代码的最前面。Cortex
A7 内核有 8 个异常中断,这 8 个异常中断的中断向量表如表 9.1.2.1 所示:
表 9.1.2.1 Cortex-A7 中断向量表
中断向量表里面都是中断服务函数的入口地址,因此一款芯片有什么中断都是可以从中断
向量表看出来的。从表 9.1.2.1 中可以看出,Cortex-A7 一共有 8 个中断,而且还有一个中断向
量未使用,实际只有 7 个中断。和"示例代码 9.1.1.1"中的 STM32F103 中断向量表比起来少
了很多!难道一个能跑 Linux 的芯片只有这 7 个中断?明显不可能的!那类似 STM32 中的
EXTI9_5_IRQHandler、TIM2_IRQHandler 这样的中断向量在哪里?I2C、SPI、定时器等等的中
断怎么处理呢?这个就是 Cortex-A 和 Cotex-M 在中断向量表这一块的区别,对于 Cortex-M 内
核来说,中断向量表列举出了一款芯片所有的中断向量,包括芯片外设的所有中断。对于 Cotex
A 内核来说并没有这么做,在表 9.1.2.1 中有个 IRQ 中断, Cortex-A 内核 CPU 的所有外部中
断都属于这个 IRQ 中断,当任意一个外部中断发生的时候都会触发 IRQ 中断。在 IRQ 中断服
务函数里面就可以读取指定的寄存器来判断发生的具体是什么中断,进而根据具体的中断做出
相应的处理。这些外部中断和 IRQ 中断的关系如图 9.1.2.1 所示:

图 9.1.2.1 外部中断和 IRQ 中断关系
在图 9.1.2.1 中,左侧的 Software0_IRQn~PMU_IRQ2_IRQ 这些都是 I.MX6U 的中断,他
们都属于 IRQ 中断。当图 9.1.2.1 左侧这些中断中任意一个发生的时候 IRQ 中断都会被触发,
所以我们需要在 IRQ 中断服务函数中判断究竟是左侧的哪个中断发生了,然后再做出具体的处
理。
在表 9.1.2.1 中一共有 7 个中断,简单介绍一下这 7 个中断:
①、复位中断(Rest),CPU 复位以后就会进入复位中断,我们可以在复位中断服务函数里面
做一些初始化工作,比如初始化 SP 指针、DDR 等等。
②、未定义指令中断(Undefined Instruction),如果指令不能识别的话就会产生此中断。
③、软中断(Software Interrupt,SWI),由 SWI 指令引起的中断,Linux 的系统调用会用 SWI
指令来引起软中断,通过软中断来陷入到内核空间。
④、指令预取中止中断(Prefetch Abort),预取指令的出错的时候会产生此中断。
⑤、数据访问中止中断(Data Abort),访问数据出错的时候会产生此中断。
⑥、IRQ 中断(IRQ Interrupt),外部中断,前面已经说了,芯片内部的外设中断都会引起此
中断的发生。
⑦、FIQ 中断(FIQ Interrupt),快速中断,如果需要快速处理中断的话就可以使用此中断。
在上面的 7 个中断中,我们常用的就是复位中断和 IRQ 中断,所以我们需要编写这两个中
断的中断服务函数,稍后我们会讲解如何编写对应的中断服务函数。首先我们要根据表 9.1.2.1
的内容来创建中断向量表,中断向量表处于程序最开始的地方,比如我们前面例程的 start.S 文
件最前面,中断向量表如下:
示例代码 9.1.1.1 Cortex-A 向量表模板
1 .global _start
/* 全局标号
*/
2
3 _start:
4
ldr pc**, =Reset_Handler /* 复位中断
*/
5
ldr pc, =Undefined_Handler /* 未定义指令中断
*/
6
ldr pc, =SVC_Handler /* SVC(Supervisor)中断
*/
7
ldr pc, =PrefAbort_Handler /* 预取终止中断 */
8
ldr pc, =DataAbort_Handler /* 数据终止中断 */
9
ldr pc, =NotUsed_Handler /* 未使用中断 */
10 ldr pc, =IRQ_Handler /* IRQ 中断 */
11
ldr pc, =FIQ_Handler /* FIQ(快速中断)未定义中断 */
12
13 /* 复位中断 */
14 Reset_Handler:**
15
/* 复位中断具体处理过程 */
16
17 /* 未定义中断 */
18 Undefined_Handler**:**
19
ldr r0**, =Undefined_Handler
20
bx r0
21
22 /* SVC 中断 */
23 SVC_Handler:**
24
ldr r0**, =SVC_Handler
25
bx r0
26
27 /* 预取终止中断 */
28 PrefAbort_Handler:**
29
ldr r0**, =PrefAbort_Handler
30
bx r0
31
32 /* 数据终止中断 */
33 DataAbort_Handler:**
34
ldr r0**, =DataAbort_Handler
35
bx r0
36
37 /* 未使用的中断 */
38 NotUsed_Handler:**
39
40
ldr r0**, =NotUsed_Handler
41
bx r0
42
43 /* IRQ 中断!重点!!!!! */
44 IRQ_Handler:**
45
/* 复位中断具体处理过程 */
46
47 /* FIQ 中断 */
48 FIQ_Handler**:**
49
ldr r0**, =**FIQ_Handler
50
bx r0
第 4 到 11 行是中断向量表,当指定的中断发生以后就会调用对应的中断复位函数,比如
复位中断发生以后就会执行第 4 行代码,也就是调用函数 Reset_Handler,函数 Reset_Handler
就是复位中断的中断复位函数,其它的中断同理。
第 14 到 50 行就是对应的中断服务函数,中断服务函数都是用汇编编写的,我们实际需要
编写的只有复位中断服务函数 Reset_Handler 和 IRQ 中断服务函数 IRQ_Handler,其它的中断本
教程没有用到,所以都是死循环。在编写复位中断复位函数和 IRQ 中断服务函数之前我们还需
要了解一些其它的知识,否则的话就没法编写。