裸机系列代码地址:链接:http://pan.baidu.com/s/1pLHOd0v 密码:4x5s
当某事件发生时,硬件会设置某个寄存器;CPU会在每执行完一个指令时,通过硬件查看这个寄存器,如果发现所关注的事件发生
了,则中断当前程序流程,跳转到一个固定的地址处理这事情,事后返回继续执行的程序。中断的使用步骤:
(1)设置好中断模式下的栈,禁止中断。当发生中断IRQ时,CPU进入中断模式,这时使用中断模式下的栈
(2)准备好中断服务程序(ISR),在中断服务程序中跳转到具体的中断处理程序,在具体的中断处理程序中做想要做的事,并清除中断
(3)进入、退出中断模式时,需要保存、恢复被中断程序的运行环境
HandleIRQ:
sub lr,lr,#4
stmdb sp!, {r0-r12,lr}
ldr lr,=int_return
ldr pc,=EINT_Handle
int_return:
ldmia sp!,{r0-r12,pc}^
(4)根据具体的中断,设置相关的外设。比如对于GPIO中断,需要将相应的引脚功能设为“外部中断”,设置中断触发条件,一些中断拥有
自己的屏蔽寄存器,还需要开启它。
(5)对于“Request sources”的中断,将INTSUBMASK寄存器中的相应位设为0
(6)设置中断优先级
(7)将INTMASK寄存器中的相应位设为0
(8)设置CPSR寄存器的中断使能位,使能中断
中断控制寄存器
(1)SUBSRCPND寄存器(SUB SOURCE PENDING)
标示有子中断的中断,在S3C2440中这类有子中断的中断有6个,共对应15个子中断
INT_UART0 : INT_RXD0 , INT_TXD0 , INT_ERR0
INT_UART1 : INT_RXD1 , INT_TXD1 , INT_ERR1
INT_UART2 : INT_RXD2 , INT_TXD2 , INT_ERR2
INT_ADC : INT_ADC_S , INT_TC
INT_CAM : INT_CAM_C , INT_CAM_P
INT_WDT_AC97 : INT_WDT , INT_AC97
当这些子中断发生并且没有被INTMASK寄存器屏蔽,则他们中的若干位将“汇集”出现在SRCPND寄存器的一位上
例如,SUBSRCPND中的 INT_RXD0 , INT_TXD0 , INT_ERR0只要有一位只要有一个发生没有被屏蔽,则SRCPND寄存器
上的INT_UART0将被标示。往某位中写1即可令此位为0,写入0无效果,数据保持不变
(2)INTSUBMASK寄存器(INTERRUPT SUB MASK)
INTSUBMASK寄存器用来屏蔽SUBSRCPND寄存器所标示的中断,INTSUBMASK中的某位被置为1时,对应的中断被屏蔽
(3)SRCPND寄存器(SOURCE PENDING)
SRCPND中每一位被用来标示一个(或一类子中断)是否已经发生。若想清除某一位,往此位写1
(4)INTMASK(INTERRUPT MASK)
INTMASK寄存器被用来屏蔽SRCPND寄存器中所标示的中断。INTMASK某位被设为1时,对应的中断被屏蔽
(5)PRIORITY寄存器
当有多个普通中断同时发生时,中断控制器将选出最高优先级的中断,首先处理它。
(6)INTPND寄存器(INTERRUPT PENDING)
经过中断优先级仲裁器选出优先级最高的中断后,这个中断在INTPND寄存器中的相应位被置1,随后,CPU将进入
中断模式处理它。同一时间内,此寄存器只有一位被置1
(7)INFOFFSET寄存器(INTERRUPT OFFSET)
这个寄存器被用来表示INTPND寄存器中哪位被置1了,即INTPND寄存器[x]位为1时,INTOFFSET寄存器的值为x
在清除SRCPND,INTPND寄存器时,INTOFFSET寄存器被自动清除
怎样判断具体是哪一个中断发生了
1、对于无子中断的中断类,通过INTOFFSET或INTPND即可知道是哪个中断发生了。
2、对于有子中断的中断类,光通过INFOFFSET无法知道具体发生的中断,还需要通过SUBSRCPND来进一步判断具体发生的中断
3、对于EINT4~EINT23光通过INFOFFSET也无法知道具体发生的中断类型,因为EINT4~EINT7在SRCPND中以EINT4_7标示,通过
INTOFFSET我们只能知道EINT4~EINT7中有一个发生了,EINT9~EINT23是同样的道理。此时我们需要EINTPEND来判断具体发生
的中断类型
下面来看一个具体中断的实例
Makefile文件
objs:= head.o init.o irq.o main.o
irq.bin: $(objs)
arm-linux-ld -Ttext 0x00000000 -o irq_elf $^
arm-linux-objcopy -O binary -S irq_elf $@
arm-linux-objdump -D -m arm irq_elf > irq.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f irq_elf irq.dis irq.bin *.o
头文件
/*GPIO registers*/
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPFUP (*(volatile unsigned long *)0x56000058)
#define GPGCON (*(volatile unsigned long *)0x56000060)
#define GPGDAT (*(volatile unsigned long *)0x56000064)
#define GPGUP (*(volatile unsigned long *)0x56000068)
/*interrupt registes*/
#define SRCPND (*(volatile unsigned long *)0x4A000000)
#define INTMOD (*(volatile unsigned long *)0x4A000004)
#define INTMSK (*(volatile unsigned long *)0x4A000008)
#define PRIORITY (*(volatile unsigned long *)0x4A00000c)
#define INTPND (*(volatile unsigned long *)0x4A000010)
#define INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define SUBSRCPND (*(volatile unsigned long *)0x4A000018)
#define INTSUBMSK (*(volatile unsigned long *)0x4A00001c)
/*external interrupt registers*/
#define EINTMASK (*(volatile unsigned long *)0x560000a4)
#define EINTPEND (*(volatile unsigned long *)0x560000a8)
#define WTCON (*(volatile unsigned long *)0x53000000)
head.S
.extern main
.text
.global _start
_start:
b reset
HandleUndef:
b HandleUndef
HandlerSWI:
b HandlerSWI
HandlePrefetchAbort:
b HandlePrefetchAbort
HandleDataAbort:
b HandleDataAbort
HandleNotUsed:
b HandleNotUsed
b HandleIRQ /*中断向量表的位置必须固定,即 b HandleIRQ运行位置必须在0x18处*/
HandleFIQ:
b HandleFIQ
reset:
ldr sp,=4096
bl disable_watchdog
msr cpsr_c,#0xd2 /*进入中断模式*/
ldr sp,=3072 /*设置中断模式栈指针*/
msr cpsr_c,#0xd3 /*进入系统模式*/
ldr sp,=4096 /*设置系统模式栈指针*/
bl init_led /*初始化led,即使得LED对应的GPIO引脚为输出模式*/
bl init_irq /*初始化中断,即设置相应外设引脚为中断模式,并设置好屏蔽为,优先级仲裁方法*/
msr cpsr_c,#0x5f /*开IRQ中断,这之后程序可以响应中断*/
ldr lr,=halt_loop
ldr pc,=main
halt_loop:
b halt_loop
HandleIRQ:
sub lr,lr,#4
stmdb sp!, {r0-r12,lr}
ldr lr,=int_return
ldr pc,=EINT_Handle
int_return:
ldmia sp!,{r0-r12,pc}^
init.c
#include "s3c2440.h"
void init_irq(void)
{
#define EINT (0b10)
/*设置按键相关的端口为中断模式*/
GPFCON &= ~( (3) | (3<<4)); /*先清除下面要设置的寄存器相关位*/
GPGCON &= ~(3<<6);
GPFCON |= ((EINT << 4) | (EINT <<0)); /*设置按键对应的GPIO口为中断模式*/
GPGCON |= (EINT <<6);
EINTMASK &= (~(1<<11)) ; /*S4按键的中断为EINT11,在INTPND中的表示为EINT8_23*/
INTMSK &= ( (~(1<<0)) & (~(1<<2)) & (~(1<<5)));
PRIORITY = (PRIORITY & ( (~0x01) | (0x3<<7))) | (0x0<<7);
}
void init_led(void)
{
GPFCON &= ~( (0b11<<12) | (0b11<<10) | (0b11<<8) );
GPFCON |= ( (0b01<<12) | (0b01<<10) | (0b01<<8));
GPFDAT |= (0b111<<4);
}
void disable_watchdog(void)
{
WTCON=0X00;
}
irq.c
#include "s3c2440.h"
void EINT_Handle(void)
{
int val;
val=INTOFFSET;
switch(val)
{
case 0: /*EINT0*/
{
GPFDAT |= (0x7<<4);
GPFDAT &= ~(1<<4);
break;
}
case 2: /*EINT2*/
{
GPFDAT |= (0b111<<4);
GPFDAT &= ~(1<<5);
break;
}
case 5: /*EINT8_23*/
{
GPFDAT |= (0b111<<4);
GPFDAT &= ~(1<<6);
break;
}
default:
break;
}
/*清除中断*/
if (val == 5 )
EINTPEND = ( (1<<11) );
SRCPND = 1<<val;
INTPND = 1<<val;
main.c
int main(void)
{
while(1);
return 0;
}