可以说中断是计算机的灵魂,学习中断、使用中断十分的重要。
1.中断的处理过程
图一
从图中可以看出有两条路径可以进入中断,当多个中断公用一个入口时需要走第一条路径,在子中断源登记寄存器(SUBSRCPND)登记中断(把相应的位置1),子中断源屏蔽寄存器(SUBMASK)如果置相应位1则屏蔽中断,若置0则能够进入源中断登记寄存器(SRCPND)。一个中断一个入口时走第二条路径。产生中断时要在源中断登记(SRCPND)寄存器把相应位置1,中断屏蔽寄存器(INTMASK)决定中断是否交给仲裁器处理,中断模式寄存器决定产生的中断是IRQ还是FIQ的,如果是RIQ需要交给优先级控制寄存器(PRIORITY)最高优先级的才能在中断登记寄存器(INTPND)登记,处理器响应中断。
2.中断控制寄存器
2.1 中断源登记寄存器(SRCPND)
该寄存器32位每一位对应一个中断源,产生中断时在该寄存器置相应位为1,不论中断是否被屏蔽该位都会被置位,写1清除。
2.2 中断屏蔽寄存器(INTMSK)
该寄存器32位每一位对应一个中断源,置1表示屏蔽,置0表示允许。
2.3 中断模式寄存器(INTMOD)
该寄存器32位每一位对应一个中断源,置1表示FIQ,置0表示IRQ。仅有一个中断源能够在FIR模式下服务。
2.4 中断优先级
2.4.1 仲裁器
图二
2.4.2 优先级控制器(PRIORITY)
该寄存器32位,[20:7]控制七个优先级仲裁器每两位控制一个仲裁器,[6:0]控制七个仲裁器的模式每一位控制一个仲裁器。
图三
2.5 中断源登记寄存器(INTPND)
该寄存器32位,每一位对应一个中断源,只能有一位被置1,通过写1清除。
2.6 中断偏移寄存器(INTOFFSET)
该寄存器的值为当前正在服务的中断源的编号。
3. 使用外部中断控制LED
3.1 启动处理器
.text
.global _start
_start:
b reset
undef:
b undef
swi:
b swi
pref:
b pref
data:
b data
nouser:
b nouser
b irq
fiq:
b fiq
reset:
ldr sp, =4*1024
bl close_dog
msr cpsr_c, #0xd2 @进入中断模式
ldr sp, =3*1024 @中断模式下的堆栈
msr cpsr_c, #0xd3 @进入管理模式
ldr sp, =4*1024 @设置管理模式下的堆栈
bl init_gpio @初始化led
bl init_irq @设置中断
msr cpsr_c, #0x53 @打开IRQ
ldr lr, =main_return
ldr pc, =main
main_return:
bl main_return
irq:
sub lr, lr, #0x4
stmdb sp!, {r0-r12,lr} @保护现场,满递减堆栈
ldr lr, =ll @设置返回地址
ldr pc, =irq_interrupt @调用中断服务程序
ll:
ldmia sp!, {r0-r12,pc}^ @恢复现场
3.2 连接脚本
SECTIONS {
. = 0x00000000;
.text : { *(.text) }
.data : ALIGN(4) {*(.data)}
.bss : ALIGN(4) {*(.data)}
}
3.3 Makefile
path=./src/
all: Inte
Inte: start.o main.o
arm-linux-ld -Tinte.lds $(path)start.o $(path)main.o -o $(path)Inte
arm-linux-objcopy -O binary $(path)Inte $(path)inte.bin
start.o: $(path)start.S $(path)main.o
arm-linux-gcc -c $(path)start.S -o $(path)start.o
main.o: $(path)main.c
arm-linux-gcc -c $(path)main.c -o $(path)main.o
clean:
rm -f $(path)*.o $(path)Inte $(path)inte.bin
3.4 C函数
/*
* mian.cpp
*
* Created on: 2016年12月31日
* Author: chy
*/
//led
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
//key
#define GPGCON (*(volatile unsigned long *)0x56000060)
#define GPGDAT (*(volatile unsigned long *)0x56000064)
//interrupt
#define EXTINT1 (*(volatile unsigned long *) 0x5600008c)
#define EINTMASK (*(volatile unsigned long *)0x560000a4)
#define EINTPEND (*(volatile unsigned long *)0x560000a8)
#define INTMSK (*(volatile unsigned long *) 0X4A000008)
#define INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define SRCPND (*(volatile unsigned long *) 0X4A000000)
#define INTPND (*(volatile unsigned long *) 0X4A000010)
volatile char f[3] = {0,0,0};
//看门狗
#define WTCON (*(volatile unsigned long *) 0x53000000)
void close_dog()
{
WTCON = 0x0;
}
void init_gpio()
{
//LED
GPBCON &= (~(0x3 << 10 | 0x3 << 12 | 0x3 << 14));
GPBCON |= (0x1 << 10 | 0x1 << 12 | 0x1 << 14);
GPBDAT |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7);
//KEY
GPGCON &= (~(0x3 << 0 | 0x3 << 6 | 0x3 << 10));
GPGCON |= (0x2 << 0 | 0x2 << 6 | 0x2 << 10);
}
void init_irq()
{
EXTINT1 |= (0x2 << 0 | 0x2 << 12| 0x2 << 20); //下降沿触发
EINTMASK &= (~((0x1 << 8) | (0x1 << 11) | (0x1 << 13))); //开外部中断
INTMSK &= (~(0x1 << 5)); //开中断
}
void irq_interrupt()
{
unsigned int inte_no = INTOFFSET; //获取正在执行的中断编号
unsigned int einte_no = EINTPEND; //获取正在执行的外部中断
if(einte_no & (0x1 <<8)){
if(f[0])
GPBDAT |= (0x1 << 5);
else
GPBDAT &= ~(0x1 << 5);
f[0] = !f[0];
}
if(einte_no & 0x1 << 11){
if(f[1])
GPBDAT |= (0x1 << 6);
else
GPBDAT &= ~(0x1 << 6);
f[1] = !f[1];
}
if(einte_no & 0x1 << 13){
if(f[2])
GPBDAT |= (0x1 << 7);
else
GPBDAT &= ~(0x1 << 7);
f[2] = !f[2];
}
EINTPEND = (1<<8) | (1<<11) | (1<<13);
SRCPND |= 0x1 << inte_no; //写1清除标志位
INTPND |= 0x1 << inte_no; //写1清除标志位
}
int main()
{
while(1);
return 0;
}