基于三星 ARM Cortex-A9 Exynos4412处理器的底层实验(外部中断实验)
【1】正常启动流程
1.设备一上电,中断向量表作为第一段执行程序
2.初始化异常向量表基地址协处理器(默认是0,修改后异常跳转时,PC值=基地址+向量表位置偏移)
3.禁用MMU内存管理单元
4.分配栈空间,初始化各个模式下的栈空间(user栈初始化放在最后)
5.跳转到main执行程序
【2】IRQ中断响应原理
假设产生中断时,CPU当中正在执行一段程序(正常情况),突然之间产生外部中断,整个CPU的相应以及处理流程:
1.CPU相应流程(CPU自己完成)
1)复制备份CPSR到SPSR_IRQ
2)配置CPSR寄存器
设置工作状态为arm态
修改工作模式为IRQ模式
禁用相应中断(IRQ)
3)保存返回地址到LR_IRQ
4)设置PC到异常向量表中的IRQ处理项(0x18)
2.根据异常向量表中的跳转语句跳转到IRQ_Handle处理程序(程序员写的)
1)调整LR的值,为异常结束返回做准备
2)入栈保护寄存器数据
3)跳转到IRQ_Handle处理函数
4)出栈恢复数据
5)返回退出异常程序,并恢复CPSR
【3】中断初始化配置
1.谁产生的中断
2.什么时候产生中断
3.中断怎么到达SOC
4.产生的中断交给哪个CPU去处理
5.同时产生多个中断时,怎么处理
6.当一个IRQ中断正在处理的时候,又来了同类型的一个中断怎么办
【4】GIC中断控制器作用
中断控制器GIC的作用
1.当多个中断同时产生的时候,中断管理器将对这些中断进行排队,将优先级最高的转发给CPU处理,其他再进行排队等待
2.当一个中断正在处理的时候,外设又产生另一个中断,中断管理器会对这个中断进行记忆,等上一个响应完成后再去响应下一个
3**.为每一个中断分配一个CPU去处理**
4.为每个中断选择一个中断类型(FIQ或IRQ)
5.CPU不能区分是哪个外设产生的中断,但CPU可以通过查询中断管理器来获取当前中断的中断号
6.在中断管理器中我们可以任意打开或关闭一个中断
7.为每一个中断分配一个优先级
【5】按键K2控制蜂鸣器实验(蜂鸣器实验配置不做描述,前面有写)
1.阅读硬件原理图,得到K2与SOC之间的对应连接关系:GPX1_1
2.查找用户手册,将GPX1_1配置为中断功能。
//选择GPIO引脚功能为EINT
GPX1.CON |= (0xf<<4);
3.配置EXT_INT41CON寄存器,选择触发中断条件(电平、边沿)
//设置中断信号检测:↓下降沿
EXT_INT41_CON &= ~(0x7<<4);
EXT_INT41_CON |= (2<<4);
4.配置EXT_INT41_MASK寄存器,开启按键中断,使之能够到达GIC
//使能GPIO对中断的检测
EXT_INT41_MASK &= ~(1<<1);
5.配置GIC中断管理器,对K2按键中断进行管理配置(第9章节)
从9.1.1获取到GIC支持的中断类型和功能,从Figure 9-1 了解到中断大致物理结构;从Table 9-2了解到GIC对每个中断源的编号管理(ID);中断原理部分阅读9.4.1章节,具体配置看9.4.2章节.
//中断白名单(中断使能):SPI中断号25,全局中断号57
ICDISER.ICDISER1 |= (1<<25);
1)使能中断源
设置ICDISER寄存器使能对应ID中断源,使之能够到达GIC(9.4.1.2章节)
这里要注意,ICDISER和ICDICER的功能是一样,在ICDISER中写是使能,在ICDICER写1是禁用
2)中断挂起状态设置和清除(在本次实验中不需要设置)
设置ICDISPR寄存器挂起中断,设置ICDICPR寄存器清除挂起(每次结束中断时需要清除)
3)选择处理中断CPU
配置ICDIPTR寄存器,选择CPU0处理k2按键中断。
//为中断分配处理的CPU0
ICDIPTR.ICDIPTR14 &= ~(0xff<<8);
ICDIPTR.ICDIPTR14 |= (1<<8);
5)设置中断门槛
配置ICCPMR寄存器,为到达CPU的中断设置一个中断优先级过滤门槛
//设置优先级别屏蔽门槛
CPU0.ICCPMR = 255;
6)打开CPU0的接口中断开关
ICCICR
//使能CPU接口中断
CPU0.ICCICR = 1;
7)打开GIC
ICDDCR
6.中断处理函数
1)获取中断号
目的是让CPU中运行处理程序识别是哪一个中断源产生的中断
int irq_num = 0;
//读取中断号
irq_num = CPU0.ICCIAR & 0x3ff;
2)根据中断号选择处理程序
switch(irq_num)
{
case 57: //按键K2
//3、切换蜂鸣器状态
pwm_change();
printf("in do_irq\n");
//清除GPx1_1中断标记
EXT_INT41_PEND |= (1<<1);
break;
}
3)清除中断标记
EXT_INT41_PEND写1清零
ICDICPR写1清0
ICCEOIR写中断号清除中断
//清除GIC中断标记
CPU0.ICCEOIR |= irq_num;
代码:
#include "exynos_4412.h"
void pwm_init(void)
{
//1、选择引脚功能pwmTOU0输出功能
GPD0.CON &= ~(0xf<<0);
GPD0.CON |= (2<<0);
//2、一级分频:249
PWM.TCFG0 &= ~(0xff<<0);
PWM.TCFG0 |= (249<<0);
//3、二级分频:4
PWM.TCFG1 &= ~(0xf<<0);
PWM.TCFG1 |= (2<<0);
//4、设置周期:1000Hz,占空比50%
PWM.TCNTB0 = 100;
PWM.TCMPB0 = PWM.TCNTB0 / 2;
//5、手动更新TCNT和TCMP
PWM.TCON |= (1<<1);
PWM.TCON &= ~(1<<1);
//6、开启自动重装载
PWM.TCON |= (1<<3);
}
void pwm_change(void)
{
//7、启动定时器
PWM.TCON ^= (1<<0);
if(PWM.TCON & 1)
printf("pwm is open!\n");
else
printf("pwm is close!\n");
}
void key_init(void)
{
/****************GPIO配置*****************/
//1、选择GPIO引脚功能为EINT
GPX1.CON |= (0xf<<4);
//2、设置中断信号检测:↓下降沿
EXT_INT41_CON &= ~(0x7<<4);
EXT_INT41_CON |= (2<<4);
//3、使能GPIO对中断的检测
EXT_INT41_MASK &= ~(1<<1);
/****************GIC配置*****************/
//4、中断白名单(中断使能):SPI中断号25,全局中断号57
ICDISER.ICDISER1 |= (1<<25);
//5、使能GICD调度器检测中断信号
ICDDCR = 1;
//5、为中断分配处理的CPU0
ICDIPTR.ICDIPTR14 &= ~(0xff<<8);
ICDIPTR.ICDIPTR14 |= (1<<8);
//6、设置优先级别屏蔽门槛
CPU0.ICCPMR = 255;
//7、使能CPU接口中断
CPU0.ICCICR = 1;
}
void delay(unsigned int time)
{
int i;
for(; time>0; time--)
{
for(i=2000; i>0; i--)
{
;
}
}
}
void do_irq(void)
{
int irq_num = 0;
//1、读取中断号
irq_num = CPU0.ICCIAR & 0x3ff;
//2、识别中断源,选择执行对应处理程序
switch(irq_num)
{
case 57: //按键K2
//3、切换蜂鸣器状态
pwm_change();
printf("in do_irq\n");
//清除GPx1_1中断标记
EXT_INT41_PEND |= (1<<1);
break;
}
//清除GIC中断标记
// CPU0.ICCEOIR |= (CPU0.ICCIAR & 0x3ff);
CPU0.ICCEOIR |= irq_num;
delay(300);
}
int main(void)
{
/*1初始化按键*/
key_init();
pwm_init();
while(1)
{
;
}
return 0;
}