主题 : 关于中断问题的分析与解决 复制链接 | 浏览器收藏 | 打印
勺饮不器盛沧海,拳石频移磊泰山
级别: 侠客
UID: 15752
精华: 0
发帖: 71
金钱: 355 两
威望: 71 点
贡献值: 0 点
综合积分: 142 分
注册时间: 2010-03-09
最后登录: 2010-10-19
楼主  发表于: 2010-05-23 11:22

 关于中断问题的分析与解决

描述:图1:中断处理流程图
图片:
本人最近在研究mini2440的板上中断,也曾问过诸多问题,各位大侠的帮助,在下不胜感激!
本人的亲身感受是:关于中断的回帖并不是很多,问题分析的不是很集中,本人不材,冒昧总结几句。
第一部分 中断知识
1.S3C2440中断分类:外部中断和内部中断
(1)外部中断24个外部中断占用GPF0-GPF7(EINT0-EINT7),GPG0-GPG15(EINT8-EINT23)。用这些脚做中断输入,则必须配置引脚为中断,并且不要上拉。具体参考datesheet数据手册。
寄存器:EXTINT0-EXTINT2:三个寄存器设定EINT0-EINT23的触发方式。
        EINTFLT0-EINTFLT3:控制滤波时钟和滤波宽度。
        EINTPEND:这个是中断挂起寄存器,清除时要写1,后面还有几个是写1清除。当一个外部中断(EINT4-EINT23)发生后,那么相应的位会被置1。
        EINTMASK:这个简单,是屏蔽中断用的,也就是说位为1时,此次中断无效。
(2)内部中断。内部中断有8个寄存器
寄存器:SUBSRCPND:当一个中断发生后,那么相应的位会被置1,表示一个中断发生了。
        INTSUBMSK:中断屏蔽寄存器,具体屏蔽什么,自己看手册去吧。
        INTMOD:中断的方式。一个中断可以是普通中断,也可以是快中断,在这里设置,但只能有一个快中断。
        PRIORITY :优先级寄存器
        SRCPND :当一个中断发生后,那么相应的位会被置1,表示一个或一类中断发生了。
        INTMSK :中断屏蔽寄存器。
        INTPND :中断发生后,SRCPND中会有位置1,可能好几个(因为同时可能发生几个中断),这些中断会由优先级仲裁器选出一个最紧迫的,然后吧把INTPND中相应位置1,所以同一时间只有一位是1。也就是说前面的寄存器置1是表示发生了,只有INTPND置1,CPU才会处理。
        INTOFFSET :用来表示INTPND中哪一位置1了,好让你查询,普通中断跳转时查询用。清除INTPND、SRCPND时自动清除。
   中断的处理过程参照附图1。
2.中断过程
a 如果是不带子中断的内部中断:发生后SRCPND相应位置1,如果没有被INTMSK屏蔽,那么等待进一步处理。
b 如果是带子中断的内部中断:发生后SUBSRCPND相应位置1,如果没有被INTSUBMSK屏蔽,那么SRCPND相应位置1,等待进一步处理,几个SUBSRCPND可能对应同一个SRCPND,对应表如下:
SRCPND                        SUBSRCPND
INT_UART0                    INT_RXD0,INT_TXD0,INT_ERR0
INT_UART1                    INT_RXD1,INT_TXD1,INT_ERR1
INT_UART2                    INT_RXD2,INT_TXD2,INT_ERR2
INT_ADC                        INT_ADC_S, INT_TC
INT_CAM                        INT_CAM_C, INT_CAM_P
INT_WDT_AC97             INT_WDT, INT_AC97
c 如果是外部中断:EINT0-EINT3发生后SRCPND相应位置1,如果没有被INTMSK屏蔽,那么等待进一步处理。EINT4-EINT23发生后EINTPEND相应位置1,如果没有被EINTMASK屏蔽,那么SRCPND相应位EINT4-7 或EINT8-23置1,如果没有被INTMSK屏蔽,等待进一步处理,几个EINTPEND对应同一个SRCPND,对应表如下:
SRCPND                        EINTPEND
EINT0                             EINT0
EINT1                             EINT1
EINT2                             EINT2
EINT3                             EINT3
EINT4-7                          EINT4-EINT7
EINT8-23                        EINT8-EINT23
三种中断都等待进一步处理了。接下来从SRCPND往下看,看INTMSK。如果中断被屏蔽了,就不用说了(注意:快中断也能被屏蔽)。如果没有被屏蔽,那么会进一步到INTMOD。如果是快中断,那么直接出来,进入FIQ(即CPU进入快中断模式处理)。如果是普通中断,那么SRCPND可以有多为置1(FIQ只能有一个),这时就会经过PRIORITY选出一个优先级高的,然后把根据选出的中断把INTPND相应位置1(注意:只能选出一个),进入IRQ,让CPU处理。
3.中断的开启。
a.如果是不带子中断的内部中断,只需设置INTMSK,让它不屏蔽中断就可以了。
b 如果是带子中断的内部中断,需设置INTSUBMSK和INTMSK,让它门不屏蔽中断就可以了。
c 如果是外部中断,对于EINT8-23需要设置EINTMASK和INTMSK。对于EINT0-EINT3只需设置INTMSK。
4.中断的清除。
a.如果是不带子中断的内部中断,只需清除SRCPND,注意清除需位置1。
b 如果是带子中断的内部中断,需清除SRCPND和SUBSRCPND,注意先清除SUBSRCPND,再清除SRCPND。因为,如果你先清除SRCPND的话,然后在清除SUBSRCPND的过程中,SRCPND会以为又有中断发生,又会置1。也就是说一次中断会响应两次。所以必须先掐断源头。
c 如果是外部中断,对于EINT8-23需要清除EINTPEND和SRCPND(同样注意顺序)。对于EINT0-EINT3只需清除SRCPND。

