要想写按键中断程序,首先我们要了解一下什么是中断,它的执行过程是怎么样的。
在ARM中中断源分为两类:外部中断和内部中断。内部中断大多是有CPU内部自己产生的,外部中断就是由各种外部硬件产生的:比如按键。
中断能否成功处理,有两部分,1是触发中断信号,2是ARM能否识别中断。
触发中断信号
触发中断信号,很简单,有高电平,低电平,上升沿,下降沿触发四种,至于用那种需要程序员来设置。可是一个设备能否具有触发中断信号的能力,还要看它是否是中断源,还有它是否处于中断模式中,这个可以查看手册中相关的介绍就能够知道。
CPU识别中断信号
中断并不是一发过来CPU就会自动处理的。一个中断信号传到给CPU前,还得经过一个或者多个中断控制器的处理。中断控制器说白了就是用来过滤中断的。对于ARM中的所有中断源,在中断控制器里会有对应寄存器来控制,包括状态标志和中断开关。
一个中断源要想到达CPU,它所对应的寄存器应该都是允许该中断的,就拿按键IRQ中断举例:
首先:设置按键所对应的寄存器为中断模式、配置中断触发方式
然后:开中断,在裸机中除了要打开中断控制器中的位之外,CPSR寄存器的中断控制位也一定要打开。这个是中断的总开关,如果不打开一切都白费了。中断的处理过程
当CPU接收到中断信号的时候,它会打断当前正在执行的程序,保存当前的寄存器(即保存PC和其他寄存器),查找中断向量表,跳到相应的地址去执行中断程序(即把中断向量表中的对应值赋值给PC),执行完之后跳回刚才中断的地方继续执行(即把原来保存的PC恢复回来)。好吧,简单写一个按键中断程序参考一下:
首先先用汇编写一段引导代码,主要的功能就是,设置堆栈指针,设置中断向量表,打开CPSR的中断位。代码如下:
.global _start /* 声明一个全局的标号 */
.global key_isr
_start:
ldr r0, =0xE2700000
mov r1, #0
str r1, [r0]
/* 设置栈,以调用c函数 */
ldr sp, =0x40000000
/* 开总中断,CPSR寄存器 */
mov r0, #0x53
msr CPSR_cxsf, r0
bl clock_init /*设置中断向量表*/
bl main /* 跳转到C函数去执行 */
key_isr:
ldr sp, =0xD0037F80
/* 计算返回地址:PC的值等于当前执行的地址+8,当CPU正要执行某条指令时(还未执行),被中断,
** 这是这条刚要执行的指令的地址刚好=PC-4 */
stmfd sp!, {r0-r12, lr} /* 保护现场 */
bl do_irq
/* 恢复现场 */
ldmfd sp!, {r0-r12, pc}^ /* ^表示把spsr恢复到cpsr */
subs pc, lr, #4
halt:
b halt
中断向量表的设置代码:
#define _Exception_Vector 0xD0037400 //Yiny210 在程序开始时就跳到了这里、而不是0地址
#define pExceptionRESET ( *((volatile unsigned long *)(_Exception_Vector + 0x0)) )
#define pExceptionUNDEF ( *((volatile unsigned long *)(_Exception_Vector + 0x4)) )
#define pExceptionSWI ( *((volatile unsigned long *)(_Exception_Vector + 0x8)) )
#define pExceptionPABORT ( *((volatile unsigned long *)(_Exception_Vector + 0xc)) )
#define pExceptionDABORT ( *((volatile unsigned long *)(_Exception_Vector + 0x10)) )
#define pExceptionRESERVED ( *((volatile unsigned long *)(_Exception_Vector + 0x14)) )
#define pExceptionIRQ ( *((volatile unsigned long *)(_Exception_Vector + 0x18)) )
#define pExceptionFIQ ( *((volatile unsigned long *)(_Exception_Vector + 0x1c)) )
void key_isr(void);
void exceptionundef(void)
{
while(1);
}
void exceptionswi(void)
{
while(1);
}
void exceptionpabort(void)
{
while(1);
}
void exceptiondabort(void)
{
while(1);
}
// 设置中断向量表
void system_initexception( void)
{
// 给每个中断一个处理函数地址
pExceptionUNDEF = (unsigned long)exceptionundef;
pExceptionSWI = (unsigned long)exceptionswi;
pExceptionPABORT = (unsigned long)exceptionpabort;
pExceptionDABORT = (unsigned long)exceptiondabort;
pExceptionIRQ = (unsigned long)key_isr;
pExceptionFIQ = (unsigned long)key_isr;
}
main.c
#define LEDCON (*(volatile unsigned long *)0xE0200280)
#define LEDDAT (*(volatile unsigned long *)0xE0200284)
#define BUZCON (*(volatile unsigned long *)0xE02000A0)
#define BUZDAT (*(volatile unsigned long *)0xE02000A4)
#define KEY1CON (*(volatile unsigned long *)0xE0200C40)
#define EXT_INT_2_CON (*(volatile unsigned long *)0xE0200E08)
#define EXT_INT_2_MASK (*(volatile unsigned long *)0xE0200F08)
#define EXT_INT_2_PEND (*(volatile unsigned long *)0xE0200F48)
//保存中断处理程序地址
#define VIC0INTSELECT (*(volatile unsigned long *)0xF200000C)
#define VIC0INTENABLE (*(volatile unsigned long *)0xF2000010)
#define VIC0VECTADDR16 (*(volatile unsigned long *)0xF2000140)
#define VIC0ADDRESS (*(volatile unsigned long *)0xF2000F00)
void int_key1();
void do_irq();
void delay(void)
{
int i;
for(i = 0x100000; i > 0; i --)
;
}
int main()
{
//设置异常向量表
system_initexception();
//LED init
LEDCON &= ~((0xf << 0) | (0xf << 4) | (0xf << 8) | (0xf << 14));
LEDCON |= (0x1 << 0) | (0x1 << 4) | (0x1 << 8) | (0x1 << 12);
LEDDAT |= 0xf;
//BUZ init
BUZCON &= ~((0xf << 0) | (0xf << 4) | (0xf << 8) | (0xf << 14));
BUZCON |= (0x1 << 0) ;
BUZDAT &= 0xe;
//interrupt init
KEY1CON |= 0xf; //interrupt mode
EXT_INT_2_CON |= 1<<1;
EXT_INT_2_MASK &= ~(1<<0);
VIC0INTENABLE |= (0x1 << 16);
VIC0VECTADDR16 = (unsigned int)int_key1;
while(1)
{
LEDDAT &= 0x0;
delay();
LEDDAT |= 0xf;
delay();
}
return 0;
}
void do_irq()
{
//处理中断的时候 中断函数的地址会自动赋值给 VIC0ADDRESS
( ( void(*)(void) )VIC0ADDRESS )() ;
}
void int_key1()
{
//中断服务程序
BUZDAT |= 0x1;
delay();
delay();
BUZDAT &= 0xe;
//清中断
VIC0ADDRESS = 0;
EXT_INT_2_PEND |= (1 << 0);
}
Makefile
uart.bin: start.o main.o clock.o int.o
arm-linux-ld -Tint.lds -o uart.elf $^
arm-linux-objcopy -O binary uart.elf uart.bin
arm-linux-objdump -D uart.elf > uart_elf.dis
%.o : %.s
arm-linux-gcc -o $@ $< -c -fno-builtin
%.o : %.c
arm-linux-gcc -o $@ $< -c -fno-builtin
clean:
rm *.o *.elf *.bin *.dis *.exe *~ -f