一、中断控制器
在处理IRQ的时候,会将CPSR写入IRQ_SPSR,然后将CPU切换为IRQ模式,把状态改成ARM状态,把I位写成1禁止全部的IRQ,所以中断这样是我们不想要的。4412是一个四核的CPU,在发送中断前要确定发送给哪个CPU。任何一个外部设备都能触发FIQ和IRQ,只是FIQ速度快一些。还有很多问题,为了解决这些问题,三星公司在设计时就加了一个中断控制器
中断优先级只能决定排队的先后,但是高优先级的不能打断低优先级的中断。
中断控制器的作用:
多个中断同时产生时可对这些中断挂起排队,然后按照优先级依次发送给CPU处理
可以为每一个中断分配一个优先级
一个中断正在处理时若又产生其它中断,可将新的中断挂起,待CPU空闲时再发送
可以为每一个中断选择一个CPU处理
可以为每一个中断选择一个中断类型(FIQ或IRQ)
CPU接收到中断信号后并不能区分是哪个外设产生的,此时CPU可查询中断控制器来获取当前的中断信号是由哪个硬件产生的,然后再进行对应的处理
可以打开或禁止每一个中断
......
二、Exynos4412下的中断控制器
它支持三种类型的中断
SGI:软中断,不是ARM架构里的那个SWI,这个是多核处理器之间用的比较多,一般操作系统内核中会使用。
PPI:私有外设中断,只能发给某个确定的CPU
SPI:共享外设中断,可以发送给任何一个CPU去执行。
可以编程设置:
安全模式和非安全模式:安全状态可以触发FIQ和IRQ,非安全状态只可以触发IRQ,咱们一般只使用IRQ
配置中断优先级。
打开或者关闭一些中断。
接收中断的处理器。
中断状态说明:可以选择这几种状态
中断控制器(GIC)有编号0-159,共160个中断信号ID,编号只是起到标识作用,没有其它作用
[15:0]分配给了SGI,[31:16]分配给了PPI,[159:32]分配给了SPI
本次使用的GPIO中断是EINT[9],也就是外部中断9.
三、中断控制器寄存器详解
真正做开发时这些中断控制器的配置其实是不需要自己写的,ARM-cortex-A9系列的控制器一般都要跑个Linux或者Android操作系统,而这些东西操作系统都为我们写好了,直接调就行。但现在是裸机开发,所以把相关的寄存器找到配置一次
写0忽略全部的外部中断信号,并且不将挂起的中断转发到CPU的接口
写1 监控所有的外部中断,并将挂起的中断转发到CPU的接口
相当于GIC(中断控制器)的总开关。
ICDISER_CPU寄存器的作用:寄存器接收到中断信号,通过配置该寄存器对应的位,控制该中断信号发送或不发送给CPU。
5个32位寄存器控制这160个中断的开和关。
下面这个寄存器是ICDIPTR_CPU,它的作用是为每一个中断选择处理他的CPU。
哪一位置1,中断信号就发给哪个CPU处理。但4412是一个四核的CPU,所以高四位是没有用的。
一共需要40个寄存器来管理这160个中断归属于哪一个CPU处理
例:想把6号中断交给CPU2处理,则把偏移地址为0x804的寄存器的[23:16]写为00000100即可。
想把57号中断交给CPU0处理,则把偏移地址为0x838的寄存器的[15:8]写为00000001即可。
正常上电默认使用CPU0,想使用其它的CPU要设置很多CPU相关的寄存器。
ICCICR_CPUn寄存器是中断控制器和CPU之间的接口,他就像一个开关,用哪个CPU就要打开哪个。
不设置FIQ和IRQ,会默认是给IRQ,所以我们就不设置了,因为本来就是要做IRQ的实验
然后因为只有一个中断所以也不需要设置中断优先级。
所以我们一共需要设置下面4个寄存器就可以了
ICDDCR:中断总开关
ICDISER:每个中断的小开关,我们要打开57号中断的开关
ICDIPTR:确定我们的57号中断发送给哪个CPU处理,暂时只能是CPU0
ICCICR:中断控制器和CPU之间的接口
四、中断控制器编程
#include "exynos_4412.h"
int main()
{
/*外设层次 —— 让外部的硬件控制器产生一个中断信号并发送给中断控制器*/
/*将GPX1_1设置成中断功能*/
GPX1.CON = GPX1.CON | (0xF << 4);
/*设置GPX1_1中断触发方式:下降沿触发*/
EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
/*使能GPX1_1的中断功能*/
EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));
/*中断控制器层次 —— 让中断控制器接收外设发来的中断信号并对其进行管理然后再转发给一个合适的CPU去处理*/
/*全局使能中断控制器,使其能够接收外部设备产生的中断信号并转发给CPU接口*/
ICDDCR = ICDDCR | 1;
/*在中断控制器中使能57号中断,使中断控制器在接收到57号中断后,能将其进一步转发到CPU接口*/
ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);
/*选择CPU0来处理57号中断*/
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0x1 << 8);
/*将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/
CPU0.ICCICR = CPU0.ICCICR | 1;
return 0;
}