介绍一种简练、高效、灵敏的ARM异常中止处理办法。
在ARM中,因为一切的中止都运用同一个异常中止进口地址,即0x00000018。因而需求在异常中止处理程序中依据相应的中止号调用对应的中止服务函数。
一般有两种处理方式:
1. 在汇编中保存现场,然后调用C言语编写的中止处理程序,使命处理完结之后,再回来到汇编中康复现场,并回来到断点。其间C言语编写的中止处理程序,经过switch句子对INTOFFSET进行判别,然后散转履行对应的服务函数。
import IRQ_EXCEPTION
0x00000018 LDR PC,=IRQ_ENTRY
IRQ_ENTRY
STMFD SP!,{R0-R8,LR}
BL IRQ_EXCEPTION
LDMFD SP!,{R0-R8,LR}
SUBS PC,LR,#4
void IRQ_EXCEPTION()
{
switch(INTOFFSET)
{
case 0:
break;
case 1:
break;
}
}
缺陷:1)一切的中止处理函数都必须在这个C文件中界说。
2)中止处理函数不能再程序履行进程中被替换。
3)因为不知道中止处理函数用到了哪些寄存器,因而维护现场时,需求把或许用到的一切作业寄存器
都维护起来。再加上C言语中的判别,这些过程都会添加中止呼应时刻。
2. 运用关键字__irq来界说每个中止处理函数,由编译器来刺进维护现场及中止回来的代码,因为编译器知道此函数用到了哪些寄存器,因而它只维护被用到的寄存器。接下来的问题是,当发生中止时,怎么直接调用对应的中止处理函数?
一般会在内存中分配32*4个存储单元,寄存每个中止处理函数的首地址,在汇编中,直接依据INTOFFSET从中止处理函数向量表中取出对应的函数首地址送给PC,直接调用对应的中止处理函数。C言语中需求借用函数指针将中止处理函数首地址写入到中止处理函数向量表里的对应方位上。
IRQ_HandlerStart EQU 0x33FFFF00
0x00000018 LDR PC,=IRQ_ENTRY
………… ………………………………
IRQ_ENTRY
SUB SP,SP,#4 ;为寄存中止处理函数首地址留出空间
STMFD SP!,{R0,R1,R2} ;维护下面的算法用到的作业寄存器
LDR R0,=INTOFFSET
LDR R1,[R0] ;取出中止号
LDR R2,=IRQ_HandlerStart
ADD R0,R2,R1,LSL #2 ;核算中止号对应的中止处理函数在向量表中的方位
LDR R1,[R0] ;取出对应的中止处理函数首地址
STR R1,[SP,#12] ;存储到方才预留的空间里
LDMFD SP!,{R0,R1,R2,PC} ;出栈,数据从左向右康复,最终将中止处理函数首地址给PC
#define ISR_StartAddr 0x33FFFF00
#define pISR_EINT0 (*(unsigned *)(ISR_StartAddr+0*4))
#define pISR_UART0 (*(unsigned *)(ISR_StartAddr+28*4))
void IniTISR()
{
pISR_EINT0 = EINT0_Handler;
pISR_TIMER0 = UART0_Handler;
}
void __irq EINT0_Handler()
{
………………
}
void __irq UART0_Handler()
{
………………
}
缺陷:1)要确保汇编与C中界说的中止处理函数向量表的首地址相同
2)要界说很多个函数指针,编写起来比较费事
咱们能够将中止处理函数向量表当作一个具有32个成员的数组,每个成员都是函数指针,指向的是无形参、无回来值的中止处理函数。咱们能够在汇编顶用SPACE关键字来界说这个函数指针数组变量,并为其分配空间。 在C言语中只需求用extern声明下它是外部界说的即可。
0x00000018 LDR PC,=IRQ_ENTRY
………… ………………………………
IRQ_ENTRY
SUB SP,SP,#4 ;为寄存中止处理函数首地址留出空间
STMFD SP!,{R0,R1,R2} ;维护下面的算法用到的作业寄存器
LDR R0,=INTOFFSET
LDR R1,[R0] ;取出中止号
LDR R2,=INTVECTOR ;获取函数指针数组首地址
ADD R0,R2,R1,LSL #2 ;核算中止号对应的中止处理函数在向量表中的方位
LDR R1,[R0] ;取出对应的中止处理函数首地址
STR R1,[SP,#12] ;存储到方才预留的空间里
LDMFD SP!,{R0,R1,R2,PC} ;出栈,数据从左向右康复,最终将中止处理函数首地址给PC
AREA INTVECT,DATA
INTVECTOR SPACE 32*4
为了将此函数指针数组变量分配到内存中,需求在涣散加载文件中指定这个段的履行域在内存空间
VECT_REGION 0x33FFFF00
{
StartUp.o(INTVECT)
}
typedef void __irq (*INTFUNC)(void); //函数指针类型重界说,
extern INTFUNC INTVECTOR[32];
void InitiISR()
{
INTVECTOR[0] = EINT0_Handler;
INTVECTOR[28] = UART0_Handler;
}
void __irq EINT0_Handler()
{
………………
}
void __irq UART0_Handler()
{
………………
}
特色:1)只需求在涣散加载文件中对这个中止处理函数向量表的首地址指定一次,防止犯错。
2)运用函数指针数组,省掉多个函数指针的界说。
3)在程序履行进程中,能够经过修正函数指针数组里的内容替换中止处理函数。
4)能够再界说一个中止注册函数,进步程序的灵敏性。
void ISR_Register(INT8U num,INT32U addr)
{
INTVECTOR[num] = addr;
}
以上说到的变量都能够只放在interrupt.c中,不同的中止处理函数能够在不同的文件中编写,它们只需求调用ISR_Register即可。这样能够进步程序的结构化。
别的,还能够将中止号用#define界说一下,以进步程序的可读性,如下:
#define INT_TIMER0 10
#define INT_UART0 28
#define INT_RTC 30
INTVECTOR[INT_UART0] = UART0_Handler;
INTVECTOR[INT_RTC] = RTC_Handler;