可以說中斷是計算機的靈魂,學習中斷、使用中斷十分的重要。
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;
}