210学习日记(11)
--中断
由于后面的IIC会用到中断,这里就先讲讲中断吧:
写完S5PV210的按键中断程序后,我才知道"6410只是一个过渡产品"确实是正确的,为什么这样说呢?下面我就给大家进行详细的阐述:
中断处理过程等等我就不讲了,一期视频里面已经讲得非常详细了,建议大家在学习本章前,把2440和6410的关于中断的视频再看一次。接下来,我就直接对比他们吧:
(1).对于2440:
中断向量只出现在start.s开始部分,类容如下:
.globl _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
ldr pc, _fiq
也就是说,中断向量的入口地址是固定的!!
(2).对于6410:
中断向量不仅出现在start.s开始部分,类容如下:
.globl _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
ldr pc, _fiq
还引入了向量地址寄存器(即VICxADDRESS),它会进一步存储中断处理函数的入口地址,详细类容,请大家学习一期的6410的视频的相关章节。
(注意:关于6410的讲述可能有问题,因为我没有6410开发板,无法验证,仅供参考)
(3).对于S5PV210:
只说一说6410只是一个过渡产品,就中断角度而言,是因为在S5PV210中,中断向量舍弃了start.s开始的部分(因为从某种角度来讲,S5PV210上电后,不在是从0地址开始执行了,大家可以看下学习日记(2)中介绍的S5PV210的启动流程),而只保留了向量地址寄存器(即VICxADDRESS)部分,而该寄存器的功能和6410的一样。
问:既然S5PV210的start.s中不在有中断向量,那么S5PV210是怎么处理中断的呢?
答:以按键中断为例子,当中断发生时,PC不再是跳到0x00000018地址处,而是去访问向量地址寄存器(即VICxADDRESS),在该寄存器里面读出一个地址(即可以认为是我们的中断处理函数的地址),然后进一步跳到该地址处去执行。其他的过程和2440的处理过程一样(比如模式切换等等)
接下来我一个具体的按键中断为例子(实现按K4,计数器加1,按K5,计数器减1的功能),代码放在了"Tiny210学习日记_代码"目录下了,名为"12_key_irq":
一上来,就写start.s,和2440的几乎无差别(一定不用写中断向量和关看门狗),比如初始化时钟,初始化nand,初始化内存,初始化需要模式的栈(用户模式,中断模式)等等操作,当然也会初始化中断,类容如下:
void irq_init(void)
{
/* 设置GPH2_3(K4),GPH3_0(K5)用于中断 */
GPH2CON |= 0xf<<12;
GPH3CON |= 0xf<<0;
/* 设置触发方式为下降沿触发 */
EXT_INT_2_CON |= 0x2<<12;
EXT_INT_3_CON |= 0x2<<0;
/* 使能中断(GPIO里面的) */
EXT_INT_2_MASK &= ~(1<<3);
EXT_INT_3_MASK &= ~(1<<0);
/* 设置为IRQ中断 */
VIC0INTSELECT &= ~(1<<16);
/* 使能中断(中断控制器里面的) */
VIC0INTENABLE |= 1<<16;
/* 设置中断向量 */
VIC0VECTADDR16 = (int)key_IRQ;
}
注意:
当中断发生时,VIC0VECTADDR16中的值(即中断处理函数的地址)会被自动的复制给向量地址寄存器(即VICxADDRESS),然后当中断发生时,就会访问向量地址寄存器(即VICxADDRESS),从而找到key_IRQ中断处理函数。(这些在6410视频里面有讲解)
问:以前2440在中断来时,我们会计算返回地址,保护现场等操作,难道S5PV210就不用了吗?
答:答案是否定的,S5PV210一样是要做的,而且一个不能够少,过程如下(这部分程序写在start.s中):
.global key_IRQ
key_IRQ: /* 该函数就是在中断初始化中,VIC0VECTADDR16指向的函数 */
sub lr, lr, #4 /* 1.计算返回地址 */
stmdb sp!, {r0-r12, lr} /* 2.保护现场 */
/* 3. 处理异常 */
bl do_irq
/* 4. 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^表示把spsr恢复到cpsr */
最终,经过上面的处理后,又和我们的2440的中断函数完全一样了,因此我们就在do_irq函数中清中断,分辨中断,执行处理等操作,该部分程序中是这样做的,如下:
void do_irq(void)
{
/* 清中断 */
EXT_INT_2_PEND |= 1<<3;
EXT_INT_3_PEND |= 1<<0;
/* 清中断向量 */
VIC0ADDRESS = 0;
if(!(GPH2DAT & (1<<3))) /* 如果K4被按下 */
{
wy_printf("counter(k4) : %d \n",i++); /* 计数器加1 */
}
if(!(GPH3DAT & (1<<0))) /* 如果K4被按下 */
{
wy_printf("counter(K5) : %d \n",i--);/* 计数器减1 */
}
}
注意:
1.参考代码放在了"Tiny210学习日记_代码"目录下了,名为"12_key_irq"。
2.测试方法,当终端有显示后,直接按开发板上面的K4,K5键,就能实现计数功能。
注:
如有问题,请到韦东山LINUX视频讨论群里面,我们一起讨论学习,或者加我QQ:317312379