中断控制器
此为ARM终端流程。
R14是LR,R15为PC寄存器。中断发生时,R14得到R15的备份,而在中断结束返回时,把R14中的值给R15.
其中R13-R15都有子寄存器,也就是说,在进入中断后,不需要单独给他们进行备份(FIR是R8-R15)
除了这些寄存器,每个工作模式下还有一个CPSR寄存器,保存当前程序状态。下图
中断处理过程:
总而言之,中断处理应该用这样的顺序
(4)根据具体中断,设置具体外设
(5)把对应的MASK寄存器的对应位设置为0
(6)确定是FIR还是IRQ
(7)把CPSR寄存器的I-bit或者F-bit设置为0以使能中断
PRIORITY寄存器决定优先级,由6个一级仲裁器(arbiter)和1个二级仲裁器组成
注意:不属于一个仲裁者的不能比大小和优先级
一下为代码例程,main函数什么也没做,就不提了。
@****************************************************************************** @ File:head.S @ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数 @****************************************************************************** .extern main .text .global _start _start: @****************************************************************************** @ 异常向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用 @****************************************************************************** b Reset @ 0x04: 未定义指令中止模式的向量地址 HandleUndef: b HandleUndef @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式 HandleSWI: b HandleSWI @ 0x0c: 指令预取终止导致的异常的向量地址 HandlePrefetchAbort: b HandlePrefetchAbort @ 0x10: 数据访问终止导致的异常的向量地址 HandleDataAbort: b HandleDataAbort @ 0x14: 保留 HandleNotUsed: b HandleNotUsed @ 0x18: 中断模式的向量地址 b HandleIRQ @ 0x1c: 快中断模式的向量地址 HandleFIQ: b HandleFIQ Reset: ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈 bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启 msr cpsr_c, #0xd2 @ 进入中断模式 ldr sp, =3072 @ 设置中断模式栈指针 msr cpsr_c, #0xd3 @ 进入管理模式 ldr sp, =4096 @ 设置管理模式栈指针, @ 其实复位之后,CPU就处于管理模式, @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略 bl init_led @ 初始化LED的GPIO管脚 bl init_irq @ 调用中断初始化函数,在init.c中 msr cpsr_c, #0x53 @ 设置I-bit=0,开IRQ中断 ldr lr, =halt_loop @ 设置返回地址 ldr pc, =main @ 调用main函数 halt_loop: b halt_loop HandleIRQ: sub lr, lr, #4 @ 计算返回地址 stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器 @ 注意,此时的sp是中断模式的sp @ 初始值是上面设置的3072 ldr lr, =int_return @ 设置调用ISR即EINT_Handle函数后的返回地址 ldr pc, =EINT_Handle @ 调用中断服务函数,在interrupt.c中 int_return: ldmia sp!, { r0-r12,pc }^ @ 中断返回, ^表示将spsr的值复制到cpsr
init.c:
/* * init.c: 进行一些初始化 */ #include "s3c24xx.h" /* * LED1,LED2,LED4对应GPB5、GPB6、GPB7、GPB8 */ #define GPB5_out (1<<(5*2)) #define GPB6_out (1<<(6*2)) #define GPB7_out (1<<(7*2)) #define GPB8_out (1<<(8*2)) #define GPB5_msk (3<<(5*2)) #define GPB6_msk (3<<(6*2)) #define GPB7_msk (3<<(7*2)) #define GPB8_msk (3<<(8*2)) /* * K1,K2,K3,K4对应GPG0,GPG3,GPG5,GPG6 * 中断引脚为EINT8,EINT11,EINT13,EINT14 */ #define GPG0_int (0x2<<(0*2)) #define GPG3_int (0x2<<(3*2)) #define GPG5_int (0x2<<(5*2)) #define GPG6_int (0x2<<(6*2)) #define GPG0_msk (3<<(0*2)) #define GPG3_msk (3<<(3*2)) #define GPG5_msk (3<<(5*2)) #define GPG6_msk (3<<(6*2)) /* * 关闭WATCHDOG,否则CPU会不断重启 */ void disable_watch_dog(void) { WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可 } void init_led(void) { // LED1,LED2,LED3,LED4对应的4根引脚设为输出 GPBCON &= ~(GPB5_msk | GPB6_msk | GPB7_msk | GPB8_msk); GPBCON |= GPB5_out | GPB6_out | GPB7_out | GPB8_out; } /* * 初始化GPIO引脚为外部中断 * GPIO引脚用作外部中断时,默认为低电平触发、IRQ方式(不用设置INTMOD) */ void init_irq( ) { // K1,K2,K3,K4对应的4根引脚设为中断功能 GPGCON &= ~(GPG0_msk | GPG3_msk | GPG5_msk | GPG6_msk); GPGCON |= GPG0_int | GPG3_int | GPG5_int | GPG6_int; // 对于EINT8,11,13,14,需要在EINTMASK寄存器中使能它 EINTMASK &= ~((1<<8) | (1<<11) | (1<<13) | (1<<14)); /* * 看2440手册Figure 14-2. Priority Generating Block * EINT8~EINT23的优先级是一样的 * 所以不用设置PRIORITY了 */ // PRIORITY = (PRIORITY & ((~0x01) | ~(0x3<<7))); // bit5是EINT8~EINT23的总开关 INTMSK &= ~(1<<5); }
interrupt.c:
#include "s3c24xx.h" void EINT_Handle() { unsigned long oft = INTOFFSET; /* oft应该为5, INTMSK的bit5是EINT8~EINT23的总开关 */ unsigned long val = EINTPEND; /* * K1,K2,K3,K4对应GPG0,GPG3,GPG5,GPG6 * 即 EINT8,EINT11,EINT13,EINT14 * 它们共享INTMSK的bit5 * oft都是5(对应INTMSK寄存器) * 需要读EINTPEND确定是发生的是哪个中断 */ GPBDAT |= (0xF<<5); // 所有LED熄灭 if (val & (1<<8)) { // K1被按下 GPBDAT &= ~(1<<5); // LED1点亮 } if (val & (1<<11)) { // K2被按下 GPBDAT &= ~(1<<6); // LED2点亮 } if (val & (1<<13)) { // K3被按下 GPBDAT &= ~(1<<7); // LED3点亮 } if (val & (1<<14)) { // K4被按下 GPBDAT &= ~(1<<8); // LED4点亮 } //清中断 EINTPEND = (1<<8) | (1<<11) | (1<<13) | (1<<14); SRCPND = 1<<oft; INTPND = 1<<oft; }
posted on 2013-10-06 10:59 desert-camel 阅读(703) 评论(0) 编辑 收藏 举报