五、代码实现
要处理中断异常,必须安装异常向量表,异常的处理流程可以参考前面的文章《6. 从0开始学ARM-异常、异常向量表、swi》
1. 异常向量表基址
异常向量表地址是可以修改的,比如uboot在启动的时候,会从flash中搬运代码到RAM中,而flash的异常向量表地址和ram的地址肯定不一样,所以搬运完代码后,就必须要修改对应的异常向量表地址。
修改异常向量表的地址的需要借助协处理器指令mcr:
ldr r0,=0x40008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register
上述命令是将地址0x40008000设置为异常向量表的地址,关于mcr指令,我们没有必要深究,知道即可。
RAM中异常向量表地址我们选用的是0x40008000,以下是exynos4412 地址空间分布。
exynos4412 地址分布2. 异常向量表安装.text
.global _start
_start:
b reset
ldr pc,_undefined_instruction
ldr pc,_software_interrupt
ldr pc,_prefetch_abort
ldr pc,_data_abort
ldr pc,_not_used
ldr pc,=irq_handler
ldr pc,_fiq
reset:
ldr r0,=0x40008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register
init_stack:
//初始化栈
……
b main //跳转至c的main函数
irq_handler: //中断入口函数
sub lr,lr,#4
stmfd sp!,{r0-r12,lr}
.weak do_irq
bl do_irq
ldmfd sp!,{r0-r12,pc}^
stacktop: .word stack+4*512//栈顶
.data
stack: .space 4*512 //栈空间
中断入口函数do_irq()
void do_irq(void)
{
static int a = 1;
int irq_num;
irq_num = CPU0.ICCIAR&0x3ff; //获取中断号
switch(irq_num)
{
case 57:
printf("in the irq_handler");
//清GPIO中断标志位
EXT_INT41_PEND = EXT_INT41_PEND |((0x1 << 1));
//清GIC中断标志位
ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (0x1 << 25);
break;
}
//清cpu中断标志
CPU0.ICCEOIR = CPU0.ICCEOIR&(~(0x3ff))|irq_num;位
}
实现按键中断的初始化函数key_init():
void key_init(void)
{
GPX1.CON =GPX1.CON & (~(0xf << 4)) |(0xf << 4); //配置引脚功能为外部中断
GPX1.PUD = GPX1.PUD & (~(0x3 << 2)); //关闭上下拉电阻
EXT_INT41_CON = EXT_INT41_CON &(~(0xf << 4))|(0x2 << 4); //外部中断触发方式
EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1)); //使能中断
ICDDCR = 1; //使能分配器
ICDISER.ICDISER1 = ICDISER.ICDISER1 | (0x1 << 25); //使能相应中断到分配器
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xff << 8))|(0x1 << 8); //选择CPU接口
CPU0.ICCPMR = 255; //中断屏蔽优先级
CPU0.ICCICR = 1; //使能中断到CPU
return ;
}
六、轮询方式
除了中断方式之外我们还可以通过轮询方式读取按键的信息,原理如下:
循环检测GPX1_1引脚输入的电平,为低电压时,按键按下,为高电平时,按键抬起。
配置GPX1_1引脚功能为输入,设置内部上拉下拉禁止。 GPX1.CON = GPX1.CON &(~(0xf<<4)) ;
GPX1.PUD = GPX1.PUD & ~(0x3 << 2);
按键消抖:按键按下后由于机械特性,会在极短的时间内出现电平忽0忽1,所以我们检测到按键按下后,需要给一个延时,然后再判断按键是不是仍然按下。
代码实现
int main (void)
{
led_init();
pwm_init();
GPX1.CON = GPX1.CON &(~(0xf<<4))|0x0<<4;
while(1)
{
if(!(GPX1.DAT & (0x1<<1))) // 返回为真,按键按下
{
delay_ms(10);
if(!(GPX1.DAT & (0x1<<1))) //二次检测,去抖
{
GPX2.DAT |= 0x1 << 7; //Turn on LED2
delay_ms(500);
beep_on();
GPX2.DAT &= ~(0x1<<7); //Turn off LED2
delay_ms(500);
while(!(GPX1.DAT & (0x1<<1)));
beep_off();
}
}
}
return 0;
}