由於中斷會設計到ARM內核工做模式的切換,因此先簡要介紹一下各個模式:ARM模式的切換要設計到寄存器CPSR,下面是各個位表示的含義,CPSR[4:0]是工做模式切換控制位。T=0時是ARM指令模式,T=1時是Thumb指令模式。F=0時是容許FIQ,F=1是禁止FIQI=0時是容許IRQ,I=1是禁止IRQ
cpsr[4:0]
|
處理器模式
|
英文表示
|
1,0000
|
用戶模式
|
usr
|
1,0001
|
快速中斷模式
|
fiq
|
1,0010
|
中斷模式
|
irq
|
1,0011
|
管理模式
|
svc
|
1,0111
|
停止模式
|
abt
|
1,1011
|
未定義
|
und
|
1,1111
|
系統模式
|
sys
|
MRS R0,CPSR ;把CPSR讀取到R0BIC R0,#0x1f ;低5位清零LDR R1,=MODE_Fiq ;設置R1 爲0b10001,跳轉到fiq模式ORR R0,R0,R1 ;R0和R1相或,設置低5位MSR CPSR_c,R0 ;把R0的值從新賦值到CPSRLDR SP,=Stact_Fiq ;設置fiq的棧指針BIC R0,#0x1f ;低5位清零LDR R1,=MODE_Irq ;跳轉Irq模式ORR R0,R0,R1MSR CPSR_c,R0LDR SP,=Stact_Irq ;設置irq的棧指針BIC R0,#0x1fLDR R1,=MODE_Svc ;跳轉到svc模式ORR R0,R0,R1MSR CPSR_c,R0LDR SP,=Stact_Svc ;設置svc的棧指針BIC R0,#0x1fLDR R1,=MODE_Abort ;跳轉到abort模式ORR R0,R0,R1MSR CPSR_c,R0LDR SP,=Stact_Abort ;設置abort的棧指針BIC R0,#0x1fLDR R1,=MODE_Undef ;跳轉到undef模式ORR R0,R0,R1MSR CPSR_c,R0LDR SP,=Stact_Undef ;設置undef棧指針BIC R0,#0x1fLDR R1,=MODE_Sys ;跳轉到sys模式ORR R0,R0,R1MSR CPSR_c,R0LDR SP,=Stact_Sys ;設置sys棧指針
外部中斷分組
|
對應GPIO
|
External interrupt Group 0
|
GPN0~GPN15 GPL8~GPL14 GPM0~GPM4
(16+7+5=28,因此EINT0PEND有28bit來識別這28箇中斷)
|
External interrupt Group 1
|
GPA0~GPA7 GPB0~GPB6
|
External interrupt Group 2
|
GPC0~GPC7
|
External interrupt Group 3
|
GPD0~GPD5
|
External interrupt Group 4
|
GPF0~GPF14
|
External interrupt Group 5
|
GPG0~GPG7
|
External interrupt Group 6
|
GPH0~GPH9
|
External interrupt Group 7
|
GPO0~GPO15
|
External interrupt Group 8
|
GPP0~GPP14
|
External interrupt Group 9
|
GPQ0~GPQ9
|
中斷號 | 中斷源 | 對應外部中斷 | VIC組 |
0 | INT_EINT0 | External interrupt 0~3 | VIC0 |
1 | INT_EINT1 | External interrupt 4~11 | VIC0 |
32 | INT_EINT2 | External interrupt 12~19 | VIC1 |
33 | INT_EINT3 | External interrupt 20~27 | VIC1 |
53 | INT_EINT4 | External interrupt group1~group9 | VIC1 |
按鍵 | 對應引腳 | 對應外部中斷 | 中斷源 |
KEY1 | GPN0 | External interrupt 0 | INT_EINT0 |
KEY2 | GPN1 | External interrupt 1 | INT_EINT0 |
KEY3 | GPN2 | External interrupt 2 | INT_EINT0 |
KEY4 | GPN3 | External interrupt 3 | INT_EINT0 |
KEY5 | GPN4 | External interrupt 4 | INT_EINT1 |
KEY6 | GPN5 | External interrupt 5 | INT_EINT1 |
IC
ARM7(4510)
|
IC
ARM9(2440)
|
IC
ARM11(6410)
|
|
內核
(core)
|
CPSR I-bit | CPSR I-bit |
CPSR I-bit
VIC Port(Enable)
VIC interface(PC <--> A0~A31)
|
中斷控制器
(IC)
|
INTMOD
INTPND
INTMSK
|
INTOFFSET
INTPRI
INTPND
INTMOD
INTMSK
SRCPND
|
VectADDRESS(32bit -> A0~A31,中斷函數地址的註冊 )
Vectors(handlers中斷處理函數)
Priority(優先級的判別)
VIC0IRQSTATUS/VIC0FIQSTATUS(IRQ/FIQ的中斷懸起位)
VIC0INTSELECT (選擇是IRQ仍是FIQ模式)
VIC0INTENABLE(MASK功能)
VIC0RAWINTR(顯示FIQ中斷是否置位,從EINT0PND上傳輸過來的)
|
中斷源控制器
(GPIO)
|
EINTCON
(F/R/L)
GPXCON
(EINT)
|
EINTCON
(F/R/L)
GPXCON
(EXIT)
|
INTMASK
EINT0PND (須要手動清除)
EINT0CON0
(Low/High level Falling/Rising/Both edge)
GPxCON
(EINT)
|
硬件層 | key/UART/USB/Timer | key/UART/USB/Timer | key/UART/USB/Timer |
/* set GPNIO to EINT mode , 10 --> Eint */ temp = GPNCON ; temp &= ~(0x3<<0); temp |= (0x2<<0); GPNCON = temp; /* set EINT triger mode to falling eage ,01x = Falling edge */ temp = EINT0CON0 ; temp &= ~(0x7<<0); temp |= (0x3<<0); EINT0CON0 = temp;
while (1) { for(ch='a';ch<='z';ch++){ if( (EINT0PEND & 0x1)==0x1){ //輪詢EINT0PEND有沒有被置位 uart_putchar('+'); } uart_putchar(ch); delay(); } }
PEND |= 1<<0; (not good) PEND = 0xFFFFFFFF; (not good) PEND = 1<<0; (Good!)
/* EINT0MASK[0] = 1 : Mask EINT0 */ temp = EINT0MASK ; temp &= ~(0x1<<0); temp |= (0x1<<0); EINT0MASK = temp;
/* EINT0MASK[0] = 0 : disMask EINT0 */ temp = EINT0MASK ; temp &= ~(0x1<<0); EINT0MASK = temp; uart_init(); while (1) { for(ch='a';ch<='z';ch++){ if( (VIC0RAWINTR & 0x1)==0x1){ uart_putchar('+'); } uart_putchar(ch); delay(); } }
/* EINT0MASK[0] = 0 : disMask EINT0 */ temp = EINT0MASK ; temp &= ~(0x1<<0); EINT0MASK = temp; uart_init(); while (1) { for(ch='a';ch<='z';ch++){ if( (VIC0RAWINTR & 0x1)==0x1){ uart_putchar('+'); EINT0PEND = 0x1; } uart_putchar(ch); delay(); } }
void mymain(void){ unsigned char ch; unsigned int temp=0; /* set GPNIO to EINT mode , 10 --> Eint */ temp = GPNCON ; temp &= ~(0x3<<0); temp |= (0x2<<0); GPNCON = temp; /* set EINT triger mode to falling eage ,01x = Falling edge */ temp = EINT0CON0 ; temp &= ~(0x7<<0); temp |= (0x3<<0); EINT0CON0 = temp; /* EINT0MASK[0] = 0 : disMask EINT0 */ temp = EINT0MASK ; temp &= ~(0x1<<0); //temp |= (0x1<<0); EINT0MASK = temp; /* VIC0INTENABLE[0]=1 : enable interrupt */ /* clear bit by VIC0INTENCLEAR */ temp = VIC0INTENABLE ; temp &= ~(0x1<<0); temp |= (0x1<<0); VIC0INTENABLE = temp; /* VIC0INTSELECT[0] = 0 : set to IRQ */ temp = VIC0INTSELECT ; temp &= ~(0x1<<0); VIC0INTSELECT = temp; uart_init(); while (1) { for(ch='a';ch<='z';ch++){ if( (VIC0IRQSTATUS & 0x1)==0x1){ uart_putchar('+'); EINT0PEND = 0x1; } uart_putchar(ch); delay(); } }按下KEY出現下面的現象:
/* VIC0INTENABLE[0]=0 : disable interrupt */ /* clear bit by VIC0INTENCLEAR */ temp = VIC0INTENABLE ; temp &= ~(0x1<<0); VIC0INTENABLE = temp;
/* init CPSR I-bit */ //0101,0011 __asm{ mov r0,#0x53 msr cpsr,r0 }
int val; /* init CPSR I-bit */ //0101,0011 __asm { mov val,#0x53 msr cpsr,val }
int val2; /* VIC Enable (cp15) */ __asm { mrc p15,0,val2,c1,c0,0 orr val2,val2,#(1<<24) mcr p15,0,val2,c1,c0,0 }
一、跳轉的地址向量要提早設置好二、通知ARM11內核,啓動VIC Port 功能緊接着的問題是,如何在執行完beep以後返回主程序?(假設咱們的目的是蜂鳴beep)緣由:beep程序不能做爲IRQ_handler1)保存cpu現場STMFD2)清除掉Pending bit,調用beep3)恢復cpu現場LDMFD (LR-4)->PC ,SPSR -> CPSR修改start.s,實現IRQ_handler1)IRQ模式下的sp指針須要初始化2)除了清除pending bit以外,還須要清除VIC0ADDRESS =0 ;
一、當 IRQ 異常發生的時候,cpu 跳轉到 0x18二、背景知識:reset 0 地址被映射 map 到 iROM (內容不可修改)0 地址 在 iROM 中 (0xD0000000)iRAM (0xD0020000) -> 0x20000 (iROM 被映射到了 0x20000)經過 md 命令,查看相關內存單元值,發現 0x18: 0xEA000018通過一系列分析,最終在 iROM 中的跳轉指令會加載從 0xD0037400 地址開始的值,做爲異常發生後要跳轉的地址+offset ,所以只須要修改 0xD0037418 的向量便可。三、(int)IRQ_handler -> 0xD0037400 + 0x18若是是 SWI 軟件中斷,則在 0xD0037408 處填寫swi_handler的地址
void C_IRQ_handler(void) { EINT0PEND = 0x1; uart_putchar(' '); uart_putchar('+'); uart_putchar(' '); }
/* VIC0VECTADDR[31:0] --> 0x71200100 : 0x7120017C */ /* Contains ISR vector addresses */ #define VIC0VECTADDR0 (*((volatile unsigned int *)0x71200100 )) #define VIC1VECTADDR0 (*((volatile unsigned int *)0x71300100 )) /* install IRQ handler to Vectors */ /* EINT0 --> VIC0VECTADDR[0] */ VIC0VECTADDR0 = (int)C_IRQ_handler;
import C_IRQ_handler export asm_IRQ_handler asm_IRQ_handler ldr sp,=0x58000000 ;lr=lr-4 sub r14,r14,#4 stmfd r13!,{r1-r12,r14} bl C_IRQ_handler ldmfd r13!,{r1-r12,pc}^
void C_IRQ_handler(void) { EINT0PEND = 0x1;//clear pending bit VIC0ADDRESS = 0;//clear address uart_putchar(' '); uart_putchar('+'); uart_putchar(' '); }