在开始之前整理一下ARM9结构中断建立过程:
/* EXCEPTION HANDLER VECTOR TABLE */
^ DRAM_BASE
HandleUndef # 4
HandleSwi # 4
HandlePrefetch # 4
HandleAbort # 4
HandleReserv # 4
HandleIrq # 4
HandleFiq # 4
小注: 这里的^是MAP,#是FIELD
也就是在DARM的BANK0里面最开始的地方定义了一个中断向量表(已经固化了的),这些中断向量在程序储存空间中占用一个连续的地址空间段(只有几个字节,一条指令的长度)。中断向量就是发生中断和异常时的一个标识,代表着中断程序的入口地址,即指定各异常中断与其处理程序的对应关系。当中断源发出的中断信号被CPU检测到后,如果中断系统允许响应这个中断请求,程序会自动跳转到这个中断向量处去执行。在中断向量区只存放有一条指向中断处理程序的指令,中断处理程序还存在其他的地方。因此,中断触发之后CPU就能找到对应的处理程序的入口了。
使用s3c2440的外部中断需要配置的寄存器有:SRCPND、INTMSK、INTPND、EXTINTn、PRIORITY、EINTMASK、EINTPEND,还有就是2440的外部中断I/O引脚是第F和G组所以也要根据需要配置这两组I/O对应的寄存器。当然还有一个中断模式选择的寄存器INTMOD,这是用来配置普通中断IRQ和快中断FIQ的,上电复位的初始状态是IRQ。
前面说到的那些寄存器有些是用来设置是否屏蔽中断请求的,有些是用来反映程序运行时中断请求的状态的这是为了能够进入中断处理程序。下面结合中断点灯例程序程序做一一介绍:
startup.S文件
@ File:startup.S
@ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数
.extern main
.text
.global _start
_start:
@ 注:在定义异常向量时即使有些异常模式不需要用到但是还是要按照中断向量的顺序一一定义
b Reset @0x00:上电复位
HandleUndef: @ 0x04: 未定义指令中止模式的向量地址
b HandleUndef
HandleSWI: @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
b HandleSWI
HandlePrefetchAbort: @ 0x0c: 指令预取终止导致的异常的向量地址
b HandlePrefetchAbort
HandleDataAbort: @ 0x10: 数据访问终止导致的异常的向量地址
b HandleDataAbort
HandleNotUsed: @ 0x14: 保留
b HandleNotUsed
b HandleIRQ @ 0x18: 中断模式的向量地址
HandleFIQ: @0x1C:快中断模式的向量地址
b HandleFIQ
@下面是对上面需要用到的向量的定义:
Reset:
ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈 这里是栈顶,因为片内内存只有4K(4*1024=4096)
bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
msr CPSR_c, #0xd2 @ 进入中断模式,只有在中断模式下才可以设置对应的栈,这里只是设置堆栈,所以禁止所有的中断
ldr sp, =3072 @ 设置中断模式栈指针
msr cpsr_c, #0xd3 @ 进入管理模式
ldr sp, =4096 @ 设置管理模式栈指针,上电复位之后,CPU就处于管理模式,
bl init_led @ 初始化LED的GPIO管脚
bl init_irq @ 调用中断初始化函数,在init.c中
msr cpsr_c, #0x53 @ 设置设置控制位[0-7bit],开IRQ中断。CPSR是使能跳转到异常中断向量入口的根本原因
ldr lr, =halt_loop @ 设置返回地址
ldr pc, =main @ 调用main函数 也可以直接用一句:bl main 代替
halt_loop:
b halt_loop
HandleIRQ:
sub lr, lr, #4 @ 计算返回地址,用于返回
stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器
@ 注意,此时的sp是中断模式的sp
@ 初始值是上面设置的3072
ldr lr, =int_return @ 设置调用EINT_Handle函数后的返回地址
ldr pc, =EINT_Handle @ 调用中断服务函数,在interrupt.c中
int_return:
ldmia sp!, { r0-r12,pc }^ @ 中断返回,出栈, ^表示将spsr的值复制到cpsr,这样可以回到管理模式
init.c文件
#include"s3c2440.h"
//用于设置成输出
#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))
//用于将GPF组IO设置成外部中断
#define GPF1_int (0x2<<(1*2))
#define GPF4_int (0x2<<(4*2))
#define GPF2_int (0x2<<(2*2))
#define GPF0_int (0x2<<(0*2))
#define GPF1_msk (3<<(1*2))
#define GPF4_msk (3<<(4*2))
#define GPF2_msk (3<<(2*2))
#define GPF0_msk (3<<(0*2))
void disable_watch_dog(void)
{
WTCON = 0;
}
void init_led(void)
{
GPBCON &= ~(GPB5_msk | GPB6_msk | GPB7_msk | GPB8_msk);
GPBCON |= GPB5_out | GPB6_out | GPB7_out | GPB8_out;
GPBDAT |=(0xF<<5);
GPBUP &= ~((1<<5)|(1<<6)|(1<<7)|(1<<8));
}
//外部中断初始化
void init_irq()
{
GPFCON &= ~(GPF1_msk | GPF4_msk | GPF2_msk | GPF0_msk);
GPFCON |= GPF1_int | GPF4_int | GPF2_int | GPF0_int; //中断模式
EXTINT0 &= ~((7<<0)|(7<<1)|(7<<2)|(7<<4)); //低电平触发
PRIORITY = (PRIORITY & ((~0x01) | ~(0x3<<7)));
INTMOD=0x0;
SRCPND = 0x17; //清除ENIT0、1、2、4的中断标志位
INTPND = 0x17;
EINTPEND |= (1<<4);
//使能中断
EINTMASK &= ~(1<<4);
INTMSK &= ~((1<<4)|(1<<2)|(1<<1)|(1<<0));
}
interrupt.c文件
#include "s3c2440.h"
void EINT_Handle()
{
unsigned long oft = INTOFFSET;
// unsigned long val = EINTPEND;
GPBDAT |= (0xF<<5); // 所有LED熄灭
if (oft==1)
{
// K1被按下
GPBDAT &= ~(1<<5); // LED1点亮
}
if (oft==4)
{
// K2被按下
GPBDAT &= ~(1<<6); // LED2点亮
}
if (oft==2)
{
// K3被按下
GPBDAT &= ~(1<<7); // LED3点亮
}
if (oft==0)
{
// K4被按下
GPBDAT &= ~(1<<8); // LED4点亮
}
//清中断标志位
EINTPEND = (1<<1) | (1<<4) | (1<<2) | (1<<0);
SRCPND = 1<<oft; //先
INTPND = 1<<oft; //后
}
main.c
int main( )
{
while(1);
return 0;
}
makefile文件
objs := startup.o init.o interrupt.o main.o
int.bin: $(objs)
arm-linux-ld -Ttext 0x00000000 -o int_elf $^
arm-linux-objcopy -O binary -S int_elf $@
arm-linux-objdump -D -m arm int_elf > int.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f int.bin int_elf int.dis *.o
编程小结:
(1)在启动代码里做好一些要用到的硬件初始化的C跳转,设置好中断异常模式下的堆栈、出入栈,然后设置中断向量
(2)中断初始化:设置模式,外部中断触发方式,优先级,清除标志位,使能中断
(3)中断处理函数的定义:处理内容+最后的清中断标志位
好了,外部中断就写到这里。