第二部分 2440init.s中断部分分析(参考网上的一篇文章)

中断向量
    b    HandlerIRQ    ;handler for IRQ interrupt

很自然,中断向量一般放在开头,
异常服务程序这里不用中断(interrupt)而用异常(exception),毕竟中断只是异常的一种情况。所有有器件
引起的中断,例如TIMER中断,UART中断,外部中断等等,都有一个统一的入口,那就是中断异常 IRQ ! 然后从IRQ的服务函数里面分辨出,当前究竟是什么中断,再跳转到相应的中断服务程序。
HandlerIRQ 很明显是一个标号,我们找到了HandlerIRQ HANDLER HandleIRQ
这里是一个宏定义,我们再找到这个宏:
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
    sub    sp,sp,#4     ;decrement sp(to store jump address)
    stmfd    sp!,{r0}     ;PUSH the work register to stack(lr does not push because it return to originaladdress)
    ldr r0,=$HandleLabel ;load the address of HandleXXX to r0
    ldr r0,[r0]     ;load the contents(service routine start address) of HandleXXX
    str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
    ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND
它的功能是完成标号的替换,例如IRQ中断,则HandlerLabel换成了HandlerIRQ。IRQ异常向量就是跳转到这里执行的,
ldr r0,=HandleIRQ ,中断真正的处理函数
    AREA RamData, DATA, READWRITE
    ^ _ISR_STARTADDRESS        ; _ISR_STARTADDRESS=0x33FF_FF00
HandleReset     # 4
HandleUndef     # 4
HandleSWI        # 4
HandlePabort # 4
HandleDabort # 4
HandleReserved # 4
HandleIRQ        # 4
其中:^就是 MAP ,这段程序的意思是,从 _ISR_STARTADDRESS开始,预留一个变量,每个变量一个标号,预留的空间为 4个字节,也就是32BIT,其实这里放的是真正的C写的处理函数的地址。
接下来就是设置处理函数的地址,让PC指针可以正确的跳转。
  ; Setup IRQ handler
    ldr    r0,=HandleIRQ ;This routine is needed
    ldr    r1,=IsrIRQ     ;if there is not 'subs pc,lr,#4' at 0x18, 0x1c
    str    r1,[r0]
说白了就是将 IsrIRQ 的地址填到 HandleIRQ对应的地址里面,前面说了 HandleIRQ 放的是中断处理的
函数的入口地址,我们继续找 IsrIRQ
IsrIRQ
    sub    sp,sp,#4 ;reserved for PC
    stmfd    sp!,{r8-r9}
    ldr    r9,=INTOFFSET
    ldr    r9,[r9]                     ;读入中断偏移码
  ldr    r8,=HandleEINT0     ;二级跳转表的首地址
  add    r8,r8,r9,lsl #2          ;R8=R8+R9X4得到相应的中断入口地址
  ldr    r8,[r8]
    str    r8,[sp,#8]                ;中断入口地址送进SP(第一个代码留出的4字节空间)
  ldmfd    sp!,{r8-r9,pc}

道理很简单, HandleEINT0 就是所有IRQ中断向量表的入口,在这个地址上面,加上一个适当的偏移量INTOFFSET ,那么我们就知道到底是哪个IRQ在申请中断了。
例如看按键的外部中断,是怎么具体设置的,参看/src/keyscan.c 里面的代码:KeyScan_Test();Key_ISR();
在 KeyScan_Test里面,我们发现了有这么一句
    pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR;
Key_ISR就是上面提到的按键中断服务函数.函数的名字,代表的就是函数的地址!!!!
说到底 pISR_EINT0 = (U32)Key_ISR;
完成的操作就是,将 Key_ISR 的地址存放到 HandleEINT0        # 4 这个IRQ向量表里面!!!!
    加上 _irq 关键字之后,编译器就会处理好所有的保存动作了,并不需要多关心。但是这个是 ARM-CC 编译器的关
键字,GCC中并没有这个东西,所以GCC处理中断的时候最好还是自己保存一下。

第三部分 MMU
关于MMU的介绍很多,况且地址变化的图比较难画,本人就不多此一举了,用到的话找本书翻翻就可。

第四部分 实例
本人按照测试程序的keyscan.c编写了按键中断的简单例程,如附录所示。当然,这个工程还需要重新进行DebugRel Settings才行。
第五部分 常见问题
关于找不到中断的问题,本人屡试不爽,参考了本站过来人的经验:
1.这位仁兄的中断分析:http://www.aiothome.net/read.php?tid-5148.html
2.第二位仁兄的回帖我找不见了,小弟在这里替您重申一下:
(1)在2440test文件中找到MMU.c函数,添加到工程,找到(MMU_SetMTT(0x00000000,0x03f00000,(int)__ENTRY,RW_CB)),将(int)__ENTRY改成0x30000000,然后再主程序中调用MMU_Init();
(2)在2440test文件中找到MMU.c函数,添加到工程,然后到2440init.s的ENTRY处(AREA Init, CODE, READONLY),在下面添加
  EXPORT __ENTRY
__ENTRY
(3)这是ofourme对小弟的回帖,http://www.aiothome.net/thread.php?fid-52.html
  其他中断的应用大同小异,这要正确设置和中断相关的寄存器即可。以上文字是小弟学习中断的深刻体会,同时,十分感谢那些在网上无私传贴和给我帮助的人,但愿能给同路人带来帮助。




描述:附录:按键中断程序
附件: keyscan.rar (93 K) 下载次数:272
勺饮不器盛沧海,拳石频移磊泰山
级别: 新手上路
UID: 5561
精华: 0
发帖: 8
金钱: 70 两
威望: 57 点
贡献值: 0 点
综合积分: 16 分
注册时间: 2009-04-28
最后登录: 2013-07-02
1楼  发表于: 2010-05-23 17:41
  
赞一个,受益匪浅
级别: 骑士
UID: 9169
精华: 0
发帖: 249
金钱: 1330 两
威望: 299 点
贡献值: 0 点
综合积分: 498 分
注册时间: 2009-09-19
最后登录: 2017-08-14
2楼  发表于: 2010-05-25 08:40
呵呵,中断理解够深刻的
级别: 新手上路
UID: 22848
精华: 0
发帖: 42
金钱: 210 两
威望: 42 点
贡献值: 0 点
综合积分: 84 分
注册时间: 2010-06-08
最后登录: 2010-12-27
3楼  发表于: 2010-06-12 07:02
写的太好了
级别: 新手上路
UID: 24236
精华: 0
发帖: 14
金钱: 70 两
威望: 14 点
贡献值: 0 点
综合积分: 28 分
注册时间: 2010-07-04
最后登录: 2011-06-25
4楼  发表于: 2010-07-04 22:00
受用了!
级别: 新手上路
UID: 25273
精华: 0
发帖: 11
金钱: 55 两
威望: 11 点
贡献值: 0 点
综合积分: 22 分
注册时间: 2010-07-21
最后登录: 2012-03-21
5楼  发表于: 2010-07-22 14:41

 回 4楼(涛哥依然骄傲) 的帖子

说得太好了,谢谢这位大哥!
级别: 新手上路
UID: 33632
精华: 0
发帖: 19
金钱: 95 两
威望: 19 点
贡献值: 0 点
综合积分: 38 分
注册时间: 2010-12-03
最后登录: 2011-06-06
6楼  发表于: 2011-02-08 22:29
赞,太好了
级别: 新手上路
UID: 51966
精华: 0
发帖: 12
金钱: 60 两
威望: 12 点
贡献值: 0 点
综合积分: 24 分
注册时间: 2011-07-10
最后登录: 2011-10-31
7楼  发表于: 2011-07-25 16:19
楼主,弱弱的问一下 ,为什么添加了mmu的初始化函数,程序运行时会死在这个函数里,基于mdk环境的