1.轮询与中断
1.CPU与硬件的交互方式
- 轮询
CPU执行程序时不断地询问硬件是否需要其服务,若需要则给予其服务,若不需要一段时间后再次询问,周而复始 - 中断
CPU执行程序时若硬件需要其服务,对应的硬件给CPU发送中断信号,CPU接收到中断信号后将当前的程序暂停下来,转而去执行中断服务程序,执行完成后再返回到被打断的点继续执行 - DMA
硬件产生数据后,硬件控制器可将产生的数据直接写入到存储器中,整个过程无需CPU的参与
2.轮询方式实现按键实验
#include "exynos_4412.h"
int main()
{
/*将GPX1_1设置成输入功能*/
GPX1.CON = GPX1.CON & (~(0xf << 4));
while(1){
if(!(GPX1.DAT & (1<<1))){
printf("KEY2 PRESSED\n");
while(!(GPX1.DAT & (1<<1)));
}else {
}
}
return 0;
}
3.中断方式实现按键实验
GPIO中断
3.1 中断相关寄存器
(1) GPX1CON
/*将GPX1_1设置为中断功能*/
GPX1.DAT = GPX1.DAT | (0xF << 4);
(2) EXT_INT41CON
在这里,我们选择下降沿触发
/*设置GPX1_1中断触发方式*/
EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
(3) EXT_INT41_MASK
/*使能GPX1_1的中断功能*/
EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1));
合并代码:
#include "exynos_4412.h"
int main()
{
/*将GPX1_1设置为中断功能*/
GPX1.DAT = GPX1.DAT | (0xF << 4);
/*设置GPX1_1中断触发方式*/
EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
/*使能GPX1_1的中断功能*/
EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1));
return 0;
}
设置好这些寄存器,我们的GPIO就可以向外发出中断请求了,当然,信号并不是直接发给CPU,而是要经过一个中断控制器,再发给CPU。
2.中断控制器
2.1 中断控制器
在Exynos4412芯片中,有4个CPU和160个能发出中断信号的外设,如果把外部信号产生的中断信号直接发给CPU,会出现以下问题:
1>多个外设同时发送中断信号给CPU
2>CPU正在处理其他中断,这时产生一个新的中断给CPU,如果是同等级的中断请求,CPU是收不到的
3>多核处理器,外设硬件将中断信号发给那个CPU核
4>外部硬件发送FRQ还是IRQ信号
5>CPU需要区分信号是谁发送的
…
所以,中断控制器就是解决这些问题的
- 中断控制器
中断控制器作用
多个中断同时产生时可对这些中断挂起排队,然后按照优先级依次发送给CPU处理
可以为每一个中断分配一个优先级
一个中断正在处理时若又产生其它中断,可将新的中断挂起,待CPU空闲时再发送
可以为每一个中断选择一个CPU处理
可以为每一个中断选择一个中断类型(FIQ或IRQ)
CPU接收到中断信号后并不能区分是哪个外设产生的,此时CPU可查询中断控制器
来获取当前的中断信号是由哪个硬件产生的,然后再进行对应的处理
可以打开或禁止每一个中断
... ...
2.2 Exynos_4412的中断控制器(部分)
通用中断控制器(GIC)是一种集中式资源,它支持和管理系统中的中断。
GIC提供:寄存器管理中断源、中断行为和中断路由到一个或多个处理器
支持ARM架构安全扩展
启用、禁用和从硬件(外围)中断源生成处理器中断生成软件中断
中断屏蔽和优先级
(1)ICDDCR
(2) ICDISER_CPU
(3) ICDIPTR_CPU
(4)ICCICR_CPUn: 全局启用CPU接口对已连接处理器的中断信令。
3.中断编程
异常:处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生,这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件,异常事件处理完成之后再返回到被异常打断的点继续执行程序
异常处理机制:不同的处理器对异常的处理的流程大体相似,但是不同的处理器在具体实现的机制上有所不同;比如处理器遇到哪些事件认为是异常事件遇到异常事件之后处理器有哪些动作、处理器如何跳转到异常处理程序如何处理异常、处理完异常之后又如何返回到被打断的程序继续执行等我们将这些细节的实现称为处理器的异常处理机制
ARM异常源
ARM异常模式
ARM异常响应
异常向量表:
异常向量表的本质是内存中的一段代码
表中为每个异常源分配了四个字节的存储空间
遇到异常后处理器自动将PC修改为对应的地址
因为异常向量表空间有限一般我们不会再这里
写异常处理程序,而是在对应的位置写一条跳转指令使其跳转到指定的异常处理程序的入口
注:ARM的异常向量表的基地址默认在0x00地址 ,但可以通过配置协处理器来修改其地址
异常返回
ARM异常返回的动作
1.将SPSR_的值复制给CPSR使处理器恢复之前的状态
2.将LR_的值复制给PC,使程序跳转回被打断的地址继续执行
异常优先级
LR寄存器
R14(LR,Link Register)
链接寄存器,一般有以下两种用途:
> 执行跳转指令(BL/BLX)时,LR会自动保存跳转指令下一条指令的地址程序需要返回时将LR的值复制到PC即可实现
> 产生异常时,对应异常模式下的LR会自动保存被异常打断的指令的下一条指令的地址,异常处理结束后将LR的值复制到PC可实现程序返回
原理
当执行跳转指令或产生异常时,LR寄存器中不会凭空产生一个返回地址,其原理是当执行跳转指令或产生异常时,处理器内部会将PC寄存器中的值拷贝到LR寄存器中,然后再将LR寄存器中的值自减4
BL
当执行BL指令时,指令执行过程中处理器内部就会将PC寄存器的值拷贝到LR寄存器,然后再将LR寄存器中的值自减4, 所以LR寄存器中保存的就是BL指令下一条指令的地址
该时刻PC=N+8 LR=N+4
IRQ中断
IRQ中断
当执行一条指令时产生了一个IRQ中断,执行这条指令过程中处理器不会保存返回地址,而是执行完成后才会保存,但执行完成后PC的值又会自动增4所以对于IRQ来说LR中保存的是被中断打断的指令的下下条指令的地址
该时刻PC=N+12 LR=N+8
#include "exynos_4412.h"
//异常处理
void do_irq(void)
{
unsigned int IrqNum = 0;
//从中断控制器获取终端号
IrqNum = CPU0.ICCIAR & (0x3ff);
switch(IrqNum)
{
case 0:
//0号中断处理程序
break;
case 1:
//1号中断处理程序
break;
/*
*
* */
case 57:
printf("Key2 Pressed\n");
//清除GPIO控制器中的中断挂起位
EXT_INT41_PEND = (1 << 1);
//通知中断控制器,当前中断已经处理完成,可以发送其他的中断
CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3ff)) | (57);
break;
/**
*...
*/
case 159:
//159号中断处理程序
break;
default:
break;
}
}
void delay(unsigned int Time)
{
while(Time--);
}
int main()
{
/*(一)外设层次————让外部的硬件控制器能产生一个中断信号并发送给中断控制器*/
/*将GPX1_1设置为中断功能*/
GPX1.CON = GPX1.CON | (0xF << 4);
/*设置GPX1_1中断触发方式*/
EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
/*使能GPX1_1的中断功能*/
EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1));
/* (二)中断控制器层次————让中断控制器接收外设发来的中断信号并对其进行管理然后转发给一个合适的CPU去处理*/
/*全局使能中断控制器----使其能够接收外部设备产生的中断信号并转发给CPU*/
ICDDCR = ICDDCR | 1;
/*在中断控制器中使能57号端口,使中断控制器在接收到57号中断后能将其进一步转发到CPU接口*/
ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);
/*将57号中断交予CPU0处理*/
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xff << 8)) | (0x01 << 8);
/*将中断控制器与CPU0之间的接口使能,使得中断控制器转发的信号能够到达CPU0*/
CPU0.ICCICR = CPU0.ICCICR | 1;
GPX2.CON = GPX2.CON & (~(0xf<<28)) | (0x1<<28);
while(1){
delay(1000000);
GPX2.DAT = GPX2.DAT | (1<<7); //led2点亮
delay(1000000);
GPX2.DAT = GPX2.DAT & (~(1<<7));
}
return 0;
}