【版权归stuyou@126.com所有,转载请注明出处】
主机环境:UBUNTU1004 32bit + arm-linux-gcc(v4.5.1)
开发板:友善之臂tiny6410,S3C6410 CPU
任务描述:基于VIC_Port模式,编写中断程序,当按下开发板上的key1~key4时,分别点亮核心板上的LED1~LED4。
【原理】
S3C6410支持两种中断处理方式,一种是非向量模式,另一种是向量模式。其中非向量模式,当中断产生时,跳转到中断异常去,然后这个中断异常中,编写程序,判断是哪一个中断产生,然后去执行对应的中断处理程序。又称system_bus模式,在整个程序的最开始的位置根据异常向量表,安排异常向量,S3C6410及之前处理器采用这种中断处理模式。向量模式设定每个中断对应的中断服务程序的入口地址(设置相关寄存器),这样当中断产生的时候,就不用跳转到中断异常去了,直接跳转到对应的中断程序去了(由硬件自动完成)。这样中断处理的效率就提高了。又称VIC_port模式。这里采用VIC_port模式编写S3C6410中断处理裸机程序。
采用VIC_port模式编写S3C6410中断处理程序一共分为三个步骤:一.使能VIC_port模式、二.中断初始化、三.中断服务程序ISR。
【一.使能VIC_port模式】
使用如下ARM汇编代码,开启VIC_port模式
-
//开启S3C6410的中断VIC_port功能
-
//开启此功能,发生中断时,CPU会自动
-
//跳到ISR,执行ISR。这种跳转操作
-
//是由硬件自动执行的
-
mrc p15,0,r0,c1,c0,0
-
orr r0,r0,#(1<<24)
-
mcr p15,0,r0,c1,c0,0
【二.中断初始化】
中断初始化包括三个方面,1.中断源初始化;2.中断控制器初始化;3.CPSR开中断
【1.中断源初始化】
这里的中断源就是按键,先看按键硬件原理图,如图1所示。
图1 按键硬件图
从图1看出,key1~key4用作外部中断XENT0~3,分别连接至S3C6410的GPN0~3口。正常情况下,
XENT0~3处于高电平,当按键key1~key4按下时,XENT0~3为低电平。
中断源的初始化包括三个方面:A.配置GPN0~3为中断功能;B.配置中断触发方式;C.使能中断
A.
配置GPN0~3为中断功能
使用GPNCON寄存器,配置
GPN0~3为中断功能。GPNCON寄存器配置表如图2所示。
图2 GPNCON寄存器
从图2可以看出,若要配置GPN0~3为中断功能,则需使GPNCON[7:0]=10101010,代码如下:
-
#define GPNCON (*((volatile unsigned long *)0x7F008830))
-
GPNCON &= ~(0xff);
-
GPNCON |= 0xaa;
这里先对GPNCON的[7:0]清零,然后再对其配置为
10101010
B.配置中断触发方式
配置中断触发方式需要对EINT0CON0寄存器进行配置。图3是该寄存器的配置表
图3
EINT0CON0寄存器
这里,中断触发类型配置为下降沿触发,因此需配置EINT0CON0[7:0]=00100010,代码如下:
-
#define EINT0CON0 (*((volatile unsigned long *)0x7F008900))
-
/* 设置中断触发方式为: 下降沿触发 */
-
EINT0CON0 &= ~(0xff);
-
EINT0CON0 |= 0x22;
C.使能中断
使能中断,需要对EINT0MASK寄存器进行配置。图4是
EINT0MASK的配置表
图4 EINT0MASK寄存器
从图4可以看出,若要使能中断ENT0~3,只需要把EINT0MASK寄存器的[3:0]设置为0000即可。代码如下
-
#define EINT0MASK (*((volatile unsigned long *)0x7F008920))
-
/* 开启中断 */
-
EINT0MASK &= ~(0xf);
通过以上步骤,对中断源进行了初始化。此时按下按键时,中断源已经有能力向CPU发送中断了。下一步要对中断控制器进行初始化,使得中断控制器有能力把中断源产生的中断发送给CPU。
【2.中断控制器初始化】
S3C6410的中断管理机制如图5所示。
图5 S3C6410中断管理
从图5可以看出,S3C6410一共两个矢量中断控制VIC0/VIC1,一共管理64个中断源,其中VIC0管理0~31号中断源,VIC1管理32~63号中断源。这里才用了复用中断号的方法。外部中断EINT0~3就共用了0号中断源,也就是说当key1~key4的任何一个键按下时,都会触发0号中断。
中断控制器的初始化的主要工作包括:A.设定中断类型;B.设定中断服务程序的入口地址;C.使能中断
A.设定中断类型
设定中断类型使用VICxINTSELECT(x=0 or 1)寄存器。但是在配置之前呢,为了预防不可知的中断发生,应先把中断关闭,使用VICxINTCLEAR(x=0 or 1)寄存器可以关闭相应中断,图6是VIC0INTCLEAR寄存器配置表。图7是VIC0INTSELECT寄存器的配置表。
图6 VIC0INTCLEAR寄存器配置表
图7 VIC0INTSELECT寄存器的配置表
图6可以看出,VIC0INTCLEAR寄存器的每一位对应一个中断源。因此要关闭0号中断源(EINT0~3),只需要往VIC0INTCLEAR[0]写入1
图7可以看出,VIC0INTSELECT寄存器的每一位对应一个中断源的类型。因此要设定0号中断源(EINT0~3)为IRQ类型,只需要往VIC0INTSELECT[0]写入0。代码如下
-
VIC0INTENCLEAR |= (0x1);//中断控制器关闭中断
-
VIC0INTSELECT &= (~(0x1));//设定0号中断源的中断类型为IRQ
B.设定中断服务程序的入口地址
设定中断服务程序的入口地址使用VICxVECTADDR寄存器。VIC0VECTADDR寄存器配置表如图8所示
图8 VIC0VECTADDR寄存器
从图8可以看出,VIC0VECTADDR是一组寄存器,共32个,分别对应VIC0管理的32个中断源的中断服务程序的入口地址。由于key1~key3对应的中断号为0,因此要把中断服务程序的入口地址赋值给VIC0VECTADDR[0]寄存器。代码如下
-
//中断服务函数入口地址赋值给VIC0VECTADDR0
-
//中断发生时,CPU会自动到VIC0VECTADDR0读取
-
//中断服务程序的入口地址,执行中断服务函数
-
//配置VIC_PORT使能模式下,中断发生不会进入
-
//异常向量,避免使用sys_bus模式处理中断
-
VIC0VECTADDR0 = (unsigned long)&do_irq;
这里的do_irq是中断服务函数。
C.使能中断
中断控制器中使能中断,使用VICxINTENABLE寄存器。VIC0INTENABLE寄存器的配置表如图9所示。
图9 VIC0INTENABLE寄存器
从图9可以看出,VIC0INTENABLE的每一位对应一个中断源。若要使能0号中断源,则把VIC0INTENABLE[0]=1即可。
-
/* 在中断控制器里使能这些中断 */
-
VIC0INTENABLE |= (0x1); /* bit0: EINT0~3 */
【3.CPSR开中断】
ARM处理器中有个IRQ中断总开关,就是CPSR寄存器的bit7,需要将该位设置为0,CPU才能响应中断。ARM汇编代码如下:
-
//开启中断总开关,设置CPSR的I位为0
-
mrs r0,cpsr
-
bic r0,r0,#0x80
-
msr cpsr_c,r0
通过以上步骤,中断初始化步骤完成,从中断源到中断控制器再到CPU的中断通道已经打通。
此时如果有按键按下,GPIO模块就会产生一个中断给中断控制器,中断控制器中已经使能了中断,发出一个信号给CPU,CPU执行每一条指令之前,都会先判断有无中断发生,如果有:
1.CPU进入irq模式;
2.之前的CPSR保存到SPSR_irq
3.使用irq模式下的R13_irq和R14_irq
4.把下一条指令的地址存入R14_irq
5.硬件读取VIC0VECTADDR中设定的ISR地址,跳入ISR执行
以上5个步骤是由硬件自动完成,接下来就要设计中断服务程序ISR,来处理中断了。
【三.中断服务程序ISR】
任何一个中断服务程序的设计一般都由以下三个部分组成:1.保护现场;2.中断处理;3.恢复现场。
先看保护现场和恢复现场。所谓的保护现场是指在中断处理之前,把相应的寄存器入栈;所谓的回复现场,是指把入栈的寄存器重新恢复。保护现场和恢复现场的程序代码如下(ARM汇编):
-
//do_irq函数是中断服务函数,首先保存现场
-
//然后跳转到key_press按键判断程序
-
//最后恢复现场
-
do_irq:
-
ldr sp, =0x54000000 //发生IRQ中断后,CPU自动切换到IRQ模式下
-
//sp是分组寄存器,因此关看门狗后设置的
-
//栈不能使用,这里要重新设置栈,供保存、
-
//恢复现场使用
-
-
sub lr, lr, #4 //lr存放的是发生中断时那条指令的下一条指令
-
//恢复现场时,保证能正常返回到主程序,
-
//这里lr应减去4
-
-
stmdb {r0-r12, lr} //保存现场
-
bl key_isr //跳到key_isr程序,
-
ldmia {r0-r12, pc}^ //恢复现场,^表示把SPSR恢复到CPSR
由于中断发生时,硬件动作中把下一条指令的地址存入LR,而上一条指令尚未执行,在恢复现场后,程序应从中断发生时的上一条指令开始执行,因此这里LR需减去4。
保护完现场之后,跳到中断处理程序key_isr来处理中断。中断的处理一般有三个步骤构成:A.分辨是哪个中断;B.进入相应的中断处理程序;C.清中断
A.分辨是哪个中断
当中断发生时,EINT0PEND寄存器的相应位会被置1,通过判断相应位,即可知道是哪个中断发生了。图10是EINT0PEND寄存器配置表。
图10 EINT0PEND寄存器
从图10可以看出,当EINT0~3发生中断时,EINT0PEND[3:0]分别被置1。因此用过判断EINT0PEND寄存器中哪一位为1,即可知道是哪个中断发生了。
此外,该寄存器还用来作为清除中断使用。往相应的位写入1时,即可清除该中断。
B.中断处理
-
void key_isr(void)
-
{
-
unsigned long key_press=0;
-
key_press=(EINT0PEND & 0xf);//读EINT0PEND[3:0],判断哪个键按下
-
-
switch(key_press)
-
{ //key1按下
-
case 1:
-
{
-
led_display(LED_ALL_OFF);
-
led_display(LED1_ON);
-
break;
-
}
-
//key2按下
-
case 2:
-
{
-
led_display(LED_ALL_OFF);
-
led_display(LED2_ON);
-
break;
-
}
-
//key3按下
-
case 4:
-
{
-
led_display(LED_ALL_OFF);
-
led_display(LED3_ON);
-
break;
-
}
-
//key4按下
-
case 8:
-
{
-
led_display(LED_ALL_OFF);
-
led_display(LED4_ON);
-
break;
-
}
-
-
default:
-
break;
-
-
}
-
-
//清中断
-
EINT0PEND = 0xf;
-
VIC0ADDRESS = 0;
-
}
中断处理时,先根据EINT0PEND寄存器的值判断是哪个按键按下,然后再点亮相应的LED灯。最后清中断。
C.清中断
清除中断分为两个步骤,第1步,先清除源头,即往
EINT0PEND寄存器的相应位写入1,即EINT0PEND[3:0]=1111;第2步,再清中断控制器。清除中断控制器时,需要使用VICxADDRESS,VIC0ADDRESS寄存器的配置表如图11所示。
图11
VIC0ADDRESS寄存器
从图11可以看出,VIC0ADDRESS寄存器的每一位对应一个中断源,往该位写入任意值,即可清除该中断,即VIC0ADDRESS[0]=0。清中断代码如下
-
//清中断
-
EINT0PEND = 0xf;
-
VIC0ADDRESS = 0;
【Makefile】
-
irq.bin: start.o main.o led.o irq.o
-
arm-linux-ld -Ttext 0x50000000 -o irq.elf $^
-
arm-linux-objcopy -O binary irq.elf irq.bin
-
arm-linux-objdump -D irq.elf > irq_elf.dis
-
%.o : %.S
-
arm-linux-gcc -o $@ $< -c
-
-
%.o : %.c
-
arm-linux-gcc -o $@ $< -c
-
-
clean:
-
rm *.o *.elf *.bin *.dis -rf
【编译、运行】
执行make,编译生成二进制文件irq.bin,使用友善之臂的裸机程序烧写工具minitools,把irq.bin下载到
开发板内存运行。当分别按下key1~key4时,LED1~LED4分别点亮。
源代码:
阅读(9574) | 评论(2) | 转发(1) |