ARM异常这里昨天本应该就完成了,加上断网,修电脑,放松一下啥的给耽误了。。。
Clock中start.s虽然拷到了,从0地址开始的地址,但是能正常工作,那是因为PC是一条一条的挨着执行指令的,而且没有发生异常,所以能正常运行。
Vector中的start.s那样就支持了异常发生时的处理,也就是发生异常也能正确运行。
/*个人对以前一个程序执行过程的理解,和这里没关系*/
上节我们说了异常。中断也是异常中的一种,有FIQ,IRQ,这里只说IRQ。
6410的中断:总是有一个外部触发点,比如按键中断,我们这里就用按键来说明,,然后中断进入6410soc里的中断控制器,控制器进行识别是那个中断,然后把中断信号发给CPU,CPU从当前模式进入到中断处理模式,处理完中断,顺便清除中断。所以我们在写中断裸机驱动的时候,就按着这个思路一步步设置下来。
6410有两个VIC(矢量中断控制器),分为VIC0和VIC1,每一个VIC支持32个中断源,总共支持64个中断源。每个中断源有个中断号,从0~63。
当发生一个中断时,我们需要执行相应的ISR,如何让CPU准确的去执行自己的ISR呢?这里就引入以下的这些 矢量地址寄存器,总共有64个,他们的作用就是记录每个中断处理函数的地址。当要执行某个ISR时,硬件就会自动把VICxVECTADDRn中的值付给VIC0ADDRESS这个寄存器……感觉不好描述,再说点重要的,我们直接对着代码分析中断的过程吧。
VIC0的基础地址是0x7120_0000
VIC1的基础地址是0x7130_0000
中断中的所有寄存器的地址都是在这俩个地址的后边加一个偏移,定义这些寄存器的时候需要注意。
啥也不说了,跟个代码走:
这个先不看,是按键中断服务程序。先看下一个程序。
void key_isr(void)
{
if(EINT0PEND & (1 << 0)){ //判别第0组外部中断的第0个中断是否发生,多个中断用一个I/o引脚。
EINT0PEND |= (1 << 0); //清除中断。
uprintf("EINT0 happend\n");
}
if(EINT0PEND & (1 << 1)){
EINT0PEND |= (1 << 1);
uprintf("EINT1 happend\n");
}
if(EINT0PEND & (1 << 2)){
EINT0PEND |= (1 << 2);
uprintf("EINT2 happend\n");
}
if(EINT0PEND & (1 << 3)){
EINT0PEND |= (1 << 3);
uprintf("EINT3 happend\n");
}
if(EINT0PEND & (1 << 4)){
EINT0PEND |= (1 << 4);
uprintf("EINT4 happend\n");
}
if(EINT0PEND & (1 << 5)){
EINT0PEND |= (1 << 5);
uprintf("EINT5 happend\n");
}
}
//这个按键初始化函数就是要设置一个外部中断触发事件
void key_init(void)
{
GPNCON &= ~(0xfff << 0);
GPNCON |= (0xaaa << 0); //本板子有6个按键,把这些按键设置成外部中断模式。
EINT0CON0 &= ~(0xfff << 0);//中断中有外部中断,内部中断如串口中断,定时器中断……,而外部中断有9个中断组,按键在第0个中断组中。
EINT0CON0 |= (0x222 << 0); //设置为下降沿触发
EINT0MASK &= ~(0x3f << 0); //这里还的使能,让按键到中断控制器之间的开关打开。
request_irq(0,key_isr);//第一个参数表示,中断号(0~63),第二表示中断后要执行的函数,也就是上面那个函数。
request_irq(1,key_isr);
}
void do_irq(void)
{
void (*handle)(void) = 0;
if(VIC0ADDRESS != 0){
handle = (void *)VIC0ADDRESS;
}
if(VIC1ADDRESS != 0){
handle = (void *)VIC1ADDRESS;
}
if(handle != 0){
handle();
}
VIC0ADDRESS = 0;
VIC1ADDRESS = 0;
}
void request_irq(int no,void (*handle)(void))
{
if(no < 32 && no >= 0){
*(&VIC0VECTADDR0 + no) = (unsigned int)handle; //这里寄存器的作用上面说过。/*CPU运行中发生一个中断之后就跳到本文章最后的汇编哪里,然后跳到do_irq函数,在这里判断是那个VIC里发生的中断(中断时,硬件自动把VICxVECTADDRn的值赋给VICxADDRESS)然后执行ISR,最后那两句话是清除中断,手册里说写任意值都行*/
VIC0INTENABLE |= (1 << no);
}
if(no < 64 && no >= 32){
*(&VIC1VECTADDR0 + no - 32) = (unsigned int)handle;
VIC1INTENABLE |= (1 << (no - 32));
}
}
/*打开CPU的总中断开关位*/
mrs r0,cpsr
bic r0,#(1 << 7)
msr cpsr,r0
/*发生中断异常时跳到这里*/
irq:
mov sp,#0x1800
stmfd sp!,{r0-r12,lr}
bl do_irq
ldmfd sp!,{r0-r12,pc}^
b .
看到这里是不是觉得写得很烂,其实我也感觉很烂,很多功能没有用到,我是觉得初学者一切从简单做起容易成功,二是心急想要进行下一部分,所以就不花时间细研究了,以后慢慢深入!
好在网上我又找到了一篇相关的文章,写的很详细,图文并茂。下一篇文章我转过来,大家细细研究下。。。
阅读(2088) | 评论(0) | 转发(1) |