RealView MDK 与 AT91SAM7S64: 中断中实现LED闪烁

 
    终于在中断中实现了示例1的功能,首先让我们来看一下IAR与RealView的IRQ中断代码及它们的区别。

     IAR启动代码中关于IRQ的代码如下:

     reset
        B InitReset ; 0x00 Reset handler
    undefvec:
        B undefvec ; 0x04 Undefined Instruction
    swivec:
        B swivec ; 0x08 Software Interrupt
    pabtvec:
        B pabtvec ; 0x0C Prefetch Abort
    dabtvec:
        B dabtvec ; 0x10 Data Abort
    rsvdvec:
        B rsvdvec ; 0x14 reserved
   
irqvec:
        B IRQ_Handler_Entry ; 0x18 IRQ

    fiqvec: ; 0x1c FIQ
    FIQ_Handler_Entry:
 

    ......略......

    IRQ_Handler_Entry:

    ;- Manage Exception Entry
    ;- Adjust and save LR_irq in IRQ stack
    sub lr, lr, #4
    stmfd sp!, {lr}

    ;- Save SPSR need to be saved for nested interrupt
    mrs r14, SPSR
    stmfd sp!, {r14}

    ;- Save and r0 in IRQ stack
    stmfd sp!, {r0}

    ;- Write in the IVR to support Protect Mode
    ;- No effect in Normal Mode
    ;- De-assert the NIRQ and clear the source in Protect Mode
    ldr r14, =AT91C_BASE_AIC
    ldr r0 , [r14, #AIC_IVR]
    str r14, [r14, #AIC_IVR]

    ;- Enable Interrupt and Switch in Supervisor Mode
    msr CPSR_c, #ARM_MODE_SVC

    ;- Save scratch/used registers and LR in User Stack
    stmfd sp!, { r1-r3, r12, r14}

    ;- Branch to the routine pointed by the AIC_IVR
    mov r14, pc
    bx r0

    ;- Restore scratch/used registers and LR from User Stack
    ldmia sp!, { r1-r3, r12, r14}

    ;- Disable Interrupt and switch back in IRQ mode
    msr CPSR_c, #I_BIT | ARM_MODE_IRQ

    ;- Mark the End of Interrupt on the AIC
    ldr r14, =AT91C_BASE_AIC
    str r14, [r14, #AIC_EOICR]


    ;- Restore R0
    ldmia sp!, {r0}

    ;- Restore SPSR_irq and r0 from IRQ stack
    ldmia sp!, {r14}
    msr SPSR_cxsf, r14

    ;- Restore adjusted LR_irq from IRQ stack directly in the PC
    ldmia sp!, {pc}^
   

    在IRQ中断向量处程序跳到了IRQ_Handler_Entry,IRQ_Handler_Entry所做的可以概括为三点:

     1.保护当前运行环境

     2.调用用户的中断函数(此时用户中断函数地址存储于AIC_IVR)

     3.恢复中断前的环境并返回

     说到此我们再来看一下RealView启动代码中关于IRQ的部分:

     Vectors LDR PC,Reset_Addr
     LDR PC,Undef_Addr
     LDR PC,SWI_Addr
     LDR PC,PAbt_Addr
     LDR PC,DAbt_Addr
     NOP ; Reserved Vector
    
; LDR PC,IRQ_Addr
     LDR PC,[PC,#-0xF20] ; Vector From AIC_IVR
     ; LDR PC,FIQ_Addr
     LDR PC,[PC,#-0xF20] ; Vector From AIC_FVR

       可以看出在IRQ中断向量处直接跳转到了用户的中断处理程序,这么一来那些保护环境与回复环境的任务就必须在用户的中断程序中实现,

    幸运的是RealView提供了一个关键字“__irq”,它可以自动生成这些代码的中断程序,从而使中断处理变得更简单,但这并不意味着在

    RealView中不能象IAR那样实现中断处理程序,如果你愿意完成可以改写启动代码,而不使用“__irq”关键字修饰中断处理程序。

       void isr_xxx_handler(void)  //iar中的中断函数原型

       __irq void isr_xxx_handler(void) //RealView中的中断函数原型

       搞清楚以上关于中断的技巧后本以为示例1的改写工作会很顺利,但还是遇到了一个麻烦,发现中断程序只进入一次,就如定时器停止工作

    一般,于是再次从IAR中断处理启动程序中寻找答案,功夫不负有心人,我终于注意到了下面这一段代码:

    ;- Mark the End of Interrupt on the AIC
    ldr r14, =AT91C_BASE_AIC
    str r14, [r14, #AIC_EOICR]

      原来这并不属于ARM内核,是AT91SAM7S64的中断控制器AIC的一个问题,AIC要求退出中断前要清AIC_EOICR寄存器表示中断完成,所以

    在RealView中断处理函数的最要要加上AIC_EOICR的清零处理。完整的代码如下:

    #include <at91sam7s64.h>

    #define LED_MASK (1<<7)

    #define TC_CLKS_MCK1024 0xC004 //WAVE 模式 1024分频
    #define TIMER0_INTERRUPT_LEVEL 1

    unsigned int g_TimerFlag=0;

    //定时器中断 2Hz
    __irq void timer0_handler(void)
    {
        unsigned int dummy= AT91C_BASE_TC0->TC_SR; //清除中断标记

        if(g_TimerFlag)
        {
            g_TimerFlag=0;
            AT91C_BASE_PIOA->PIO_CODR=LED_MASK;//关LED
        }
        else
        {
            g_TimerFlag=1;
            AT91C_BASE_PIOA->PIO_SODR= LED_MASK;//开LED
        }
   
        AT91C_BASE_AIC->AIC_EOICR=0;//中断结束
    }

    void Timer0Init(void)
    {
        AT91C_BASE_PMC->PMC_PCER= 1<< AT91C_ID_TC0; //定时器0电源打开

        AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS ; //计数禁止
        AT91C_BASE_TC0->TC_IDR = 0xFFFFFFFF ; //中断禁止
        AT91C_BASE_TC0->TC_CMR = TC_CLKS_MCK1024 ; //定时分频1024模式
        AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN ;//计数允许


        AT91C_BASE_AIC->AIC_IDCR = 1 << AT91C_ID_TC0 ; //中断禁止
        AT91C_BASE_AIC->AIC_SVR[AT91C_ID_TC0] = (unsigned long)timer0_handler;//设置中断服务程序
        AT91C_BASE_AIC->AIC_SMR[AT91C_ID_TC0] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | TIMER0_INTERRUPT_LEVEL ;
        AT91C_BASE_AIC->AIC_ICCR = 1 << AT91C_ID_TC0 ;//中断清除
        AT91C_BASE_AIC->AIC_IECR = 1 << AT91C_ID_TC0 ;//中断允许

        AT91C_BASE_TC0->TC_IER = AT91C_TC_CPCS;//IRQ enable CPC
        AT91C_BASE_TC0->TC_RC=22500;//中断频率为2Hz

        AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; //Start timer0

    }

    int main(void)
    {
        AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOA ; //PIOA时钟使能
        AT91C_BASE_PIOA->PIO_PER = LED_MASK; // Set in PIO mode
        AT91C_BASE_PIOA->PIO_OER = LED_MASK; // Configure in Output
        AT91C_BASE_PIOA->PIO_CODR=LED_MASK;//关LED

        Timer0Init();

        while(1);
    }

 

      下载完整的源代码
 

芯艺设计室(http://www.chipart.cn) 2010.08.19  转载请注明出处!
 

芯艺设计室    蒙ICP备06005492号

Copyright© 2004-2011 ChipArt Design House All Rights Reserved