因为中断会设计到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 }
1、跳转的地址向量要提前设置好2、通知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 ;
1、当 IRQ 异常发生的时候,cpu 跳转到 0x182、背景知识:reset 0 地址被映射 map 到 iROM (内容不可修改)0 地址 在 iROM 中 (0xD0000000)iRAM (0xD0020000) -> 0x20000 (iROM 被映射到了 0x20000)通过 md 命令,查看相关内存单元值,发现 0x18: 0xEA000018经过一系列分析,最终在 iROM 中的跳转指令会加载从 0xD0037400 地址开始的值,作为异常发生后要跳转的地址+offset ,因此只需要修改 0xD0037418 的向量即可。3、(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(' '); }