您的位置:首页 > 其它

TX2440 看手册学习2440-深入理解中断处理机制(ADS1.2编译)

2013-05-07 02:02 435 查看
TX2440 看手册学习2440-深入理解中断处理机制(ADS1.2编译)

在中断控制器中有五个控制寄存器:中断源未决寄存器,中断模式寄存器,屏蔽寄存器,优先级寄存器和中断未决寄存器.

一.支持60个中断源:

中断源未决寄存器:一个SRCPND中断源未决寄存器只有32位,是根本不够放60个中断源的.所以三星公把一些相似的中断源放到另外一些寄存器里成为一组,给这些组员集合定一个组名放到SRCPND中断源未决寄存器里.如:外部中断4-7,共有4个中断源,它们的组名在SRCPND中断源未决寄存器里叫做EINT4_7.看下图:



图(1)

二.中断的优先级:

优先级寄存器:通过设置寄存器PRIORITY配置参数.

如图(1)所示.把这32个主中断源分6个一级仲裁器,而又把这6个一级仲裁器组成1个二级仲裁器,加起来总共有7个仲裁器,如图(2),每个仲裁器都有配置优先规则.



图(2)

每个仲裁器基于一个位仲裁器模式控制(ARB_MODE)和选择控制信号(ARB_SEL)的

两位来处理6 个中断请求。

如果 ARB_SEL位是 00b ,优先级是 REQ0 ,REQ1 ,REQ2 ,REQ3 ,REQ4 ,和REQ5.

如果 ARB_SEL位是 01b ,优先级是 REQ0 ,REQ2 ,REQ3 ,REQ4 ,REQ1 ,和REQ5.

如果 ARB_SEL位是 10b ,优先级是 REQ0 ,REQ3 ,REQ4 ,REQ1 ,REQ2 ,和REQ5.

如果 ARB_SEL位是 11b ,优先级是REQ0 ,REQ4 ,REQ1 ,REQ2 ,REQ3,和REQ5.

注意仲裁器的REQ0 总是有最高优先级,REQ5 总是有最低优先级。此外通过改变

ARB_SEL 位,我们可以翻转REQ1 到REQ4 的优先级。

如果ARB_MODE 位置0,ARB_SEL 位不会自动改变,使得仲裁器在一个固定优先级的模

式下操作(注意在此模式下,我们通过手工改变ARB_SEL 位来配置优先级)。另外,如

果ARB_MODE 位是1 ,ARB_SEL 位以翻转的方式改变。例如如果REQ1 被服务,则

ARB_SEL 位自动的变为01b ,把REQ1 放到最低的优先级。ARB_SEL 变化的详细规则如

下:

如果REQ0 或REQ5 被服务,ARB_SEL位完全不会变化。

如果REQ1 被服务,ARB_SEL位变为01b 。

如果REQ2 被服务,ARB_SEL位变为10b 。

如果REQ3 被服务,ARB_SEL位变为11b 。

如果REQ4 被服务,ARB_SEL位变为00b 。

三.中断处理机制:

中断模式寄存器:该INTMOD寄存器包括32位,每位与一个中断源相关。如果某位置1,相应的中断将在FIQ 模式下处理。否则在IRQ 模式下操作。请注意仅有一个中断源能够在FIR模式下服务,也就是说,INTMOD 仅有一个位可以被置1 。FIQ 比任何一个IRQ的优先级都要高.

屏蔽寄存器:该INTMSK寄存器包括32位,每个都是和一个中断源相关。如果某位置1,则CPU不会服务相应中断源的中断请求(注意SRCPND的相应位还是会被置1 )。如果屏蔽位为0 ,中断请求可以被服务。

中断未决寄存器:INTPND中断未决寄存器的32位显示是否相应的中断请求有最高优先级,其中断请求未屏蔽且在等待中断服务。因为INTPND 寄存器位于优先级逻辑之后,仅 1 位可以被置1,且中断请求生成对CPU的IRQ 。在对于IRQ 的中断服务程序中,我们可以读取寄存器决定那个中断源被服务。

在中断控制器中有五个控制寄存器:中断源未决寄存器,中断模式寄存器,屏蔽寄存器,优先级寄存器和中断未决寄存器,这5个寄存器都是主中断源的寄存器.在子中断源中还会包含子中断源未决寄存器,子中断屏蔽寄存器.

我们先来看一下中断流程图,如图(3):



图(3)

