2.中断向量表
3.中断管理
4.中断服务函数
中断向量表是一系列中断服务程序入口地址组成的表。
主要存放中断服务函数的入口地址。
比如一般 ARM 处理器都是从地址 0X00000000 开始执行指令的,那么中断向量表就是从 0X00000000 开始存放的。
如果将程序下载到其他位置还要进行中断向量表偏移。
比如我们将程序下载到0X8780 0000,所以我们要设置中断向量表偏移位0X8780 0000
中断管理,IMX6UL 使用GIC( general interrupt controller)作为中断管理,STM32使用NVIC
我们使用中断的目的就是为了使用中断服务函数,当中断发生以后中断服务函数就会被调用,我们要处理的工作就可以放到中断服务函数中去完成。
1.中断向量表
当CPU发生中断后,CPU会到指定位置运行程序,比如当复位中断发生时,CPU就到0x00处执行代码,
当外部中断IRQ发生时,CPU就会到0X18处执行程序。
因此我们以要事先在这些地址写好跳转指令,比如我们在0x18处写上ldr pc, =IRQ_Handler那么当IRQ发生时,CPU执行0x18处的代码,而0x18处的代码是跳转到 IRQ_Handler函数,因此IRQ即为外部中断的服务函数。
①、复位中断(Rest),CPU 复位以后就会进入复位中断,我们可以在复位中断服务函数里面做一些初始化工作,比如初始化 SP 指针、DDR 等等。
②、未定义指令中断(Undefined Instruction),如果指令不能识别的话就会产生此中断。
③、软中断(Software Interrupt,SWI),由 SWI 指令引起的中断,Linux 的系统调用会用 SWI指令来引起软中断,通过软中断来陷入到内核空间。
④、指令预取中止中断(Prefetch Abort),预取指令的出错的时候会产生此中断。
⑤、数据访问中止中断(Data Abort),访问数据出错的时候会产生此中断。
⑥、IRQ 中断(IRQ Interrupt),外部中断,芯片内部的外设中断都会引起此
中断的发生。
⑦、FIQ 中断(FIQ Interrupt),快速中断,如果需要快速处理中断的话就可以使用此中。
其中IRQ为外部中断,当有外部中断(如普通IO高电平中断,UART中断)发生时,都会触发 IRQ 中断。在 IRQ 中断服
务函数里面就可以读取指定的寄存器来判断发生的具体是什么中断。
程序编写如下,此时中断服务函数内皆为死循环,我们可以根据情况再做编写。
_start:
ldr pc, =Reset_Handler /* 复位中断 */
ldr pc, =Undefined_Handler /* 未定义中断 */
ldr pc, =SVC_Handler /* SVC(Supervisor)中断 */
ldr pc, =PrefAbort_Handler /* 预取终止中断 */
ldr pc, =DataAbort_Handler /* 数据终止中断 */
ldr pc, =NotUsed_Handler /* 未使用中断 */
ldr pc, =IRQ_Handler /* IRQ中断 */
ldr pc, =FIQ_Handler /* FIQ(快速中断)未定义中断 */
...
...
...
/* FIQ中断 */
IRQ_Handler:
ldr r0, =IRQ_Handler
bx r0
/* FIQ中断 */
FIQ_Handler:
ldr r0, =FIQ_Handler
bx r0
最常用的是IRQ中断和复位中断。
I.MX6UL共有128个IRQ如下图
0-15:软件中断
16-31:私有中断
2.中断控制器
I.MX6U 选择了 32 个优先级。在使用中断的时候需要初始化 GICC_PMR 寄存
器,此寄存器用来决定使用几级优先级。
GICC_PMR 寄存器只有低 8 位有效,这 8 位最多可以设置 256 个优先级,
11111111 256 个优先级。
11111110 128 个优先级。
11111100 64 个优先级。
11111000 32 个优先级
11110000 16 个优先级。
I.MX6U 支持 32 个优先级,所以 GICC_PMR 要设置为 0b11111000。
3.抢占优先级和子优先级尾数设置
抢占优先级和子优先级各占多少位是由寄存器 GICC_BPR 来决定的
寄存器GICC_BPR只有低3位有效,其值不同,抢占优先级和子优先级占用的位数也不同
0 [7:1] [0] 7 级抢占优先级,1 级子优先级。
1 [7:2] [1:0] 6 级抢占优先级,2 级子优先级。
2 [7:3] [2:0] 5 级抢占优先级,3 级子优先级。
3 [7:4] [3:0] 4 级抢占优先级,4 级子优先级。
4 [7:5] [4:0] 3 级抢占优先级,5 级子优先级。
5 [7:6] [5:0] 2 级抢占优先级,6 级子优先级。
6 [7:7] [6:0] 1 级抢占优先级,7 级子优先级。
7 无 [7:0] 0 级抢占优先级,8 级子优先级。
为了简单起见,一般将所有的中断优先级位都配置为抢占优先级,比如 I.MX6U 的优先级位数为 5(32 个优先级),所以可以设置 Binary point 为 2,表示 5 个优先级位全部为抢占优先级。
4.优先级设置
I.MX6U 一共有 32 个抢占优先级,数字越小优先级越高。具体要使用某个中断的时候就可以设置其优先级为 0~31。某个中断 ID 的中断优先级设置由寄存器D_IPRIORITYR 来完成,
使用寄存器 D_IPRIORITYR 的 bit7:4 来设置优先级,也就是说实际的优先级要左移 3 位。比如要设置
ID40 中断的优先级为 5,示例代码如下:
GICD_IPRIORITYR[40] = 5 << 3;
有关优先级设置的内容就讲解到这里,优先级设置主要有三部分:
①、设置寄存器 GICC_PMR,配置优先级个数,比如 I.MX6U 支持 32 级优先级
②、设置抢占优先级和子优先级位数,一般为了简单起见,会将所有的位数都设置为抢占优先级。
③、设置指定中断 ID 的优先级,也就是设置外设优先级。
1.中断向量表我们在上边已经编写完毕
接下来编写复位中断服务函数
Reset_Handler:
...
...
...
1.关闭I.cache和D.cache
2.设置中断向量偏移
3.设置处理器9种工作模式的SP指针。
4.清楚bss段
5.跳到C函数
CP15协处理器
MRC读CP15协处理器
MCR写CP15协处理器
指令格式:
MCR{cond} p15,
MRC 的指令格式和 MCR 一样,只不过在 MRC 指令中 Rt 就是目标寄存器,也就是从CP15 指定寄存器读出来的数据会保存在 Rt 中。而 CRn 就是源寄存器,也就是要读取的写处理器寄存器。
假如我们要将 CP15 中 C0 寄存器的值读取到 R0 寄存器中,那么就可以使用如下命令:
MRC p15, 0, r0, c0, c0, 0
其中SCTLR寄存器比较重要
第0位控制着MMu的打开和关闭
第1位控制对齐
第2位控制D cache
第11位控制分支预测
第12位控制I cache
cpsid i /* 关闭全局中断 */
/* 关闭I,DCache和MMU
* 采取读-改-写的方式。
*/
mrc p15, 0, r0, c1, c0, 0 /* 读取CP15的C1寄存器到R0中 */
bic r0, r0, #(0x1 << 12) /* 清除C1寄存器的bit12位(I位),关闭I Cache */
bic r0, r0, #(0x1 << 2) /* 清除C1寄存器的bit2(C位),关闭D Cache */
bic r0, r0, #0x2 /* 清除C1寄存器的bit1(A位),关闭对齐 */
bic r0, r0, #(0x1 << 11) /* 清除C1寄存器的bit11(Z位),关闭分支预测 */
bic r0, r0, #0x1 /* 清除C1寄存器的bit0(M位),关闭MMU */
mcr p15, 0, r0, c1, c0, 0 /* 将r0寄存器中的值写入到CP15的C1寄存器中 */
将中断向量首页地址写入到CP15协处理器的VBAR寄存器
/* 汇编版本设置中断向量表偏移 */
ldr r0, =0X87800000
dsb//数据同步指令保证数据执行完毕
isb//指令同步指令保证指令执行完毕
mcr p15, 0, r0, c12, c0, 0
dsb
isb
CPSR寄存器的M[4:0]位位处理器模式控制位
/* 设置各个模式下的栈指针,
* 注意:IMX6UL的堆栈是向下增长的!
* 堆栈指针地址一定要是4字节地址对齐的!!!
* DDR范围:0X80000000~0X9FFFFFFF
*/
/* 进入IRQ模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x12 /* r0或上0x13,表示使用IRQ模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0x80600000 /* 设置IRQ模式下的栈首地址为0X80600000-0X80400000,大小为2MB */
/* 进入SYS模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x1f /* r0或上0x13,表示使用SYS模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0x80400000 /* 设置SYS模式下的栈首地址为0X80400000-0X80200000,大小为2MB */
/* 进入SVC模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0X80200000 /* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */
cpsie i /* 打开全局中断 */
#if 0
/* 使能IRQ中断 */
mrs r0, cpsr /* 读取cpsr寄存器值到r0中 */
bic r0, r0, #0x80 /* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */
msr cpsr, r0 /* 将r0重新写入到cpsr中 */
#endif
b main /* 跳转到main函数 */