我认为还要加上这几句:
cpsr_init: @设置cpsr寄存器
mrs r0,cpsr
bic r0,r0,#0x53 //开启IrQ,进入管理模式
msr cpsr_c,r0
参考:
(1)https://blog.csdn.net/kiny_anderson/article/details/46884893
(2)https://blog.csdn.net/guohengsheng3882/article/details/78448050
(3)深入剖析ARM Cortex-A8 (378页)
****************************************************************************
流程图:
1.当发生中断————>>
2.程序会跳到相应的中断向量表,找相应的中断向量地址————>>
3.进入IRQ_handle,保存现场————>>
4.跳转到irq_handler,irq_handler分析哪一个VICN ADDR有值,把相应的处理函数地址给(*isr)()————>>
5.进入相应的中断处理函数,这三个(isr_eint2,isr_eint3,isr_eint16171819)真正的中断处理函数,
执行相应的内容,清除中断挂起{rEXT_INT_0_PEND |= (1<<3); intc_clearvectaddr();}————>>
6.恢复现场回到IRQ_handle,恢复现场————>>
7.回到主函数
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
main: ,中断处理步骤:
1.Uart.c的 串口初始化程序void uart_init(void)
2.以中断方式来处理按键的初始化 ,key_init_interrupt();
{
1. 外部中断对应的GPIO模式设置
2. 中断触发模式设置
3. 中断允许
4. 清挂起,清除是写1,不是写0
}
3.中断初始化来初步初始化中断控制器,system_init_exception();
{
1.第一阶段处理,绑定异常向量表
2.初始化中断控制器的基本寄存器,void intc_init(void)
{
1.禁止所有中断
VIC0INTENCLEAR = 0xffffffff;
VIC1INTENCLEAR = 0xffffffff;
VIC2INTENCLEAR = 0xffffffff;
VIC3INTENCLEAR = 0xffffffff;
2.选择中断类型为IRQ
VIC0INTSELECT = 0x0;
VIC1INTSELECT = 0x0;
VIC2INTSELECT = 0x0;
VIC3INTSELECT = 0x0;
}
3.清VICxADDR,清除需要处理的中断的中断处理函数的地址,void intc_clearvectaddr(void)
{
// VICxADDR:当前正在处理的中断的中断处理函数的地址
VIC0ADDR = 0; //存放当前活动的ISR地址,复位值为0x0000_0000
VIC1ADDR = 0;
VIC2ADDR = 0;
VIC3ADDR = 0;
}
}
4.绑定isr到中断控制器硬件
{
intc_setvectaddr(KEY_EINT2, isr_eint2); //KEY_EINT2 = 2
intc_setvectaddr(KEY_EINT3, isr_eint3); //KEY_EINT3 = 3
intc_setvectaddr(KEY_EINT16_19, isr_eint16171819); //KEY_EINT16_19 = 16
}
5.使能中断
{
intc_enable(KEY_EINT2); //通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
intc_enable(KEY_EINT3); //通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
intc_enable(KEY_EINT16_19); //VIC0INTENABLE
}
################################################################
异常向量表在irom_application中可以找到,直接搜0xD003_7400,然后有下面得到地址
#define exception_vector_table_base 0xD0037400
#define exception_reset (exception_vector_table_base + 0x00)
#define exception_undef (exception_vector_table_base + 0x04)
#define exception_sotf_int (exception_vector_table_base + 0x08)
#define exception_prefetch (exception_vector_table_base + 0x0C)
#define exception_data (exception_vector_table_base + 0x10)
#define exception_irq (exception_vector_table_base + 0x18)
#define exception_fiq (exception_vector_table_base + 0x1C)
################################################################
bl main
b .
IRQ_handle: //当发生中断,程序会跳到相应的中断向量表,找相应的中断执行程序,然后就会跳到这里
// 设置IRQ模式下的栈
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}^
######################################################################
// int.c的程序 通过读取VICnIRQSTATUS寄存器,判断其中哪个有一位为1,来得知哪个VIC发生中断了
unsigned long intc_getvicirqstatus(unsigned long ucontroller)
{
if(ucontroller == 0) //他这里因为只有
return VIC0IRQSTATUS; //中断状态寄存器,只读
else if(ucontroller == 1)
return VIC1IRQSTATUS;
else if(ucontroller == 2)
return VIC2IRQSTATUS;
else if(ucontroller == 3)
return VIC3IRQSTATUS;
else
{}
return 0;
}
// int.c的程序 真正的中断处理程序。意思就是说这里只考虑中断处理,不考虑保护/恢复现场
void irq_handler(void)
{
//printf("irq_handler.\n");
// SoC支持很多个(在低端CPU例如2440中有30多个,在210中有100多个)中断
// 这么多中断irq在第一个阶段走的是一条路,都会进入到irq_handler来
// 我们在irq_handler中要去区分究竟是哪个中断发生了,然后再去调用该中断
// 对应的isr。
// 虽然硬件已经自动帮我们把isr放入了VICnADDR中,但是因为有4个,所以我们必须
// 先去软件的检查出来到底哪个VIC中断了,也就是说isr到底在哪个VICADDR寄存器中
unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};
int i=0;
void (*isr)(void) = NULL; //指针函数
for(i=0; i<4; i++)
{
// 发生一个中断时,4个VIC中有3个是全0,1个的其中一位不是0
if(intc_getvicirqstatus(i) != 0)
{
isr = (void (*)(void)) vicaddr[i];
break;
}
}
(*isr)(); // 通过函数指针来调用函数
}
#########################################################################
Main.c的 int main(void)
{
——————————————————————————————————————————————————————————
uart_init();
//Uart.c的 串口初始化程序
void uart_init(void)
{
// 初始化Tx Rx对应的GPIO引脚
rGPA0CON &= ~(0xff<<0); // 把寄存器的bit0~7全部清零
rGPA0CON |= 0x00000022; // 0b0010, Rx Tx
// 几个关键寄存器的设置
rULCON0 = 0x3;
rUCON0 = 0x5;
rUMCON0 = 0;
rUFCON0 = 0;
// 波特率设置 DIV_VAL = (PCLK / (bps x 16))-1
// PCLK_PSYS用66MHz算 余数0.8
//rUBRDIV0 = 34;
//rUDIVSLOT0 = 0xdfdd;
// PCLK_PSYS用66.7MHz算 余数0.18
// DIV_VAL = (66700000/(115200*16)-1) = 35.18
rUBRDIV0 = 35;
// (rUDIVSLOT中的1的个数)/16=上一步计算的余数=0.18
// (rUDIVSLOT中的1的个数 = 16*0.18= 2.88 = 3
rUDIVSLOT0 = 0x0888; // 3个1,查官方推荐表得到这个数字
}
—————————————————————————————————————————————————————————————————————————————————————
//key_init(); 以中断方式来处理按键的初始化
key_init_interrupt();
//Key.c的程序
void key_init_interrupt(void)
{
// 1. 外部中断对应的GPIO模式设置
rGPH0CON |= 0xFF<<8; // GPH0_2 GPH0_3设置为外部中断模式
rGPH2CON |= 0xFFFF<<0; // GPH2_0123共4个引脚设置为外部中断模式
//这里是通过核心板,找相应的GPIO口的
// 2. 中断触发模式设置
rEXT_INT_0_CON &= ~(0xFF<<8); // bit8~bit15全部清零
rEXT_INT_0_CON |= ((2<<8)|(2<<12)); // EXT_INT2和EXT_INT3设置为下降沿触发
rEXT_INT_2_CON &= ~(0xFFFF<<0);
rEXT_INT_2_CON |= ((2<<0)|(2<<4)|(2<<8)|(2<<12));
// 3. 中断允许
rEXT_INT_0_MASK &= ~(3<<2); // 外部中断允许
rEXT_INT_2_MASK &= ~(0x0f<<0); //注意是取反,是让它变为0
// 4. 清挂起,清除是写1,不是写0
rEXT_INT_0_PEND |= (3<<2);
rEXT_INT_2_PEND |= (0x0F<<0);
}
————————————————————————————————————————————————————————————————————————————————————————
// 如果程序中要使用中断,就要调用中断初始化来初步初始化中断控制器
system_init_exception();
// 主要功能:绑定第一阶段异常向量表;禁止所有中断;选择所有中断类型为IRQ;
//int.c的程序 清除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; //我自己认为是他把FIQ和IRQ写在了一起
// 初始化中断控制器的基本寄存器
// 初始化中断控制器
void intc_init(void)
{
// 禁止所有中断
// 为什么在中断初始化之初要禁止所有中断?
// 因为中断一旦打开,因为外部或者硬件自己的原因产生中断后一定就会寻找isr
// 而我们可能认为自己用不到这个中断就没有提供isr,这时它自动拿到的就是乱码
// 则程序很可能跑飞,所以不用的中断一定要关掉。
// 一般的做法是先全部关掉,然后再逐一打开自己感兴趣的中断。一旦打开就必须
// 给这个中断提供相应的isr并绑定好。
VIC0INTENCLEAR = 0xffffffff;
VIC1INTENCLEAR = 0xffffffff;
VIC2INTENCLEAR = 0xffffffff;
VIC3INTENCLEAR = 0xffffffff;
// 选择中断类型为IRQ
VIC0INTSELECT = 0x0;
VIC1INTSELECT = 0x0;
VIC2INTSELECT = 0x0;
VIC3INTSELECT = 0x0;
}
// 清VICxADDR
// 清除需要处理的中断的中断处理函数的地址
void intc_clearvectaddr(void)
{
// VICxADDR:当前正在处理的中断的中断处理函数的地址
VIC0ADDR = 0; //存放当前活动的ISR地址,复位值为0x0000_0000
VIC1ADDR = 0;
VIC2ADDR = 0;
VIC3ADDR = 0;
}
}
——————————————————————————————————————————————————————————————————————————————
printf("-------------key interrypt test--------------");
——————————————————————————————————————————————————————————————————————————————
// 绑定isr到中断控制器硬件
intc_setvectaddr(KEY_EINT2, isr_eint2); //KEY_EINT2 = 2
intc_setvectaddr(KEY_EINT3, isr_eint3); //KEY_EINT3 = 3
intc_setvectaddr(KEY_EINT16_19, isr_eint16171819); //KEY_EINT16_19 = 16
// 绑定我们写的isr到VICnVECTADDR寄存器
// 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理
// 等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可。
// 参数:intnum是int.h定义的****物理中断号#####,handler是函数指针,就是我们写的isr
// VIC0VECTADDR定义为VIC0VECTADDR0寄存器的地址,就相当于是VIC0VECTADDR0~31这个
// 数组(这个数组就是一个函数指针数组)的首地址,然后具体计算每一个中断的时候
// 只需要首地址+偏移量即可。 arm总共有 32 * 4 = 128个VIC0VECTADDR寄存器
Int.c的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;
}
-----------------------------------
这是三个(isr_eint2,isr_eint3,isr_eint16171819)函数
-----------------------------------
// EINT2通道对应的按键,就是GPH0_2引脚对应的按键,就是开发板上标了LEFT的那个按键
void isr_eint2(void)
{
// 真正的isr应该做2件事情。
// 第一,中断处理代码,就是真正干活的代码
printf("isr_eint2_LEFT.\n");
// 第二,清除中断挂起
rEXT_INT_0_PEND |= (1<<2); //GPIO的PEND寄存器
void intc_clearvectaddr(void)
{
// VICxADDR:当前正在处理的中断的中断处理函数的地址
VIC0ADDR = 0; //存放当前活动的ISR地址,复位值为0x0000_0000
VIC1ADDR = 0;
VIC2ADDR = 0;
VIC3ADDR = 0;
}
}
void isr_eint3(void)
{
// 真正的isr应该做2件事情。
// 第一,中断处理代码,就是真正干活的代码
printf("isr_eint3_DOWN.\n");
// 第二,清除中断挂起
rEXT_INT_0_PEND |= (1<<3); //GPIO的PEND寄存器
intc_clearvectaddr();
}
void isr_eint16171819(void)
{
// 真正的isr应该做2件事情。
// 第一,中断处理代码,就是真正干活的代码
// 因为EINT16~31是共享中断,所以要在这里再次去区分具体是哪个子中断
if (rEXT_INT_2_PEND & (1<<0)) //0——没有产生中断 1——产生中断
{
printf("eint16\n");
}
if (rEXT_INT_2_PEND & (1<<1))
{
printf("eint17\n");
}
if (rEXT_INT_2_PEND & (1<<2))
{
printf("eint18\n");
}
if (rEXT_INT_2_PEND & (1<<3))
{
printf("eint19\n");
}
// 第二,清除中断挂起
rEXT_INT_2_PEND |= (0x0f<<0); //GPIO的PEND寄存器
intc_clearvectaddr();
}
————————————————————————————————————————————————————————————————————————————————————
// 使能中断
intc_enable(KEY_EINT2);
intc_enable(KEY_EINT3);
intc_enable(KEY_EINT16_19);
// 使能中断
//Int.c的程序 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
//前面只是把使能清除寄存器关闭 VIC0INTENCLEAR = 0xffffffff;
void intc_enable(unsigned long intnum)
{
unsigned long temp;
// 确定intnum在哪个寄存器的哪一位
// <32就是0~31,必然在VIC0
if(intnum<32)
{
temp = VIC0INTENABLE;
temp |= (1<
VIC0INTENABLE = temp; //0——无效 1——允许中断
}
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;
}
}
————————————————————————————————————————————————————————————————————————————————————-
// 在这里加个心跳
while (1)
{
printf("A ");
delay(10000);
}
return 0;
}