中断源分为2种,子中断中断源和中断源,当一个子中断产生一个中断信号,子中断源挂起寄存器(SUBRCPND)相应位自动置1,察看子中断屏蔽寄存器(SUBMASK)该子中断是否被屏蔽(人工设置),如果没屏蔽,则中断源寄存器(SRCPND)置1,察看该中断源是否被屏蔽,如果没被屏蔽,如果采用的是FIQ模式(仅有一个),就直接中断挂起寄存器置1,产生IRQ信号;否则采用IRQ模式,进行优先级判断后,高优先级的执行,中断挂起寄存器置1,产生IRQ信号。同时CPSR寄存器的I位置1,表明当前有一个IRQ中断产生。记得以前让大家注意该寄存器中的I和Q位了吧,他的作用就在这

下面我们结合2440init.s(2440init.s不熟悉的话可以看我以前写的教程)和datasheet来深入分析一下中断的处理机制,首先我们看两张图,图(4)和图(5).是CPU硬件中断请求的入口地址和优先级:



图(4)



图(5)

我们以IRQ中断为例,如图(4),IRQ的硬件中断请求入口地址0x00000018,也就是说,硬件收到请求后,会跳到这个地址开始执行.这个地址有什么,我们来看一下2440init.s汇编后的地址.如下图(6)



图(6)

很明显,在0x00000018地址的内存是一个跳转指令.图(6)汇编源码是这样的:

.........

b HandlerUndef ;handler for Undefined mode

b HandlerSWI ;handler for SWI interrupt

b HandlerPabort ;handler for PAbort

b HandlerDabort ;handler for DAbort

b . ;reserved ;//0x00000014: eafffffe .... B {pc} ; 0x14

b HandlerIRQ ;handler for IRQ interrupt

b HandlerFIQ ;handler for FIQ interrupt

.......

HandlerFIQ HANDLER HandleFIQ

HandlerIRQ HANDLER HandleIRQ

HandlerUndef HANDLER HandleUndef

HandlerSWI HANDLER HandleSWI

HandlerDabort HANDLER HandleDabort

HandlerPabort HANDLER HandlePabort

........

学过arm汇编编程的话就很容易理解这段代码的意思.

说了哪么多,入正题,中断的处理过程:

第一步:CPU每执行一条指都会检查CPSR寄存器,当发现I和F位有1位被置位时,就进行中断处理.第一步会跳到异常向量表(图4),以IRQ为例,就会运行b HandlerIRQ 这条指令.然后就进入HANDLER分解出来的函数HandlerIRQ ,代码如下:

.........

;这是共用宏.相当于多个子函数.子函数个数取决于标号$HandlerLabel的多少.而$HandleLabel只是传进来的参数.HANDLER是这个宏的宏名.

MACRO

$HandlerLabel HANDLER $HandleLabel

$HandlerLabel

;这个宏的作用是调用中断入口服务程序.

sub sp,sp,#4 ;栈空间腾出4个字节出来.准备把中断入口程序压栈.(原来sp-4)

stmfd sp!,{r0} ;(先减后压),把sp指再往下递增4个字节后,把r0压栈后,再把减后的数值写回sp.(原来sp-8),也就是说,r0放在原来sp-8的位置上.

ldr r0,=$HandleLabel ;把一些中断的首地址(硬件地址)加进来给r0.如:HandleFIQ,HandleIRQ等等.

ldr r0,[r0] ;把中断程序的首地址(硬件地址)的内容(中断入口服务程序的地址)给r0

