I.MX6U-ALPHA开发板(GPIO中断实验)

一、回顾STM32中断系统

​ 1、STM32中断向量表
​ ARM芯片从0X00000000开始运行,执行指令。在程序开始的地方存放着中断向量表。中断向量表主要功能是描述中断对应的中断服务函数。
​ 对于STM32来说代码最开始的地址存放堆栈栈顶指针。

2、中断向量偏移
一般ARM从0X000000000地址开始运行,对于STM32我们设置连接首地址为0X8000000。
如果代码一定要从0X8000000开始运行,那么需要告诉一下soc内核。也就是设置中断向量偏移。设置SCB的VTOR寄存器为新的中断向量表起始地址即可。
3、NVIC中断控制器。
NVIC就是中断管理机构。使能和关闭指定的中断、设置中断优先级。
4、中断服务函数的编写
中断服务函数就是中断要做的事情。

二、Cortex-A7中断系统

​ 1、Cortex-A中断向量表
​ Cortex-A中断向量表有8个中断,其中重点关注IRQ。Cortex-A的中断向量表需要用户自己去定义。
​ 2、中断向量偏移
​ 我们的裸机历程都是从0X87800000开始的,因此要设置中断向量偏移。
​ 3、GIC中断控制器。
​ 同NVIC一样,GIC用于管理Cortex-A的中断。GIC提供了开关中断,设置中断优先级。
​ 4、IMX6U中断号
​ 为了区分不同的中断,引入了中断号。ID0ID15是给SGI,ID16ID31是给PPI。剩下的ID32~1019给SPI,也就是按键中断、串口中断。。。。
​ 6ULL支持128个中断。
​ 4、中断服务函数的编写
​ 一个是IRQ中断服务函数的编写,另一个就是在IRQ中断服务函数里面去查找并运行的具体的外设中断服务函数,

三、GIC中断控制器

四、中断实验编写

​ 1、编写按键中断例程。
​ KEY0使用UART1_CTS这个IO。编写UART1_CTS的中断代码。
​ 2、修改start.S
​ 添加中断向量表,编写复位中断服务函数和IRQ中断服务函数。
​ 编写复位中断服务函数,内容如下:
​ ①、关闭I,D Cache和MMU。
​ ②、设置处理器9中工作模式下对应的SP指针。要使用中断那么必须设置IRQ模式下的SP指针。索性直接设置所有模式下的SP指针。
​ ③、清除bss段。
​ ④、跳到C函数,也就是main函数

3、CP15协处理器

MRC: 将 CP15 协处理器中的寄存器数据读到 ARM 寄存器中。
MRC 就是读 CP15 寄存器, MCR 就是写 CP15 寄存器, MCR 指令格式如下:

MCR{cond} p15, , , , ,

MRC p15, 0, r0, c0,c0,0
现在要关闭I,D ache和MMU,打开Cortex-A7参考手册到105页,找到SCTLR寄存器。也就是系统控制寄存器,此寄存器bit0用于打开和关闭MMU,bit1控制对齐,bit2控制D Cache的打开和关闭。Bit11用于控制分支预测。Bit12用于控制I Cache。
中断向量偏移设置
将新的中断向量表首地址写入到CP15协处理器的VBAR寄存器。
MCR{cond} p15, , , , ,
MRC p15,0,r0,c12,c0,0 //
MCR p15,0,r0,c12,c0,0

IRQ中断服务函数
mrc p15, 4, r1, c15, c0, 0 读取CP15的CBAR寄存器。CBAR寄存器保存了GIC控制器的寄存器组首地址。GIC寄存器组偏移0x10000x1fff为GIC的分发器。0x20000x3fff为CPU接口端。意味我们可以访问GIC控制器了!
代码中,R1寄存器吧保存着GIC控制器的CPU接口端基地址。读取CPU接口段的GICC_IAR寄存器的值保存到R0寄存器里面。可以从GICC_IAR的bit9~0读取中断ID,我们读取中断ID的目的就是为了得到对应的中断处理函数。
system_irqhandler就是具体的中断处理函数,此函数有一个参数,为GICC_IAR寄存器的值。
system_irqhandler处理完具体的中断以后,需要将对应的中断ID值写入到GICC_EOIR寄存器里面。

.global _start  				/* 全局标号 */

/*
 * 描述:	_start函数,首先是中断向量表的创建
 * 参考文档:ARM Cortex-A(armV7)编程手册V4.0.pdf P42,3 ARM Processor Modes and Registers(ARM处理器模型和寄存器)
 * 		 	ARM Cortex-A(armV7)编程手册V4.0.pdf P165 11.1.1 Exception priorities(异常)
 */
_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(快速中断)未定义中断 			*/

/* 复位中断 */	
Reset_Handler:

	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寄存器中	 				*/

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

	dsb
	isb
	mcr p15, 0, r0, c12, c0, 0
	dsb
	isb
#endif
    
	/* 设置各个模式下的栈指针,
	 * 注意: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,大小为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,大小为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函数 			 	*/

/* 未定义中断 */
Undefined_Handler:
	ldr r0, =Undefined_Handler
	bx r0

/* SVC中断 */
SVC_Handler:
	ldr r0, =SVC_Handler
	bx r0

/* 预取终止中断 */
PrefAbort_Handler:
	ldr r0, =PrefAbort_Handler	
	bx r0

/* 数据终止中断 */
DataAbort_Handler:
	ldr r0, =DataAbort_Handler
	bx r0

/* 未使用的中断 */
NotUsed_Handler:

	ldr r0, =NotUsed_Handler
	bx r0

/* IRQ中断!重点!!!!! */
IRQ_Handler:
	push {lr}					/* 保存lr地址 */
	push {r0-r3, r12}			/* 保存r0-r3,r12寄存器 */

	mrs r0, spsr				/* 读取spsr寄存器 */
	push {r0}					/* 保存spsr寄存器 */

	mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中
								* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49
								* Cortex-A7 Technical ReferenceManua.pdf P68 P138
								*/							
	add r1, r1, #0X2000			/* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */
	ldr r0, [r1, #0XC]			/* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,
								 * GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据
								 * 这个中断号来绝对调用哪个中断服务函数
								 */
	push {r0, r1}				/* 保存r0,r1 */
	
	cps #0x13					/* 进入SVC模式,允许其他中断再次进去 */
	
	push {lr}					/* 保存SVC模式的lr寄存器 */
	ldr r2, =system_irqhandler	/* 加载C语言中断处理函数到r2寄存器中*/
	blx r2						/* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */

	pop {lr}					/* 执行完C语言中断服务函数,lr出栈 */
	cps #0x12					/* 进入IRQ模式 */
	pop {r0, r1}				
	str r0, [r1, #0X10]			/* 中断执行完成,写EOIR */

	pop {r0}						
	msr spsr_cxsf, r0			/* 恢复spsr */

	pop {r0-r3, r12}			/* r0-r3,r12出栈 */
	pop {lr}					/* lr出栈 */
	subs pc, lr, #4				/* 将lr-4赋给pc */
	
	

/* FIQ中断 */
FIQ_Handler:

	ldr r0, =FIQ_Handler	
	bx r0									


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值