1 void init_irq( ) 2 { 3 // S2,S3对应的2根引脚设为中断引脚 EINT0,ENT2 4 GPFCON &= ~(GPF0_msk | GPF2_msk); 5 GPFCON |= GPF0_eint | GPF2_eint; 6 7 // S4对应的引脚设为中断引脚EINT11 8 GPGCON &= ~GPG3_msk; 9 GPGCON |= GPG3_eint; 10 11 // 对于EINT11,需要在EINTMASK寄存器中使能它 12 EINTMASK &= ~(1<<11); 13 14 /* 15 * 设定优先级: 16 * ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2 17 * 仲裁器1、6无需设置 18 * 最终: 19 * EINT0 > EINT2 > EINT11即K2 > K3 > K4 20 */ 21 PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ; 22 23 // EINT0、EINT2、EINT8_23使能 24 INTMSK &= (~(1<<0)) & (~(1<<2)) & (~(1<<5)); 25 }
上面就是韦东山的中断源代码:
第四行和第五行代码分析:把按键的引脚先变成中断引脚
第十二行:
EINTMASK &= ~(1<<11); 因为前3个中断是不能被masked的从第四个中断开始 当置0的时候是使能中断 这里是使能第十一个中断。 默认初始值是1
这个是对于外部中断使能
第二十一行:
PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ;
此实验用到了仲裁器0
ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2
第24行:
INTMSK &= (~(1<<0)) & (~(1<<2)) & (~(1<<5));
这个寄存器是对所有的中断 设置为0的时候是开启所有中断
下图中的函数是中断服务函数
1 void EINT_Handle() 2 { 3 unsigned long oft = INTOFFSET; 4 unsigned long val; 5 6 switch( oft ) 7 { 8 // S2被按下 9 case 0: 10 { 11 GPFDAT |= (0x7<<4); // 所有LED熄灭 12 GPFDAT &= ~(1<<4); // LED1点亮 13 break; 14 } 15 16 // S3被按下 17 case 2: 18 { 19 GPFDAT |= (0x7<<4); // 所有LED熄灭 20 GPFDAT &= ~(1<<5); // LED2点亮 21 break; 22 } 23 24 // K4被按下 25 case 5: 26 { 27 GPFDAT |= (0x7<<4); // 所有LED熄灭 28 GPFDAT &= ~(1<<6); // LED4点亮 29 break; 30 } 31 32 default: 33 break; 34 } 35 36 //清中断 37 if( oft == 5 ) 38 EINTPEND = (1<<11); // EINT8_23合用IRQ5 39 SRCPND = 1<<oft; 40 INTPND = 1<<oft; 41 }
INTOFFSET 里面的值显示了哪一个中断正在被执行
第三十八行 EINTPEND 如果清除(置1)这里面的位 则清除了中断
第三十九行和第四十行都是一样的清除中断
总结
下面总结一下如何写一个中断的程序:
1. 给中断设置栈 然后进入管理模式
2. 初始化中断 (EINTMSK和INTMSK 两个寄存器都要设置)还要设置优先级
3. 开总中断
4. 进入中断服务函数之后 先保存数据进栈 和计算返回地址
5. 然后执行服务函数里面的程序
6. 之后清除PEND寄存器里面的标志位
7. 退出中断之后POP栈内保存的值
8. 告知PC 现在寄存器的状态(ldmia sp!, { r0-r12,pc }^)