str r0,[sp,#4] ;把中断入口服务程序的地址放到栈空间sp-4的地方.

ldmfd sp!,{r0,pc} ;(先弹后增)出栈,恢复原来的r0(原来sp-8)和把中断入口服务程序的地址给pc(原来sp-4),最后,(原来sp-0)

MEND

............

^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00

HandleReset # 4

HandleUndef # 4

HandleSWI # 4

HandlePabort # 4

HandleDabort # 4

HandleReserved # 4

HandleIRQ # 4

HandleFIQ # 4

;Do not use the label 'IntVectorTable',

;The value of IntVectorTable is different with the address you think it may be.

;IntVectorTable

;@0x33FF_FF20

HandleEINT0 # 4

HandleEINT1 # 4

HandleEINT2 # 4

HandleEINT3 # 4

HandleEINT4_7 # 4

HandleEINT8_23 # 4

..............

;;;;;;第二步,跳转到二级向量表.

IsrIRQ

sub sp,sp,#4 ;reserved for PC

stmfd sp!,{r8-r9}

;===================================================================================

;这查表方法够好了吧,r8(入口)+index*4(别望了一条指令是4 bytes的喔),

;这不就是我们要找的那一项了吗.找到了表项,下一步做什么?肯定先装入了!

;==================================================================================

ldr r9,=INTOFFSET

ldr r9,[r9]

ldr r8,=HandleEINT0

add r8,r8,r9,lsl #2 ;//地址对齐,因为每个中断向量占4个字节,即isr = IvectTable + Offeset * 4 也就是查MAP表 _ISR_STARTADDRESS

ldr r8,[r8] ;//装入中断服务程序的入口

str r8,[sp,#8] ;//把入口也入栈,准备用旧招

ldmfd sp!,{r8-r9,pc} ;//施招,弹出栈,哈哈,顺便把r8弹出到PC了,跳转成功!

.................

*****下面是C代码,把中断服务程序进行注册连接一下**********:

...........

#define pISR_EINT4_7 (*(unsigned *)(_ISR_STARTADDRESS+0x30))

............

pISR_EINT4_7=(unsigned)EintHandler; //外部中断4_7中断服务子程序入口地址

...........

void __irq EintHandler(void)

{

if(rINTPND==BIT_EINT4_7)

{

ClearPending(BIT_EINT4_7);

if(rEINTPEND&(1<<4))

{

Uart_Printf("eint 4\n");

rGPFDAT = LED1ON;

Delay(500);

rGPFDAT = LEDOFF;

rEINTPEND |= 1<< 4;

}

if(rEINTPEND&(1<<5))

{

Uart_Printf("eint 5\n");

rGPFDAT = LED2ON;

Delay(500);

rGPFDAT = LEDOFF;

rEINTPEND |= 1<< 5;

}

if(rEINTPEND&(1<<6))

{

Uart_Printf("eint 6\n");

rGPFDAT = LED3ON;

Delay(500);

rGPFDAT = LEDOFF;

rEINTPEND |= 1<< 6;

}

if(rEINTPEND&(1<<7))

{

Uart_Printf("eint 7\n");

rGPFDAT = LED4ON;

Delay(500);

rGPFDAT = LEDOFF;

rEINTPEND |= 1<< 7;

}

}

}

..........

关于__irq 的使用

__irq为一个标识,用来表示一个函数是否为中断函数。对于不同的编译器,__irq在函数名中的位置不一样,例如:

ADS编译器中 : void __irq IRQ_Eint0(void);

Keil编译器中 : void IRQ_Eint0(void) __irq;

但是其意义一样,它所完成的任务是标识该函数为中断函数,在编译器编译是调用此函数时,先保护函数入口现场,然后执行中断函数,

函数执行完毕,恢复中断现场,这整个过程不需要用户重新编写代码来完成,由编译器自动完成。因而这也给不具备中断嵌套功能的ARM系统带来

了问题,若使用 __irq 时有中断嵌套产生,这现场保护就会混乱。在前一篇日志“LPC2000系列中断嵌套处理”中,自己编写中断入口现场保护代

码,并不使用 __irq 标识符号,就是这个原因。

总结如下:

1、若不想自己编写中断入口现场保护代码,而且使用中无中断嵌套,在中断函数中用 __irq 来标识我们的中断函数,否则出错;

2、若程序中要使用中断嵌套,对于无中断嵌套功能的ARM来说,一定要自己编写中断入口现场保护代码,而且不能用 __irq 标识我们的

中断函数,否则出错。

最后结合上面的代码,我们看一下在内存中中断是怎么跳转的.如图(7)



图(7)

哈哈.终于完成了中断的跳转.中断寄存器无非就是主中断源的中断源未决寄存器,中断模式寄存器,屏蔽寄存器,优先级寄存器和中断未决寄存器,和子中断源的中断源未决寄存器,子中断屏蔽寄存器,共7个,看懂就能用中断写程序了.另注意:中断标志位的清除顺序:SUBSRCPND-->SRCPND-->INTPND.

容易混绞点:

看门狗:

1)

rSRCPND |= (1<<9); ///源未决, 一定需要设置

rINTPND |= (1<<9); ///中断未决, 一定需要设置

rSUBSRCPND |= (1<<13); ///子中断未决, 查看rINTPND是否有中断复用口,有就要设置.

2)

rINTSUBMSK &= ~(1<<13); ///子中断屏蔽, 查看rINTPND是否有中断复用口,有就要设置.

rINTMSK &= ~(1<<9); ///中断屏蔽, 一定需要设置

外部中断GPF7

1)

rSRCPND |= (1<<4); ///源未决, 一定需要设置

rINTPND |= (1<<4); ///中断未决, 一定需要设置

rEINTPEND |= (1<<7); ///子中断未决, 查看rINTPND是否有中断复用口,有就要设置.

2)

rEINTMASK &= ~(1<<7); ///子中断屏蔽, 查看rINTPND是否有中断复用口,有就要设置.

rINTMSK &= ~(1<<4); ///中断屏蔽, 一定需要设置

3)同理,其它也就一样的,有中断复用,就必须找到子中断PND和子中断MSK来设置.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: