S5PV210中断系统

中断实现机制:异常向量表

1、中断是指CPU在执行程序的过程中,遇到异常情况需要处理,CPU就会中断当前的程序,保存当前程序运行处的必要参数,跳去执行异常程序,处理结束后再返回之前程序的间断处,继续执行原程序。

2、异常向量表是CPU中某些特定地址的特定定义,当中断发生时会去该向量表查询对应的异常事件,并处理该事件,完成后返回。

3、对于SOC来说,发生复位、软中断、中断、快速中断、取指令异常、数据异常等,我们都统一叫异常,所以说:中断其实是异常的一种。

4、S5PV210的异常向量表可以改变(在CP15协处理器中),以适应操作系统的需求。但是系统刚启动的时候,DRAM还没初始化,程序都在SRAM中运行。所以S5PV210在IRAM中设置异常向量表,供暂时使用。iRAM中的异常向量表起始地址为0xD0037400。

5、函数名的实质就是函数的首地址,当我们将异常处理程序的首地址和异常向量表绑定起来后,可以保证相应异常发生后,硬件自动跳转到对应异常向量表入口去执行时,可以执行到我们事先绑定的函数。

汇编保存现场和恢复现场

中断去执行异常程序时,要先保护现场和恢复现场。保护现场:中断从SVC模式来,则需要保存SVC模式下必要寄存器的值。恢复现场:中断处理完成后,要返回SVC模式时,将之前SVC模式保存的寄存器恢复回去,以免程序错乱。保存现场包括--->第一:设置IRQ栈;第二,保存LR;第三,保存R0~R12。

为什么要保存LR寄存器?

要考虑中断返回的问题。中断ISR执行完后如何返回SVC模式下去接着执行原来的代码。中断返回其实取决于我们进入中断时如何保存现场。中断返回时关键的2个寄存器就是PC和CPSR。所以我们在进入IRQ模式时,应该将SVC模式下的下一句指令的地址(中断返回地址)和CPSR保存起来,将来恢复时才可以将中断返回地址给PC,将保存的SPSR给CPSR。中断返回地址保存在LR中,CPSR保存在IRQ模式下的SPSR中。保护现场关键是保存:中断处理程序的返回地址,r0-r12(cpsr是自动保存的),恢复现场主要是恢复:r0-r12,pc,cpsr。

S5PV210的向量中断过程

异常可分为两个阶段来理解。第一个阶段是异常向量表跳转;第二个阶段就是进入了真正的异常处理程序irq_handler之后的部分。第一个阶段主要依赖CUP提供的向量表机制,主要的任务是从异常发生到响应异常并且保存和恢复现场。第二个任务目的是识别是哪个中断源,然后调用相应的处理程序来处理这个中断。

 

 

S5PV210与中断有关的寄存器

1、VICnINTENABLE

 

 

负责相应的中断的使能。当我们想使能某个中断时,只要在这个中断编号对应的VICnINTENABLE相应位写1就可以。

 

2、VICnINTENCLEAR

 

当我们执行完中断处理程序后,应当清除中断标志位,下次再次中断时才能进入。有些CPU是中断使能和禁止是一个寄存器位,写1就使能写0就进制(或者反过来写1就进制写0就使能)

 

3、VICnINTSELECT

 

设置各个中断的模式为irq还是fiq。

1.IRQ和FIQ的区别?210中支持2种中断,irq和fiq。irq是普通中断,fiq是快速中断。快速中断是一种更快响应处理的中断通道,用于对实时性要求很高的中断源。fiq在CPU设计时预先提供了一些机制保证fiq可以被快速处理,从而保证实时性。fiq的限制就是只能有一个中断源被设置为fiq,其他都是irq。

2.CPU如何保证fiq比irq快?有2个原因:第一,fiq模式有专用的r8~r12,因此在fiq的isr中可以直接使用r8-r12而不用保存,这就能节省时间;第二,异常向量表中fiq是最后一个异常向量入口。因此fiq的isr不需要跳转,可以直接写在原地,这样就比其他异常少跳转一次,省了些时间。

 

4、VICnIRQSTATUS和VICnFIQSTATUS

 

只读的状态寄存器,当发生中断时,硬件自动将该寄存器对应的位置为1,表明中断发生。

 

5、VICVECTPRIORITY

 

中断优先级设置寄存器,设置多个中断同时发生时先处理谁后处理谁的问题,可支持中断嵌套。

 

VICnVECTADDR0到31这32个寄存器分别用来存放真正的各个中断对应的isr的函数地址。相当于每一个中断源都有一个VECTADDR寄存器,设置中断的时候,把这个中断的isr地址直接放入这个中断对应的VECTADDR寄存器即可。VICnADDR这个寄存器是只需要读的,它里面的内容是由硬件自动设置的,当发生了相应中断时,硬件会自动识别中断编号,并且会自动找到这个中断的VECTADDR寄存器,然后将其读出复制到VICnADDR中,供我们使用。

 

 

 

Start.s文件

 

#define  SVC_STACK 0xd0037d80

#define  IRQ_STACK 0xd0037f80

 

.global _start

.global IRQ_handle

_start:

bl clock_init   // 初始化时钟

ldr sp, = SVC_STACK // 设置SVC栈

/* 开/关icache */

mrc p15,0,r0,c1,c0,0;// 读出cp15的c1到r0中

//bic r0, r0, #(1<<12)// bit12 置0  关icache

orr r0, r0, #(1<<12)// bit12 置1  开icache

mcr p15,0,r0,c1,c0,0;

bl main

//bl led_blink

b .

// 在这个汇编函数中,用来做中断模式下的现场保护和恢复,并且调用真正的中断处理程序

IRQ_handle:

ldr sp, = IRQ_STACK   // 设置IRQ模式下的栈

// 保存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}^

 

/*********以下对应的寄存器宏定义没写上来,可查看手册**********/

void key_init_interrupt(void)

{

// 1. 外部中断对应的GPIO模式设置

rGPH0CON |= 0xFF<<8;// GPH0_2 GPH0_3设置为外部中断模式

rGPH2CON |= 0xFFFF<<0;// GPH2_0123共4个引脚设置为外部中断模式

// 2. 中断触发模式设置

rEXT_INT_0_CON &= ~(0xFF<<8); // bit8~bit15全部清零

   // EXT_INT2和EXT_INT3设置为下降沿触发

rEXT_INT_0_CON |= ((2<<8)|(2<<12));

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);

// 4. 清挂起,清除是写1,不是写0

rEXT_INT_0_PEND |= (3<<2);

rEXT_INT_2_PEND |= (0x0F<<0);

}

void intc_init(void)

{

    // 禁止所有中断

    VIC0INTENCLEAR = 0xffffffff;

    VIC1INTENCLEAR = 0xffffffff;

    VIC2INTENCLEAR = 0xffffffff;

    VIC3INTENCLEAR = 0xffffffff;

 

    // 选择中断类型为IRQ

    VIC0INTSELECT = 0x0;

    VIC1INTSELECT = 0x0;

    VIC2INTSELECT = 0x0;

    VIC3INTSELECT = 0x0;

 

    // 清VICxADDR

    intc_clearvectaddr();

}

 

 

// 绑定异常向量表;禁止所有中断;选择所有中断类型为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_clearvectaddr(void)

{

    // VICxADDR:当前正在处理的中断的中断处理函数的地址

    VIC0ADDR = 0;

    VIC1ADDR = 0;

    VIC2ADDR = 0;

    VIC3ADDR = 0;

}

// 通过读取VICnIRQSTATUS寄存器,判断其中哪个有一位为1,可知哪个VIC发生中断了

unsigned long intc_getvicirqstatus(unsigned long ucontroller)

{

    if(ucontroller == 0)

        returnVIC0IRQSTATUS;

    else if(ucontroller == 1)

        return VIC1IRQSTATUS;

    else if(ucontroller == 2)

        return VIC2IRQSTATUS;

    else if(ucontroller == 3)

        return VIC3IRQSTATUS;

    else

    {}

    return 0;

}

 

 

//寻找对应中断处理程序

void irq_handler(void)

{

/* 此函数是检查是哪一个VIC的中断,就是isr对应在哪个VICADDR寄存器

      中,然后调用对应中断isr*/

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)();// 通过函数指针来调用函数

}

//中断服务函数

void isr_eint2(void)

{

printf("isr_eint2_LEFT.\n");

rEXT_INT_0_PEND |= (1<<2);  // 清除中断挂起

intc_clearvectaddr();

}

 

void isr_eint3(void)

{

printf("isr_eint3_DOWN.\n");

rEXT_INT_0_PEND |= (1<<3);

intc_clearvectaddr();

}

 

void isr_eint16171819(void)

{

if (rEXT_INT_2_PEND & (1<<0))

{

printf("eint16\n");

}

rEXT_INT_2_PEND |= (0x0f<<0);

intc_clearvectaddr();

}

 

void intc_setvectaddr(unsigned long intnum, void (*handler)(void)) {

    if(intnum<32)  { //VIC0

       *( (volatile unsigned long *)(VIC0VECTADDR + 4*(intnum-0)) ) =

        (unsigned)handler;

    }

    else if(intnum<64) {   //VIC1

        *( (volatile unsigned long *)(VIC1VECTADDR + 4*(intnum-32)) ) =

         (unsigned)handler;

    }

    else if(intnum<96) {  //VIC2

        *( (volatile unsigned long *)(VIC2VECTADDR + 4*(intnum-64)) ) =

         (unsigned)handler;

    }

     else {   //VIC3

        *( (volatile unsigned long *)(VIC3VECTADDR + 4*(intnum-96)) ) =

         (unsigned)handler;

    }

    return;

}

 

// 读取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;

}

 

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值