中断处理的过程:
①中断控制器汇集各类外设发出的中断信号,告诉CPU ;
②CPU保存当前程序的运行环境,调用中断服务程序(ISR)来处理这些中断
③在ISR中通过读取中断控制器、外设的相关寄存器来识别时哪个中断,并进行相应处理
④清除中断:通过读写中断控制器和外设的相关寄存器来实现
⑤最后恢复被中断程序的运行环境(恢复寄存器),继续执行
s3c2440的中断控制器结构如上图所示:
①request sources(without sub-register)中的中断源被触发后,SRCPND寄存器中相应位被置1,如果此中断没有被INTMSK寄存器屏蔽或者快速中断的话,它将被进一步处理。
② 对于request sources(with sub-register)中的中断源被触发后,SUBSRCPEND寄存器中的相应位被置1,如果此中断没有被INTSUBMSK寄存器屏蔽的话,它在 SRCPND寄存器中的相应位也被置1,以后的处理过程就和①步骤类似。
③如果被触发的中断中有快速中断,INTMOD寄存器中为1的位对应的中断是FIQ,则CPU进入快速中断模式进行处理
④ 对于一般的中断IRQ,可能同时有几个中断被触发,未被INTMSK寄存器屏蔽的中断经过比较后,选出优先级最高的中断,此中断在INTPND寄存器中的 相应位被置1,然后CPU进入中断模式进行处理。中断服务程序通过读取INTPND或者INTOFFSET来确定中断源。
技巧:到此为止,不难发现,这里用了一个很简单的思路:分类汇总。
Request sources(with sub-register):同种设备可能会对应几种中断,S3C2440采取的措施是:SRCPND存储的是所有大类的中断,具体这一大类中断中哪种类型的子中断发生了,需要再查询SUBSRCPND寄存器才能得到。
Request sources(without sub-register):这种类型的中断只有一种情况,当外部中断的触发条件满足时就产生中断,这类中断没有子类。
如何屏蔽中断请求:最简单的情况是将该中断屏蔽掉,因此这里就涉及到一个中断屏蔽寄存器,只要向该寄存器中某一位写1,就可以将改为对应的中断屏蔽掉。
INTMSK(需要屏蔽某一个子类中断)和INTMSK(需要屏蔽某一大类中断)。例如:寄存器INTMSK中有单独的位来屏蔽外部中断0~3,外部中断4~7是公用一位来屏蔽,主要原因是外部中断太多了,因此需要另外一寄存器EINTMASK来实现中断屏蔽。具体屏蔽哪一位,需要由寄存器EINTMASK来确定的。由上图可知,中断发生后,寄存器SRCPND中的相应位会置1,然后,如果该中断不被屏蔽,则寄存器INTPND中的相应位也会被置1。
中断模式寄存器INTMODE。
点击(此处)折叠或打开
- button.h
- #ifndef __BUTTON_H__
- #define __BUTTON_H__
- extern void Key_Init() ;
- extern int Key_Scan() ;
- #endif
- #ifndef __BUTTON_H__
button.c
点击(此处)折叠或打开
- #include "button.h"
- #include "config.h"
- #define KEY1 (2 << 2) //GPF1
- #define KEY2 (2 << 8) //GPF4
- #define KEY3 (2 << 4) //GPF2
- #define KEY4 (2 << 0) //GPF0
- void Key_Init(void)
- {
- rGPFCON &= ~((3 << 0) | (3 << 2) | (3 << 4) | (3 << 8)) ;
- rGPFCON |= KEY1 | KEY2 | KEY3 | KEY4 ;
- rGPFDAT |= (1 << 0) | (1 << 1) | (1 << 2 ) | (1 << 4) ;//将键盘对应的四个引脚置为高电平,完成初始化工作
- }
- int Key_Scan() //该函数在本次试验中没有用到
- {
- int keynum = 0 ;
- if((rGPFDAT & (0 << 1)) == 0 )
- {
- keynum = 1 ;
- }
- if((rGPFDAT & (0 << 4)) == 0 )
- {
- keynum = 2 ;
- }
- if((rGPFDAT & (0 << 2)) == 0 )
- {
- keynum = 3 ;
- }
- if((rGPFDAT & (1 << 0)) == 0 )
- {
- keynum = 4 ;
- }
- return keynum ;
- }
- #include "config.h"
中断执行流程:
系统启动后,系统进入管理模式(SVC模式,ARM处理器默认的),在发生中断后,ARM处理器会自动切换到外部中断模式(假设外部发送外部中断0,IRQ模式),每种工作模式都有对应一组可以访问的寄存器。
2.ARM处理器做了哪些事情,还有哪些事情需要用户编程来实现?
发生中断后,ARM处理器需要执行完当前指令,然后自动完成以下事情:
A.将当前程序状态寄存器CPSR保存到IRQ模式下的备份程序状态寄存器SPSR_irq中(执行中断返回时,其逆过程不能自动完成)。
B.将程序计数器PC值减4(这个地方要注意)存放到IRQ模式下的链接寄存器R14_irq中,即R14_irq=PC-4(执行完中断处理程序以后,需要返回的地址是发生中断的指令的下一条指令的地址,即PC-8处,然后将PC值设为0x00000018,接着就是访问异常中断向量表,查找到中断处理函数并执行,最后执行中断返回)。
C.最后将PC值强制设为0x00000018(这正是异常中断向量表中IRQ的入口地址)。
程序员需要做的事情是:
A.根据需要,有选择的保存寄存器R0~R12的值(一般是将上述寄存器的值入栈保护)。
B.中断发生后,ARM处理器自动将程序计数器PC的值设为0x00000018.因此,用户需要将中断处理函数放在这个地址处,一般是放置一条跳转指令,然后就可以找到中断处理函数。
C.计算程序的返回地址,因为ARM9处理器是5级流水线,但是程序的执行阶段是处在流水线的第三级。因此,程序寄存器PC始终指向当前正在执行指令的下两条指令处。
中断返回执行的操作是:(1)将被中断程序的处理器状态(寄存器的内容)恢复,即从IRQ模式恢复到SVC模式。(2)返回到发生异常中断的指令的下一条指令执行。 subs pc,lr,#4
此外,也可以利用入栈和出栈指令实现:
- subs lr, lr, #4
- stmfd {r0-r12,lr}
- .........
- ldmfd {r0-r12,lr}^
- stmfd {r0-r12,lr}
进入中断后,PC-4的值自动存储在R14_irq中,则将其减4即可得到实际的返回地址,然后将寄存器R0~R12和寄存器lr入栈(注意,此时是入的IRQ模式的堆栈,保存公用寄存器中用到的寄存器,返回恢复)。最后返回时将上述寄存器出栈即可,^表示SPSR_irq复制到SVC模式的寄存器CPSR中。注意,发生中断是,CPSR寄存器的值是由ARM处理器自动复制到IRQ模式下的SPSR_irq寄存器的,但是中断返回,需要用户手动恢复该寄存器的值。
使用_ _irq关键字时,编译器会自动完成对寄存器的保存以及中断返回地址的计算。