一、介绍
1、三星S5PV210中断体系介绍
异常向量表(矢量中断控制器)
异常向量表是CPU中某些特定地址的特定定义,当中断发生的时候,中断要通知CPU处理中断,在CPU设计时,定义了CPU中一些特定地址作为特定异常的入口地址,异常向量表的实现,是基于SoC内部的矢量中断控制器。
关于矢量中断控制器的描述,这里摘录了网友“亦大乐谍“的博客:
S5PV210是三星推出的一款基于Cortex-A8的Soc,其内部集成的中断控制器由4个ARM PrimeCell
PL192矢量中断控制器级连(daisy-chain)而成,每个PL192 VIC(Vectored Interrupt
Controller)支持32个中断源,所以最多支持128个。S5PV210使用了其中的93个。所谓“矢量”是指当中断发生时,软件可以直接从VIC得到提前设好的中断服务程序ISR(Interrupt
Service Routine)。
CPU在对VIC进行初始化的过程中,将各个中断源的ISR写入到VIC中存储起来,在中断发生以后,VIC会自动挑选出当前优先级最高的中断源,并将其ISR推送到VICADDRESS寄存器里,CPU可以直接拿到ISR(一般就是一个函数的起始地址),将PC跳转到这个地址取执行就可以了,速度大大加快
以上讲的是CPU硬件设计时对异常向量表的支持,接下来就需要软件支持。硬件已近决定了的发送什么异常CPU自动跳转PC到那个地址执行,软件需要做的就是把处理这个异常的代码的收地孩子填入这个异常向量的地址。
二、异常处理的两个阶段
(1)第一个阶段之所以能够进行,主要依赖于CPU设计时提供的异常向量表机制。第一个阶段的主要任务是从异常发生到响应异常并且保存/恢复现场,跳转到真正的异常处理程序处。
(2)第二个阶段的目的是识别多个中断源中究竟是哪一个发生了中断,然后调用相应的中断处理程序来处理这个中断。
三、编程思路
整个中断的工作分为2部分:
第一部分是我们为中断响应的预备工作
1、初始化中断控制器
2、绑定写好的isr到中断控制器
3、相应的中断的所有条件的使能
第二部分是当硬件产生中断后如何自动执行isr
1、第一步,经过异常向量表跳转入IRQ/FIQ入口
2、第二步,做中断现场保护(在start.s),然后跳入isr_handler
3、第三步,在isr_handler中先去搞清楚是哪个VIC中断了,然后直接去VIC的ADDR寄存器中去isr来执行即可
4、第四步,isr执行完,中断现场恢复,直接返回继续做常规任务。
四、使用外部中断时需要配置的几个重要的寄存器
1、VIC0INTENABLE和VIC0INTENCLEAR
(1) VICnINTENABLE ------interrupt enable
VINTENCLEAR ------interrupt enable clear
(2)INTENABLE寄存器负责相应的中断的使能;INTENCLEAR寄存器负责相应的中断的禁止。
(3)当我们想使能(意思就是启用这个中断,意思就是当硬件产生中断时CPU能接收的到)某个中断时,只要在这个中断编号对应的VICnINTENABLE的相应bit位写1即可(注意这个位写1其他位写0对其他位没有影响);如果我们想禁止某个中断源时,只要向VICnINTENCLEAR中相应的位写1即可。注意:这里的设计一共有2种:有些CPU是中断使能和禁止是一个寄存器位,写1就使能写0就禁止(或者反过来写1就禁止写0就使能),这样的中断使能设计就要非常小心,要使用我们之前说过的读改写三部曲来操作;
另一种就是使能和禁止分开为2个寄存器,要使能就写使能寄存器,要禁止就写禁止寄存器。这样的好处是我们使能/禁止操作时不需要读改写,直接写即可。
2、VICnINTSELECT(Interrupt Select Register,中断模式选择寄存器,0选择IRQ ,1FIQ)
(1)设置各个中断的模式为irq还是fiq。一般都设置成irq
(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。
4、VICnVECTPRIORITY0~VICnVECTPRIORITY31
(1)中断优先级设置寄存器,设置多个中断同时发生时先处理谁后处理谁的问题。一般来说高优先级的中断可以打断低优先级的中断,从而嵌套处理中断。当然了有些硬件/软件可以设置不支持中断嵌套。
5、VICnVECTADDR0~VICnVECTADDR31、VICnADDR
(1)VICnVECTADDR0~31这32个寄存器分别用来存放真正的各个中断对应的isr的函数地址。相当于每一个中断源都有一个VECTADDR寄存器,程序员在设置中断的时候,把这个中断的isr地址直接放入这个中断对应的VECTADDR寄存器即可。
(2)VICnADDR这个寄存器是只需要读的,它里面的内容是由硬件自动设置的。当发生了相应中断时,硬件会自动识别中断编号,并且会自动找到这个中断的VECTADDR寄存器,然 后将其读出复制到VICnADDR中,供我们使用。这样的设计避免了软件查找中断源和isr,节省了时间,提高了210的中断响应速度。
五、代码分析
(一)代码分析—中断模式下的现场保护和恢复
IRQ_handle:
// 设置IRQ模式下的栈 0xD003_7F80
ldr sp, =IRQ_STACK
// 保存LR
// 因为ARM有流水线,所以PC的值会比真正执行的代码+8,
sub lr, lr, #4
// 保存r0-r12和lr到irq模式下的栈上面
stmfd sp!, {r0-r12, lr}
// 在此调用真正的isr来处理中断
bl irq_handler
// 处理完成开始恢复现场,其实就是做中断返回,关键是将r0-r12,pc,cpsr一起恢复
ldmfd sp!, {r0-r12, pc}^
(二)代码分析—从main函数中中断的相关函数入手拆解
int main(void)
{
int n = 0;
uart_init();
// 如果程序中要使用中断,就要调用中断初始化来初步初始化中断控制器
system_init_exception();
eint_init();
// 绑定isr到中断控制器硬件
intc_setvectaddr(KEY_DOWN, key_isr_eint2);
intc_setvectaddr(KEY_BACK, key_isr_eint16171819);
intc_enable(KEY_DOWN);
intc_enable(KEY_BACK);
while (1)
{
printf("%d ", n++);
delay(10000);
}
return 0;
}
①代码:key_init_interrupt();
void key_init_interrupt(void)
{
//1、外部中断对应的GPIO模式设置
rGPH0CON |= 0xFF<<8; //GPH0_2和GPH0_3设置为外部中断模式
rGPH2CON |= 0XFFFF<<0; //GPH2_0,1,2,3共4个引脚设为外部中断模式
//2、中断触发模式设置,这里设为下降沿触发
EXT_INT_0_CON &= ~(0XFF<<8); //bit8~bit15全部清零
EXT_INT_0_CON |= ((2<<8)|(2<<12));//EXT_INT2和EXT_INT3设置位下降沿触发。
//3、 中断允许,清挂
rEXT_INT_0_MASK &= ~(3<<2);
rEXT_INT_2_MASK &= ~(3<<0x0f<<0);
//4、清挂起,清除是写1
rEXT_INT_0_PEND |= (3<<2);
rEXT_INT_2_PEND |= (0x0F<<0);
}
②代码:System_init_exception();
// 主要功能:绑定第一阶段异常向量表;禁止所有中断;选择所有中断类型为IRQ;
// 清除VICnADDR为0
void system_init_exception(void)
{ // 第一阶段处理,
绑定异常向量表
r_exception_reset = (unsigned int)reset_exception;
r_exception_undef = (unsigned int)undef_exception;
r_exception_sotf_int = (unsigned int)sotf_int_exception;
r_exception_prefetch = (unsigned int)prefetch_exception;
r_exception_data = (unsigned int)data_exception;
r_exception_irq = (unsigned int)IRQ_handle;
r_exception_fiq = (unsigned int)IRQ_handle;
// 初始化中断控制器的基本寄存器
intc_init();
}
代码:void intc_init(void)
// 禁止所有中断
// 为什么在中断初始化之初要禁止所有中断?
// 因为中断一旦打开,因为外部或者硬件自己的原因产生中断后一定就会寻找isr
// 而我们可能认为自己用不到这个中断就没有提供isr,这时它自动拿到的就是乱码
// 则程序很可能跑飞,所以不用的中断一定要关掉。
// 一般的做法是先全部关掉,然后再逐一打开自己感兴趣的中断。一旦打开就必须
// 给这个中断提供相应的isr并绑定好。
void intc_init(void)
{
VIC0INTENCLEAR = 0xffffffff;
VIC1INTENCLEAR = 0xffffffff;
VIC2INTENCLEAR = 0xffffffff;
VIC3INTENCLEAR = 0xffffffff;
// 选择中断类型为IRQ
VIC0INTSELECT = 0x0;
VIC1INTSELECT = 0x0;
VIC2INTSELECT = 0x0;
VIC3INTSELECT = 0x0;
// 清VICxADDR
intc_clearvectaddr();
}
-代码:void intc_clearvectaddr(void)
// 清除需要处理的中断的中断处理函数的地址
void intc_clearvectaddr(void)
{
// VICxADDR:当前正在处理的中断的中断处理函数的地址
VIC0ADDR = 0;
VIC1ADDR = 0;
VIC2ADDR = 0;
VIC3ADDR = 0;
}
③ 代码:intc_setve
ctaddr(KEY_INT2, isr_eint2);
// 绑定isr到中断控制器硬件
intc_setvectaddr(KEY_INT2, isr_eint2);
intc_setvectaddr(KEY_INT3, isr_eint3);
intc_setvectaddr(KEY_INT16_19, isr_eint16171819);
// 绑定我们写的isr到VICnVECTADDR寄存器
// 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理
// 等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可。
// 参数:intnum是int.h定义的物理中断号,handler是函数指针,就是我们写的isr
// VIC0VECTADDR定义为VIC0VECTADDR0寄存器的地址,就相当于是VIC0VECTADDR0~31这个
// 数组(这个数组就是一个函数指针数组)的首地址,然后具体计算每一个中断的时候
// 只需要首地址+偏移量即可。
void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
{
//VIC0
if(intnum<32)
{
*( (volatile unsigned long )(VIC0VECTADDR + 4(intnum-0)) ) = (unsigned)handler;
}
//VIC1
else if(intnum<64)
{
*( (volatile unsigned long )(VIC1VECTADDR + 4(intnum-32)) ) = (unsigned)handler;
}
//VIC2
else if(intnum<96)
{
*( (volatile unsigned long )(VIC2VECTADDR + 4(intnum-64)) ) = (unsigned)handler;
}
//VIC3
else
{
*( (volatile unsigned long )(VIC3VECTADDR + 4(intnum-96)) ) = (unsigned)handler;
}
return;
}
③代码:intc_ena
ble(KEY_DOWN);
// 使能中断
// 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
#define NUM_HSMMC3 (96+2)
#define NUM_CEC (96+3)
#define NUM_TSI (96+4)
#define NUM_MDNIE0 (96+5)
#define NUM_MDNIE1 (96+6)
#define NUM_MDNIE2 (96+7)
#define NUM_MDNIE3 (96+8)
#define NUM_ADC1 (96+9)
#define NUM_PENDN1 (96+10)
#define NUM_ALL (200)
void intc_enable(unsigned long intnum)
{
unsigned long temp;
// 确定intnum在哪个寄存器的哪一位
// <32就是0~31,必然在VIC0
if(intnum<32)
{
temp = VIC0INTENABLE;
temp |= (1< VIC0INTENABLE = temp; } else if(intnum<64) { temp = VIC1INTENABLE; temp |= (1<<(intnum-32)); VIC1INTENABLE = temp; } else if(intnum<96) { temp = VIC2INTENABLE; temp |= (1<<(intnum-64)); VIC2INTENABLE = temp; } else if(intnum temp = VIC3INTENABLE; temp |= (1<<(intnum-96)); VIC3INTENABLE = temp; } // NUM_ALL : enable all interrupt else { VIC0INTENABLE = 0xFFFFFFFF; VIC1INTENABLE = 0xFFFFFFFF; VIC2INTENABLE = 0xFFFFFFFF; VIC3INTENABLE = 0xFFFFFFFF; } }
上一篇:S5PV210的启动过程详解
下一篇:ARM汇编指令集的特点
推荐阅读最新更新时间:2024-11-12 22:54
设计资源 培训 开发板 精华推荐
- #第六届立创电赛# 堆功放
- 迷你usb-hub
- LTC3106IFE 简单宽输入电压降压-升压转换器的典型应用电路
- 具有 200ms 瞬态穿越能力的 LTC3126HFE 3.3V 电源的典型应用电路
- MC33163 外部 NPN 升压开关稳压器的典型应用
- LTC2668HUJ-12、16 通道、12 位 ±10V VOUT SoftSpan DAC 的典型应用电路
- MIKROE-3522,用于 AS5311 高分辨率磁性线性编码器的 Magneto 4 Click 板
- TCR5SB50U、200mA、5V 输出电压 CMOS 低压降稳压器的典型应用
- 四轴飞行器主控板设计原理图与PCB
- AD8602ARMZ-REEL 高端运算放大器电流监视器的典型应用
- 新春寻宝记,泰克送福利!快来开启你的寻宝之旅~
- 7月21日有奖直播|ADI开关/多路复用器系列产品的典型应用
- 荣耀三月 我是原创我最牛!(恩智浦全程赞助)
- 助力EEWorld 19成长计划,赢取精美好礼!
- 有奖评测:50套来自恩智浦全新快速物联网原型设计套件(价值350元)
- 骏龙科技&ADI有奖直播:适用于视频、转换器、通信的千兆数字隔离器
- 【EEWORLD第三十七届】2012年04月社区明星人物揭晓!
- 春暖花开,我为TI C2000 LaunchPad“画”外围!
- 有奖直播|基于TI最新低功耗60GHz毫米波雷达传感器的工业应用
- 将想法变成现实,Maxim MAX32630FTHR创意设计大赛,重磅开启!