I.MX6UL 中断浅析一

要了解中断首先要明白几个点

2.中断向量表

3.中断管理

4.中断服务函数

  • 中断向量表

中断向量表是一系列中断服务程序入口地址组成的表。

主要存放中断服务函数的入口地址。

比如一般 ARM 处理器都是从地址 0X00000000 开始执行指令的,那么中断向量表就是从 0X00000000 开始存放的。

如果将程序下载到其他位置还要进行中断向量表偏移

比如我们将程序下载到0X8780 0000,所以我们要设置中断向量表偏移位0X8780 0000

  • 中断管理

中断管理,IMX6UL 使用GIC( general interrupt controller)作为中断管理,STM32使用NVIC

  • 中断服务函数

我们使用中断的目的就是为了使用中断服务函数,当中断发生以后中断服务函数就会被调用,我们要处理的工作就可以放到中断服务函数中去完成。

I.MX6UL 中断简介

1.中断向量表

当CPU发生中断后,CPU会到指定位置运行程序,比如当复位中断发生时,CPU就到0x00处执行代码,

当外部中断IRQ发生时,CPU就会到0X18处执行程序。

因此我们以要事先在这些地址写好跳转指令,比如我们在0x18处写上ldr pc, =IRQ_Handler那么当IRQ发生时,CPU执行0x18处的代码,而0x18处的代码是跳转到   IRQ_Handler函数,因此IRQ即为外部中断的服务函数。

I.MX6UL 中断浅析一_第1张图片

①、复位中断(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如下图

I.MX6UL 中断浅析一_第2张图片

I.MX6UL 中断浅析一_第3张图片 其中前32个中断是CPU接口专用的。

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 的优先级,也就是设置外设优先级。

I.MX6Ul复位中断服务函数编写

1.中断向量表我们在上边已经编写完毕

接下来编写复位中断服务函数

Reset_Handler:

    ...

    ...

    ...

1.关闭I.cache和D.cache

2.设置中断向量偏移

3.设置处理器9种工作模式的SP指针。

4.清楚bss段

5.跳到C函数

CP15协处理器

MRC读CP15协处理器

MCR写CP15协处理器

指令格式:

MCR{cond} p15, , , , ,
cond:指令执行的条件码,如果忽略的话就表示无条件执行。
opc1:协处理器要执行的操作码。
Rt:ARM 源寄存器,要写入到 CP15 寄存器的数据就保存在此寄存器中。
CRn :CP15 协处理器的目标寄存器。
CRm: :协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将
CRm 设置为 C0,否则结果不可预测。
opc2 :可选的协处理器特定操作码,当不需要的时候要设置为 0。

MRC 的指令格式和 MCR 一样,只不过在 MRC 指令中 Rt 就是目标寄存器,也就是从CP15 指定寄存器读出来的数据会保存在 Rt 中。而 CRn 就是源寄存器,也就是要读取的写处理器寄存器。

假如我们要将 CP15 中 C0 寄存器的值读取到 R0 寄存器中,那么就可以使用如下命令:

MRC p15, 0, r0, c0, c0, 0

其中SCTLR寄存器比较重要

第0位控制着MMu的打开和关闭

第1位控制对齐

第2位控制D cache

I.MX6UL 中断浅析一_第4张图片

第11位控制分支预测

第12位控制I cache  

I.MX6UL 中断浅析一_第5张图片

 


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寄存器

I.MX6UL 中断浅析一_第6张图片

	/* 汇编版本设置中断向量表偏移 */
	ldr r0, =0X87800000

	dsb//数据同步指令保证数据执行完毕
	isb//指令同步指令保证指令执行完毕
	mcr p15, 0, r0, c12, c0, 0
	dsb
	isb

设置SP指针

CPSR寄存器的M[4:0]位位处理器模式控制位

I.MX6UL 中断浅析一_第7张图片

/* 设置各个模式下的栈指针,
	 * 注意: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函数 			 	*/

 

 

 

你可能感兴趣的:(I.MX6UL+Linux)