分类:
2008-09-09 17:38:46
在设计涉及到外中断的时候,比如按键中断就要研究一下怎么编了:
这里对EINT4567研究:
首先ARM芯片要中断设置要是能中断向量,然后当有IRQ中断来之后,CPU自动的到0x18地址处取指。0x18处的指令呢是CPU根据中断源算好的(比如:中断EINT4567来了,那么0x18处的指令就是跳转到地址0x30处)。然后就执行“ldr pc,=HandlerEINT4567”这条指令。这条指令的执行结果就是跳转到 “HandlerEINT4567 HANDLER HandleEINT4567”处执行。这条是宏指令,你可以看一下宏定义。执行结果就是跳转到HandleEINT4567处执行。
那么HandleEINT4567处又是什么指令呢?这就要联系44b.h文件的#define pISR_EINT4567 (*(unsigned *)(_ISR_STARTADDRESS+0x74))定义看了。HandleEINT4567处的地址就是“_ISR_STARTADDRESS+0x74”。到此还不知道这个地址对应的指令是什么。
这时候就要去看Target.c文件的中断初始化了,其中pISR_EINT4567= (unsigned) OSEINT4567ISR;这条语句就解释了中断去向何处。 OSEINT4567ISR就是在OS_CPU_A.s里面定义的中断处理程序了。
一、 关于44B0中断系统。
44B0中断系统中有两张中断转移表,经过二重转移才跳到中断处理程序。第一张中断向量表由硬件决定,所在区域为ROM(flash),地址空间从0X00开始,其中0X00-0X1C为异常向量入口地址,0X20-0XC0为中断向量入口地址。另一张中断向量表在RAM中,可以随便改,其位置在程序连接后才定。
二、 如何从第一张中断向量表跳到第二张中断向量表。
由于RAM放在地址空间的高端(距离中断向量超过了32M),故在第一张中断向量表对应位置上写上
ldr PC,# interrupt_service
如:ldr PC,=HandlerEINT4567
三、如何在启动程序中设置异常向量,中断向量表。如何把C语言中的一个中断函数对应到汇编的中断向量表中,示意图及举例如下:
#define _ISR_STARTADDRESS 0xc7fff00 //GCS6:16M bit DRAM/SDRAM
#define pISR_EINT4567 (*(unsigned *)(_ISR_STARTADDRESS+0x74))
.macro HANDLER HandleLabel
sub sp, sp, #4
stmfd sp!, {r0}
ldr r0,=\HandleLabel
ldr r0, [r0]
str r0, [sp,# 4]
ldmfd sp!, {r0,pc}
.endm
.text
ENTRY:
b ResetHandler ……
VECTOR_BRANCH:
ldr pc,=HandlerEINT0 ……
ldr pc,=HandlerEINT4567 /* 0x30 */
HandlerEINT4567:HANDLER HandleEINT4567
.equ HandleEINT4567, _ISR_STARTADDRESS+4*29
把C语言中的一个中断函数对应到汇编的中断向量表中
pISR_EINT4567 = (int)Eint4567Isr;
其实异常向量就是中断向量,ARM7的内核实际上只有8个(1个保留)异常向量,对于众多的中断源,ARM7的内核是通过IRQ、FRQ的软件查询中断状态寄存器的位来获得ISR的起始地址。而44B0为了克服这种方式所带来的中断延迟,就加入了更多的中断向量表(0x20到0xc0),要使用这种方式,必须在中断控制寄存器中设置每个中断源的方式为IRQ方式,且使用向量中断。
S3C44B0X的中断控制器有30个中断源。S3C44B0X支持新的中断处理模式称为(vectored interrupt mode),在多个中段请求发生时,由硬件优先级逻辑确定应该有哪个中断得到服务,同时硬件逻辑使中断相量表的跳转指令加载到(0X18或0X1C)位置,在该位置执行跳转指令使程序跳到相应的中断服务线程,因此相对与传统的ARM的软件方法能够大大减少中断进入延时。有两种类型的中断模式,FIQ (快速中断)和IRQ.所有的中断源在中断请求时应该确定使用的
中断模式。在网络上广为流传的44b0开发板例程中,大部分使用的都是IRQ中断模式(请查阅
寄存器rINTCON)。
一般来讲,使用44b0开发板进行调试时,无需更改44b.h,44blib.h,def.h,option.h,44binit.s,44blib.c,44blib_a.s,memcfg.s,option.s等程序,甚至无需看懂,即可编程使用44b0开发板。如前所述,硬件逻辑使中断相量表的跳转指令加载到(0X18或0X1C)位置,在该位置执行跳转指令使程序跳到相应的中断服务线程,用户只需定义相应的中断服务程序即可。中断向量表的定义如下
(节选,请看44b.h)
/* ISR */
#define pISR_RESET (*(unsigned *)(_ISR_STARTADDRESS+0x0))
#define pISR_UNDEF (*(unsigned *)(_ISR_STARTADDRESS+0x4))
#define pISR_SWI (*(unsigned *)(_ISR_STARTADDRESS+0x8))
#define pISR_PABORT (*(unsigned *)(_ISR_STARTADDRESS+0xc))
#define pISR_DABORT (*(unsigned *)(_ISR_STARTADDRESS+0x10))
#define pISR_RESERVED (*(unsigned *)(_ISR_STARTADDRESS+0x14))
#define pISR_IRQ (*(unsigned *)(_ISR_STARTADDRESS+0x18))
#define pISR_FIQ (*(unsigned *)(_ISR_STARTADDRESS+0x1c))
.........................................
#define pISR_EINT4567 (*(unsigned *)(_ISR_STARTADDRESS+0x74))
#define pISR_EINT3 (*(unsigned *)(_ISR_STARTADDRESS+0x78))
#define pISR_EINT2 (*(unsigned *)(_ISR_STARTADDRESS+0x7c))
#define pISR_EINT1 (*(unsigned *)(_ISR_STARTADDRESS+0x80))
#define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x84))
用户定义相应的中断服务程序,可以参考该语句,
pISR_EINT4567=(unsigned)Key_Int;;//将外部中断4567产生的中断,指向函数Key_Init
当然,用户也可以根据自己的爱好,任意改变函数名,
pISR_EINT4567=(unsigned)MyCat; //函数名由用户定义
最好在程序开头声明一下,
void __irq Mycat(void);;;
显然,至此可以基本理解44b0的中断,是如何与程序中的中断服务子程序联系起来的了。中断的硬件逻辑,将检测到的中断,以某种方式指向中断服务程序的地址,该地址在头文件中以宏定义的形式出现。用户在自己的程序中,将中断服务子程序的地址付给该指针,从而将其联系起来。
ARM7TDMI在矢量模式下,当从0X18地址处取指令时候,中断控制器会在数据总线上加载分支指令,这些分支指令使程序计数器能够对应到每一个中断源的向量地址。这些跳转到每一个中断源向量地址的分支指令由中断控制器产生。
例如:假设EINT0是IRQ中断,EINT0的向量地址为:0X20(见向量表),那么中断控制器必须产生0X18---0X20的分支指令。 中断控制器产生的机器码为: 0XEA000000。在各个中断源对应的中断向量地址中,存放着跳转到相应中断服务程序的程序代码。在相应向量地址处分支指令的机器代码如下计算:
矢量中断模式的机器指令代码=0XEA000000+((<目标地址>-<向量地址>-0X8)>>2)
机器代码一般由反汇编后自动产生。
结合这些,再看看程序中的代码。在无矢量中断模式,通过分析IISPR/FISPR寄存器,IRQ/FIQ处理器将移动PC到相应的ISR。HandleXXX地址包含每个响应的ISR程序的起始地址。(见 44binit.S 文件)
编译器它自动有设置的(初始化的伪编译宏),
|Image$$RO$$Limit|:表示RO区末地址后面的地址,即RW数据源的起始地址
|Image$$RW$$Base|:RW区在RAM里的执行区起始地址,也就是编译器选项RW_Base指定的地址
|Image$$ZI$$Base|:ZI区在RAM里面的起始地址
|Image$$ZI$$Limit|:ZI区在RAM里面的结束地址后面的一个地址
程序先把ROM里|Image$$RO$$Limt|开始的RW初始数据拷贝到RAM里面|Image$$RW$$Base|开始的地址,当RAM这边的目标地址到达|Image$$ZI$$Base|后就表示RW区的结束和ZI区的开始,接下去就对这片ZI区进行清零操作,直到遇到结束地址|Image$$ZI$$Limit|
下面举例一个实际的程序设计方法:
ARM44B0实验:外部中断测试实验
一、实验预备知识
实现外部中断编程的一般步骤:
(1)I/O口设置
rPCONG=0xff00;/0xffff;keyint 4~7
rPUNPG=0x00; 采用内部上拉。
(2)外部中断触发模式设置:
rEXTINT=0x22222222; //Falling edge mode
rEXTINT=0x4444444;//Rising edge mode
rEXTINT=0x777777;//Both Edge trigger mode
rEXTINT=0x0;// Low level trigger mode
(2) EXINT reg 设置
将EXINT 4~7设置为IRQ mode
rINTCON=0x5;
rINTMOD=0x0; //All=IRQ mode
修改中断处理指针
pISR_EINT4567=(unsigned)Key_Int;
开启中断
rINTMSK=~(BIT_GLOBAL|BIT_EINT4567); //start INT
中断测试程序编写:
oid Key_Init(void)
{
rINTCON=0x5;
rINTMOD=0x0; //All=IRQ mode
// rEXTINT=0x22222222; //Falling edge mode
rEXTINT=0x0; //"0" level mode
rPCONG=0xffff; //EINT7~0
rPUPG=0x0; //pull up enable
pISR_EINT4567=(unsigned)Key_Int;
rINTMSK=~(BIT_GLOBAL|BIT_EINT4567); //start INT
}
编写好了,编写中断处理程序:
oid __irq Key_Int(void)
{
which_int=rEXTINTPND;//识别是那个中断
rEXTINTPND=0xf; //clear EXTINTPND reg.
rI_ISPC=BIT_EINT4567; //clear pending_bit
……(处理模块)
}
本次实验任务:按键中断,并使相应LED亮起来,按EXINT4则第一个LED亮,依次类推。
处理程序
switch(which_int)
{
case 1:
Uart_Printf("EINT4 had been occured...\n");
Led_Display(1);
which_int=0;
break;
case 2:
Uart_Printf("EINT5 had been occured...\n");
Led_Display(2);
which_int=0;
break;
case 4:
Uart_Printf("EINT6 had been occured...\n");
Led_Display(4);
which_int=0;
break;
case 8:
Uart_Printf("EINT7 had been occured...\n");
Led_Display(0);
which_int=0;
break;
default :
break;
}
//注意理解上面程序。
实验结果:
***********************************************************************
FS44B0X
FS44B0X key1 key2 key3 key4!EINT4 had been occured...
EINT4 had been occured...
EINT4 had been occured...
EINT4 had been occured...
EINT4 had been occured...
EINT4 had been occured...
EINT4 had been occured...
*********************************************************************************
开发板上LED的闪亮随按键依次变化。
实验例程序附录如下:
#i nclude "option.h"
#i nclude "def.h"
#i nclude "44b.h"
#i nclude "44blib.h"
volatile char which_int=0;
void __irq Key_Int(void)
{
which_int=rEXTINTPND;
rEXTINTPND=0xf; //clear EXTINTPND reg.
rI_ISPC=BIT_EINT4567; //clear pending_bit
switch(which_int)
{
case 1:
Uart_Printf("EINT4 had been occured...\n");
Led_Display(1);
which_int=0;
break;
case 2:
Uart_Printf("EINT5 had been occured...\n");
Led_Display(2);
which_int=0;
break;
case 4:
Uart_Printf("EINT6 had been occured...\n");
Led_Display(4);
which_int=0;
break;
case 8:
Uart_Printf("EINT7 had been occured...\n");
Led_Display(0);
which_int=0;
break;
default :
break;
}
}
void Key_Init(void)
{
rINTCON=0x5;
rINTMOD=0x0; //All=IRQ mode
// rEXTINT=0x22222222; //Falling edge mode
rEXTINT=0x0; //"0" level mode
rPCONG=0xffff; //EINT7~0
rPUPG=0x0; //pull up enable
pISR_EINT4567=(unsigned)Key_Int;
rINTMSK=~(BIT_GLOBAL|BIT_EINT4567); //start INT
}
void Main(void)
{
rSYSCFG=CACHECFG; // Using 8KB Cache//
Port_Init();
Uart_Init(0,57600);
Delay(10);
Uart_Select(0); //Select UART0
Led_Display(0x07);
Uart_Printf("\nFS44B0X");
Uart_Printf("\n按键中断演示程序");
Uart_Printf("\n按FS44B0X开发板上的按键 key1 key2 key3 key4!");
Key_Init();
while(1);
}
注意:处理程序随便放那个函数里都可以。