我们写的中断服务函数,在中断触发时会调用,实现这个调用就需要中断向量表。中断向量表保存的是中断服务程序的入口地址。
STM32代码是下载到0x8000000开始的区域,这就和中断向量表应该在的0x00000000不一样,那么就需要进行中断向量偏移。包括我用的I.MX6ULL,代码连接到的地址是0x87800000,所以也需要进行中断向量偏移。
STM32是Cortex-M内核,中断控制器为NVIC。I.MX6ULL是Cotex-A内核,中断控制器叫做GIC。
A7内核有8个异常中断(实际上只有七个),中断向量表如下,其中主要用到的是IRQ外部中断:
A7需要自己写中断向量表,另外需要设置中断向量偏移。
I.MX6ULL是ARMv7-A架构,使用的是GIC V2版本。GIC接到外部中断信号后会报给ARM内核。原理示意如下图。上报主要有四个信号,主要关注IRQ信号。GIC将众多中断源分为:
SPI共享中断:所有核心共享的中断,所有外部中断都属于SPI中断,所有的核心都能够处理。
PPI私有中断:每个核独有的中断,需要指定核心处理。
SGI软件中断:像寄存器GICD_SGIR写入数据触发,可以用于多核通信。
一个CPU最多支持1020个中断ID,ID0-1019,其中包含了SPI,PPI,SGI。其中ID0-ID15分配给SGI,ID16-ID31分配给PPI,ID32-ID1019分配给SPI。I.MX6ULL一共有128个SPI的中断ID,加上32个SGI/PPI的ID,一共有160个中断ID。在参考手册第3.2节可以查看每个中断ID对应的中断源和描述。
主要包含IRQ中断服务函数(汇编文件里面)和IRQ中断服务函数里查找并运行的外设中断服务函数(bsp里面)。
KEY0使用UART1_CTS这个IO。
①在_start后面添加中断向量表。
②编写复位中断服务函数:1)关闭I & D cache和MMU;2)设置处理器9种工作模式下对应的sp指针;3)清除bss段;4)跳到main.c。
③CP15协处理器。主要包含MRC和MCR指令,MRC将cp15的寄存器数据读入到ARM寄存器,MCR相反。MCR指令格式为:
MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
/*
cond为条件码,忽略表示无条件执行
opc1为协处理器要执行的操作码
Rt为ARM源寄存器
CRn为cp15的目标寄存器
CRm为协处理器种附加的目标寄存器或者源操作数寄存器,不需要就设为0
opc2为可选操作码,不要就设为0
*/
/*MRC指令格式一样,不过Rt是目标寄存器,CRn为源寄存器。*/
1)关闭I,D Cache和MMU:CP15中c1寄存器设置CRn=c1,opc1=0,CRm=c0,opc2=0,这时候c1就被选为SCTLR寄存器(系统控制寄存器)。该寄存器bit12为I Cache使能位,bit11为分支预测,bit2为D Cache使能位,bit0为MMU使能位。
2)中断向量偏移:c12寄存器CRn=c12,opc1=0,CRm=c0,opc2=0时为VBAR寄存器(向量表基地址寄存器)。由于代码链接起始地址为0x87800000,所以需要将该地址设置为VBAR也就是中断向量表起始地址。
首先需要了解GIC架构分为Distributor(分发器端-0x1000-0x1FFF),CPU Interface(CPU接口端-0x2000-0x3FFF)。在得到GIC基地址之后就可以根据这些偏移量访问GIC的各个寄存器。
cp15协处理器的c15寄存器,可以作为CBAR寄存器,通过如下命令获得GIC基地址:
MRC p15, 4, r1, c15, c0, 0
然后调用c语言函数system_irqhandler,处理中断。处理完成后将GICC_IAR的值写入GICC_EOIR寄存器,表示中断完成。
前面IRQ_Handler调用了system_irqhandler函数,所以需要进行编写。I.MX6ULL的160个中断源需要160个中断处理函数,这里建立一个数组,将中断处理函数作为数组元素,中断ID作为数组下标,由system_irqhandler进行调用。进行int.c和int.h的编写,int.h如下。
①首先要设置GPIO的中断触发方式,上升沿,下降沿,高低电平,边沿触发5种。由GPIO_ICR1或ICR2寄存器设置。对于KEY0触发的中断,由于按键按下时由1变0,所以需要设置为下降沿触发。
②使能GPIO对应的中断,设置GPIO_IMR寄存器,为1使能。
③处理完中断后需要清除中断标志位,也就是清除GPIO_ISR寄存器相应的位,该寄存器写1清零。
调用通用GPIO驱动。
GIC配置:
①使能相应的中断ID,GPIO_18对应的是67+32。(32是PPI和SGI占用的,在MCIMX6Y2.h中可以查到)
②设置中断优先级。
③注册该中断ID对应的中断服务函数。
注意点:确保中断向量表处于起始位置也即是0x87800000
KEY0外部中断控制BEEP: