一、什么是中断?

1、中断的发明是用来解决宏观上的并行需要的。宏观就是从整体上来看,并行就是多件事情都完成了。

2、微观上的并行,就是指的真正的并行,就是精确到每一秒甚至每一刻,多个事情都是在同时进行的。宏观上面的并行并不等于围观的并行,有时候宏观上是并行的,微观上

是串行的。

3、为什么需要中断?因为单核CPU实际无法并行的,但是通过中断机制,可以实现假并行(宏观上的并行,微观上实际还是串行的)。

二、异常和中断的区别和联系

1、针对SoC来说,发生复位、软中断、中断、快速中断、取指令异常、数据异常等,我们都统一叫异常。所以说:中断其实是异常的一种。

2、异常的定义就是突发事件,打断了CPU的正常常规业务,CPU不得不跳转到异常向量表中去执行异常处理程序;中断是异常的一种,一般特指SoC内的内部外设产生的打断SoC

常规业务,或者外部中断(SoC的GPIO引脚传回来的中断)。

三、SoC对中断的实现机制:异常向量表

(1)异常向量表是CPU中某些特定地址的特定定义。当中断发生的时候,中断要想办法通知CPU去处理中断,怎么做到?这就要靠异常向量表。

(2)在CPU设计时,就事先定义了CPU中一些特定地址作为特定异常的入口地址(譬如定义0x00000000地址为复位异常向量地址,则发生复位异常时CPU会自动跳转到0x00000000地

址去执行指令。又譬如外部中断对应的异常向量地址为0x30000008,则发生外部中断后,CPU会硬件自动跳转到0x30000008地址去执行指令。)

(3)以上讲的是CPU硬件设计时对异常向量表的支持,下来就需要软件支持了。硬件已经决定了发生什么异常CPU自动跳转PC到哪个地址去执行,软件需要做的就是把处理这个异

常的代码的首地址填入这个异常向量地址。

(4)异常向量表中各个向量的相对位置是固定的,但是他们的起始地址是不固定的,各种SoC可以不一样,而且复杂ARM中还可以让用户来软件设置这个异常向量表的基地址。

(5)扩展到所有架构的CPU中:所有架构(譬如51单片机、PIC单片机)的CPU实现中断都是通过异常向量表实现的,这个机制是不变的;但是不同CPU异常向量表的构造和位置是

不同的。

四、s5pv210异常向量表的编程处理

1、像内存一样去访问异常向量表

(1)S5PV210的异常向量表可以改变(在CP15协处理器中),以适应操作系统的需求。但是目前系统刚启动时,此时DRAM尚未初始化,程序都在SRAM中运行。210在iRAM中设置了

异常向量表,供暂时性使用。

(2)查210的iROM application note文档中iRAM的地址分配,可知,iRAM中的异常向量表起始地址为0xD0037400。知道了异常向量表的起始地址后,各个异常对应的入口就很好

知道了。

总结:当我们将异常处理程序的首地址和异常向量表绑定起来后,异常处理初步阶段就完成了。到目前可以保证相应异常发生后,硬件自动跳转到对应异常向量表入口去执行

时,可以执行到我们事先绑定的函数。

2、为什么中断处理要先在汇编中进行

(1)中断处理要注意保护现场(中断从SVC模式来,则保存SVC模式下的必要寄存器的值)和恢复现场(中断处理完成后,准备返回SVC模式前,要将保存的SVC模式下的必要寄存

器的值恢复回去,不然到了SVC模式后寄存器的值乱了,SVC模式下原来正在进行的常规任务就被你搞坏了)。

(2)保存现场包括:第一:设置IRQ栈;第二,保存LR;第三,保存R0~R12

(3)为什么要保存LR寄存器?要考虑中断返回的问题。中断ISR执行完后如何返回SVC模式下去接着执行原来的代码。中断返回其实取决于我们进入中断时如何保存现场。中断返

回时关键的2个寄存器就是PC和CPSR。所以我们在进入IRQ模式时,应该将SVC模式下的下一句指令的地址(中断返回地址)和CPSR保存起来,将来恢复时才可以将中断返回地址

给PC,将保存的CPSR给CPSR。

(4)中断返回地址就保存在LR中,而CPSR(自动)保存在(IRQ模式下的)SPSR中。

3、汇编保存现场和恢复现场

(1)保护现场关键是保存:中断处理程序的返回地址,r0-r12(cpsr是自动保存的)

(2)恢复现场主要是恢复:r0-r12,pc,cpsr

五、s5pv210的向量中断控制寄存器

1、异常处理的2个阶段

可以将异常处理分为2个阶段来理解。第一个阶段是异常向量表跳转;第二个阶段就是进入了真正的异常处理程序irq_handler之后的部分。

(1)第一个阶段之所以能够进行,主要依赖于CPU设计时提供的异常向量表机制。第一个阶段的主要任务是从异常发生到响应异常并且保存/恢复现场、跳转到真正的异常处理程序处。

(2)第二个阶段的目的是识别多个中断源中究竟哪一个发生了中断,然后调用相应的中断处理程序来处理这个中断。

2、S3C2440的第二阶段处理过程

(1)第一个问题,怎么找到具体是哪个中断:S3C2440的中断控制器中有一个寄存器(32位的),寄存器的每一个位对应一个中断源(为了解决支持更多中断源,2440又设计了

一个子中断机制。在一级中断寄存器中有一些中断是共用的一个bit位,譬如AC97和WDT。对于共用中断,用子中断来区分究竟是哪一个发生了中断)

(2)第二个问题,怎么找到对应的isr的问题:首先给每个中断做了个编号,进入isr_handler之后先通过查阅中断源寄存器和子中断寄存器(中哪一位为1)确定中断的编号,然后

用这个编号去isr数组(isr数组是中断初始化时事先设定好的,就是把各个中断的isr的函数名组成一个数组,用中断对应的编号作为索引来查询这个数组)中查阅得到isr地址。

评价:2440的中断处理设计不是特别优秀:第一个过程中使用子中断搞成2级的很麻烦;第二个过程中计算中断编号是个麻烦事,很耗费时间。而中断处理的时间是很宝贵的(

系统有一个性能指标,叫实时性。实时性就是中断发生到响应的时间,这个时间越短越好。)

3、S5PV210的第二阶段处理过程

(1)第一个问题,怎么找到具体是哪个中断:S5PV210中因为支持的中断源很多,所以直接设计了4个中断寄存器,每个32位,每位对应一个中断源。(理论上210最多支持128个

中断,实际支持不足128个,有些位是空的);210没有子中断寄存器,每个中断源都是并列的。当中断发生时,在irq_handler中依次去查询4个中断源寄存器,看哪一个的哪

一位被置1,则这个位对应的寄存器就发生了中断,即找到了中断编号。

(2)第二个问题,怎么找到对应的isr的问题:210中支持的中断源多了很多,如果还使用2440的那一套来寻找isr地址就太慢了,太影响实时性了。于是210开拓了一种全新的寻

找isr的机制。210提供了很多寄存器来解决每个中断源对应isr的寻找问题,具体寻找过程和建立过程见下节,实现的效果是当发生相应中断时,硬件会自动的将相应isr推入一

定的寄存器中,我们软件只要去这个寄存器中执行函数就行了。

4、总结:第一阶段都相同,第二阶段各不同

(1)第一阶段(异常向量表阶段)2440和210几乎是完全相同的。实际上几乎所有的CPU在第一阶段都是相同的。

(2)第二阶段就彼此不同了。各个SoC根据自己对实时性的要求,和支持的中断源的多少,各自发明了各自处理中断,找到中断编号,进一步找到对应isr地址的方式。

五、S5PV210中断处理的主要寄存器

1、VICnINTENABLE和VICnINTENCLEAR
(1)VICnINTENABLE 对应interrupt enable,INTENCLEAR对应interrupt enable clear
(2)INTENABLE寄存器负责相应的中断的使能,INTENCLEAR寄存器负责相应的中断的禁止。
(3)当我们想使能(意思就是启用这个中断,意思就是当硬件产生中断时CPU能接收的到)某个中断时,只要在这个中断编号对应的VICnINTENABLE的相应bit位写1即可

(注意这个位写1其他位写0对其他位没有影响);如果我们想禁止某个中断源时,只要向VICnINTENCLEAR中相应的位写1即可。注意:这里的设计一共有2种:有些CPU

是中断使能和禁止是一个寄存器位,写1就使能写0就进制(或者反过来写1就进制写0就使能),这样的中断使能设计就要非常小心,要使用我们之前说过的读改写三部曲来操作;

另一种就是使能和禁止分开为2个寄存器,要使能就写使能寄存器,要禁止就写禁止寄存器。这样的好处是我们使能/禁止操作时不需要读改写,直接写即可。

2、VICnINTSELECT
(1)设置各个中断的模式为irq还是fiq。一般都设置成irq

(2)IRQ和FIQ究竟有何区别。210中支持2种中断,irq和fiq。irq是普通中断,fiq是快速中断。快速中断提供一种更快响应处理的中断通道,用于对实时性要求很高的中断源。

fiq在CPU设计时预先提供了一些机制保证fiq可以被快速处理,从而保证实时性。fiq的限制就是只能有一个中断源被设置为fiq,其他都是irq。
(3)CPU如何保证fiq比irq快?有2个原因:第一,fiq模式有专用的r8~r12,因此在fiq的isr中可以直接使用r8-r12而不用保存,这就能节省时间;第二,异常向量表中fiq是最

后一个异常向量入口。因此fiq的isr不需要跳转,可以直接写在原地,这样就比其他异常少跳转一次,省了些时间。

3、VICnIRQSTATUS和VICnFIQSTATUS
(1)中断状态寄存器,是只读的。当发生了中断时,硬件会自动将该寄存器的对应位置为1,表示中断发生了。软件在处理中断第二阶段的第一阶段,就是靠查询这个寄存器来得到中断编号的。

4、VICnVECTPRIORITY0~VICnVECTPRIORITY31
(1)中断优先级设置寄存器,设置多个中断同时发生时先处理谁后处理谁的问题。一般来说高优先级的中断可以打断低优先级的中断,从而嵌套处理中断。当然了有些硬件/软件可以

设置不支持中断嵌套。

5、VICnVECTADDR0~VICnVECTADDR31、VICnADDR

(1)这三个寄存器和210中断处理第二阶段的第二阶段有关。
(2)VICnVECTADDR0到31这32个寄存器分别用来存放真正的各个中断对应的isr的函数地址。相当于每一个中断源都有一个VECTADDR寄存器,程序员在设置中断的时候,把这个

中断的isr地址直接放入这个中断对应的VECTADDR寄存器即可。
(3)VICnADDR这个寄存器是只需要读的,它里面的内容是由硬件自动设置的。当发生了相应中断时,硬件会自动识别中断编号,并且会自动找到这个中断的VECTADDR寄存器,然

后将其读出复制到VICnADDR中,供我们使用。这样的设计避免了软件查找中断源和isr,节省了时间,提高了210的中断响应速度。

六、s5pv210中断处理编程实践

本代码并没有提供完整的源文件

/*************************************************************************************/

汇编代码: start.S文件

 #define WTCON        0xE2700000
#define SVC_STACK 0xd0037d80
#define IRQ_STACK 0xd0037f80
#define SOFT_KEY 0xE010E81C .global _start
.global IRQ_handle
.extern main
.extern irq_handler _start:
/*0 : 软开关设置*/
ldr r0, =SOFT_KEY
ldr r1, [r0]
ldr r2, =0x301
orr r1, r1, r2
str r1, [r0] /*1 : 关看门狗*/
ldr r0, =WTCON
ldr r1, [r0]
bic r1, r1, #0x20
str r1, [r0] /*2 : 设置栈指针*/
ldr sp, =SVC_STACK /*3 : 开启icache*/
mrc p15,,r0,c1,c0,
orr r0, r0, #0x1000
mcr p15,,r0,c1,c0, /*4 : 调用C语言中main函数*/
bl main main_loop:
b main_loop IRQ_handle:
ldr sp, =IRQ_STACK //设置中断模式下的栈地址
sub lr, lr, # //因为ARM的流水线机制的原因:中断返回来之后应该是要执行当前指令的下一条指令
//而此时的PC值等于当前正在执行的指令在内存中地址 + 8
//所以此时的PC-4就是我们当前正在执行的指令的下一条执行的内存地址 stmfd sp!, {r0-r12, lr} //将寄存器中的数据写入栈中
bl irq_handler //调用处理函数
ldmfd sp!, {r0-r12, pc}^ //恢复现场 #endif

interrupt.c文件

 #define  INTERRUPT   1
#if INTERRUPT #include "interrupt.h"
#include "include.h" void interrupt_init(void)
{
//将中断函数与异常向量表绑定
r_EXCEPTION_IRQ = (unsigned int)IRQ_handle; //注意这里的类型转换 //首先禁止所有的中断
VIC0INTENCLEAR = 0xffffffff; //向相应位写入1
VIC1INTENCLEAR = 0xffffffff;
VIC2INTENCLEAR = 0xffffffff;
VIC3INTENCLEAR = 0xffffffff; //选择中断类型
VIC0INTSELECT = 0x00; //写入0表示 IRQ中断类型
VIC1INTSELECT = 0x00;
VIC2INTSELECT = 0x00;
VIC3INTSELECT = 0x00; //清除VICxADDR 保险的做法 不一定非要这么做
VIC0ADDR = 0x0;
VIC1ADDR = 0x0;
VIC2ADDR = 0x0;
VIC3ADDR = 0x0; //将GPIO设为外部中断模式
GPH0CON |= (0xff << ); //EXIT[2] EXIT[3]
GPH2CON |= (0xffff << ); //EXIT[16] - EXIT[19] //选择中断触发模式 本实验采用下降沿触发 010
EXT_INT_0_CON |= (( << )|( << ));
EXT_INT_0_CON &= ~(( << )|( << )|( << )|( << ));
EXT_INT_2_CON |= (( << )|( << )|( << )|( << ));
EXT_INT_2_CON &= ~(( << )|( << )|( << )|( << )|( << )|( << )|( << )|( << )); //绑定中断处理函数到VIC2VECTADDR0寄存器
VIC0VECTADDR2 = (unsigned int)exit_2;
VIC0VECTADDR3 = (unsigned int)exit_3;
VIC0VECTADDR16 = (unsigned int)exit_16_19; //清除中断挂起 PEND寄存器 读这个寄存器0表示没有发生 1 表示发生中断
//需要我们 写如 1 清除中断挂起
EXT_INT_0_PEND |= (( << )|( << ));
EXT_INT_2_PEND |= (( << )|( << )|( << )|( << )); //开启相应中断使能
VIC0INTENABLE |= (( << )|( << )|( << )|( << )|( << )|( << )); //中断控制器中的中断使能控制开关
EXT_INT_0_MASK &= ~(( << )|( << )); //GPIO相关模块的中断使能控制的开关 写0开启
EXT_INT_2_MASK &= ~(( << )|( << )|( << )|( << ));
} void irq_handler(void)
{
//VICxADDR寄存器虽然是一个函数地址 但是在寄存器中是以整形数据存放
unsigned int vicaddr[] = {VIC0ADDR, VIC1ADDR, VIC2ADDR, VIC3ADDR};
unsigned int vicstat[] = {VIC0IRQSTATUS, VIC1IRQSTATUS, VIC2IRQSTATUS, VIC3IRQSTATUS};
int i = ;
void (*isr)(void) = NULL; //定义一个函数指针 for(i = ;i < ;i++)
{
if(vicstat[i] != ) //注意这个地方不是判断 ADDR寄存器中是否有数据 而是判断irq标志位
{
isr = (void (*)(void))vicaddr[i]; //将寄存器中的数据转化为函数地址(函数指针)赋给函数指针变量
break;
}
} (*isr)(); //执行最终的动作函数
// isr();
} #endif

整个中断的流程梳理:

整个中断的工作分为2部分:

第一部分是我们为中断响应而做的预备工作:

1. 初始化中断控制器

2. 绑定写好的isr到中断控制器

3. 相应中断的所有条件使能

第二部分是当硬件产生中断后如何自动执行isr:

1. 第一步,经过异常向量表跳转入IRQ/FIQ的入口

2. 第二步,做中断现场保护(在start.S中),然后跳入isr_handler

3. 第三步,在isr_handler中先去搞清楚是哪个VIC中断了,然后直接去这个VIC的ADDR寄存器中取isr来执行即可。

4. 第四步,isr执行完,中断现场恢复,直接返回继续做常规任务。

/*******************************************************************************************/

参考:  《朱老师嵌入式Linux开发\1.ARM裸机全集\1.8.ARM裸机第八部分-按键和CPU的中断系统》

最新文章

  1. Ubuntu中的快捷键
  2. 安装EPEL源
  3. Google Android 6.0 权限完全解析
  4. Ajax Post 类实例
  5. Java Day 05
  6. 用JS查看修改CSS样式(cssText,attribute(&#39;style&#39;),currentStyle,getComputedStyle)
  7. 深入浅出:重温JAVA中接口与抽象的区别
  8. 远光软件ASP.NET笔试题小汇总
  9. SQLite : 解决“找不到请求的 .Net Framework 数据提供程序。可能没有安装”的问题
  10. LinkedList集合
  11. js的解析顺序 作用域 严格模式
  12. python爬虫数据解析之正则表达式
  13. Elasticsearch System Call Filters Failed to Install
  14. X86架构
  15. MySQL 排名、分组后组内排名、取各组的前几名 及排名后更新插入数据表中
  16. 什么是 Spring?
  17. 百练6247-过滤多余的空格-2015正式B题
  18. Exp5
  19. java 处理json格式数据中的转义斜杠
  20. P4047 [JSOI2010]部落划分

热门文章

  1. vue组件一
  2. mount不是很熟悉 转载文章了解下 转自http://forum.ubuntu.org.cn/viewtopic.php?f=120&amp;t=257333
  3. Python 之 装饰器的写法
  4. (Array)169. Majority Element
  5. disposition
  6. 【node】使用gulp来维护网站项目
  7. C#运算符大全_各种运算符号的概述及作用
  8. 可变参数宏__VA_ARGS__和...
  9. equals标准写法
  10. 从不同方面寻找bug