|
终于在中断中实现了示例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 转载请注明出处!
|