我想在这里讨论一下有关启动代码中的中断初始化部分,所以无关的部分被删掉了。
GET ..\inc\option.a GET ..\inc\memcfg.a
(省略……)
MACRO $HandlerLabel HANDLER $HandleLabel
$HandlerLabel sub sp,sp,#4 ; decrement sp(to store jump address) stmfd sp!,{r0} ; PUSH the work register to stack(lr does't push because it return to original address) ldr r0,=$HandleLabel; load the address of HandleXXX to r0 ldr r0,[r0] ; load the contents(service routine start address) of HandleXXX str r0,[sp,#4] ; store the contents(ISR) of HandleXXX to stack ldmfd sp!,{r0,pc} ; POP the work register and pc(jump to ISR) MEND 以上是个宏,用于把中断服务程序的首地址装载到pc中,我称它为“加载程序”。 本初始化程序定义了一个数据区(在文件最后),34个字空间,存放相应中断服务程序的首地址。每个字空间都有一个标号,以Handle***命名。 在向量中断模式下使用“加载程序”来执行中断服务程序。
(省略……)
IMPORT Main ; The main entry of mon program
AREA Init,CODE,READONLY
ENTRY b ResetHandler ;for debug b HandlerUndef ;handlerUndef b HandlerSWI ;SWI interrupt handler b HandlerPabort ;handlerPAbort b HandlerDabort ;handlerDAbort b . ;handlerReserved b HandlerIRQ b HandlerFIQ ;***IMPORTANT NOTE*** ;If the H/W vectored interrutp mode is enabled, The above two instructions should ;be changed like below, to work-around with H/W bug of S3C44B0X interrupt controller. ; b HandlerIRQ -> subs pc,lr,#4 ; b HandlerIRQ -> subs pc,lr,#4
VECTOR_BRANCH ldr pc,=HandlerEINT0 ; mGA 0x20 H/W interrupt vector table ldr pc,=HandlerEINT1 ; ldr pc,=HandlerEINT2 ; ldr pc,=HandlerEINT3 ; ldr pc,=HandlerEINT4567 ; ldr pc,=HandlerTICK ; mGA 0x34 b . b . ldr pc,=HandlerZDMA0 ; mGB 0x40 ldr pc,=HandlerZDMA1 ; ldr pc,=HandlerBDMA0 ; ldr pc,=HandlerBDMA1 ; ldr pc,=HandlerWDT ; ldr pc,=HandlerUERR01 ; mGB 0x54 b . b . ldr pc,=HandlerTIMER0 ; mGC 0x60 ldr pc,=HandlerTIMER1 ; ldr pc,=HandlerTIMER2 ; ldr pc,=HandlerTIMER3 ; ldr pc,=HandlerTIMER4 ; ldr pc,=HandlerTIMER5 ; mGC 0x74 b . b . ldr pc,=HandlerURXD0 ; mGD 0x80 ldr pc,=HandlerURXD1 ; ldr pc,=HandlerIIC ; ldr pc,=HandlerSIO ; ldr pc,=HandlerUTXD0 ; ldr pc,=HandlerUTXD1 ; mGD 0x94 b . b . ldr pc,=HandlerRTC ; mGKA 0xa0 b . ; b . ; b . ; b . ; b . ; mGKA b . b . ldr pc,=HandlerADC ; mGKB 0xc0 b . ; b . ; b . ; b . ; b . ;mGKB b . b . ;0xe0=EnterPWDN ldr pc,=EnterPWDN
LTORG
下面是每个中断源的“加载程序” HandlerFIQ HANDLER HandleFIQ HandlerIRQ HANDLER HandleIRQ HandlerUndef HANDLER HandleUndef HandlerSWI HANDLER HandleSWI HandlerDabort HANDLER HandleDabort HandlerPabort HANDLER HandlePabort
HandlerADC HANDLER HandleADC HandlerRTC HANDLER HandleRTC HandlerUTXD1 HANDLER HandleUTXD1 HandlerUTXD0 HANDLER HandleUTXD0 HandlerSIO HANDLER HandleSIO HandlerIIC HANDLER HandleIIC HandlerURXD1 HANDLER HandleURXD1 HandlerURXD0 HANDLER HandleURXD0 HandlerTIMER5 HANDLER HandleTIMER5 HandlerTIMER4 HANDLER HandleTIMER4 HandlerTIMER3 HANDLER HandleTIMER3 HandlerTIMER2 HANDLER HandleTIMER2 HandlerTIMER1 HANDLER HandleTIMER1 HandlerTIMER0 HANDLER HandleTIMER0 HandlerUERR01 HANDLER HandleUERR01 HandlerWDT HANDLER HandleWDT HandlerBDMA1 HANDLER HandleBDMA1 HandlerBDMA0 HANDLER HandleBDMA0 HandlerZDMA1 HANDLER HandleZDMA1 HandlerZDMA0 HANDLER HandleZDMA0 HandlerTICK HANDLER HandleTICK HandlerEINT4567 HANDLER HandleEINT4567 HandlerEINT3 HANDLER HandleEINT3 HandlerEINT2 HANDLER HandleEINT2 HandlerEINT1 HANDLER HandleEINT1 HandlerEINT0 HANDLER HandleEINT0
;One of the following two routines can be used for non-vectored interrupt.
下面这段程序的首地址将要被放到HandleIRQ中,在非向量中断模式下发生IRQ中断时,执行此程序来判断中断源以执行相应的中断服务程序。 IsrIRQ ;using I_ISPR register. sub sp,sp,#4 ;reserved for PC stmfd sp!,{r8-r9}
;IMPORTANT CAUTION ;if I_ISPC isn't used properly, I_ISPR can be 0 in this routine.
ldr r9,=I_ISPR ldr r9,[r9] mov r8,#0x00 movs r9,r9,lsr #1 bcs %F1 add r8,r8,#4 b %B0
1 ldr r9,=HandleADC add r9,r9,r8 ldr r9,[r9] str r9,[sp,#8] ldmfd sp!,{r8-r9,pc}
;**************************************************** ;* START * ;**************************************************** ResetHandler
(省略……)
下面就是把IsrIRQ的首地址装载到HandleIRQ的代码 ;**************************************************** ;* Setup IRQ handler * ;**************************************************** ldr r0,=HandleIRQ ;This routine is needed ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c str r1,[r0]
(省略……)
ALIGN
AREA RamData, DATA, READWRITE
^ (_ISR_STARTADDRESS-0x500)
UserStack # 256 ;c1(c7)ffa00 SVCStack # 256 ;c1(c7)ffb00 UndefStack # 256 ;c1(c7)ffc00 AbortStack # 256 ;c1(c7)ffd00 IRQStack # 256 ;c1(c7)ffe00 FIQStack # 0 ;c1(c7)fff00
^ _ISR_STARTADDRESS HandleReset # 4 HandleUndef # 4 HandleSWI # 4 HandlePabort # 4 HandleDabort # 4 HandleReserved # 4 HandleIRQ # 4 HandleFIQ # 4
;Don't use the label 'IntVectorTable', ;because armasm.exe cann't recognize this label correctly. ;the value is different with an address you think it may be. ;IntVectorTable HandleADC # 4 HandleRTC # 4 HandleUTXD1 # 4 HandleUTXD0 # 4 HandleSIO # 4 HandleIIC # 4 HandleURXD1 # 4 HandleURXD0 # 4 HandleTIMER5 # 4 HandleTIMER4 # 4 HandleTIMER3 # 4 HandleTIMER2 # 4 HandleTIMER1 # 4 HandleTIMER0 # 4 HandleUERR01 # 4 HandleWDT # 4 HandleBDMA1 # 4 HandleBDMA0 # 4 HandleZDMA1 # 4 HandleZDMA0 # 4 HandleTICK # 4 HandleEINT4567 # 4 HandleEINT3 # 4 HandleEINT2 # 4 HandleEINT1 # 4 HandleEINT0 # 4 ;0xc1(c7)fff84
END
本程序的初始化中断部分实现的很巧妙,基于34个字单元将向量中断和非向量中断的实现结合在一起。 向量中断直接使用“加载程序”,把相应的中断服务程序首地址(存放于Handle***)加载到PC。 非向量中断通过执行IsrIRQ判断中断源,并同时计算出相应Handle***的地址,再将此地址的内容加载到PC。
我有两个问题: 问题一,非向量中断有个缺点,它始终从优先级最低的中断源开始识别,且是通过过分析I_ISPR寄存器的每一位来识别,但是尽管有多个中断同时发生,I_ISPR只有一位置1。如此一来,频繁发生低优先级中断是否会屏蔽其他中断? 我有过一次试验,设置两个中断INT_TICK和INT_TIMER5,两个中断服务程序都使用串口打印一串字符。当TIMER5以频率为1KHz(每毫秒一次)中断时,TICK中断服务程序毫无反应。
问题二,本代码中有这样一段 b HandlerIRQ b HandlerFIQ ;***IMPORTANT NOTE*** ;If the H/W vectored interrutp mode is enabled, The above two instructions should ;be changed like below, to work-around with H/W bug of S3C44B0X interrupt controller. ; b HandlerIRQ -> subs pc,lr,#4 ; b HandlerIRQ -> subs pc,lr,#4 大意是如果使用向量中断模式,必须用subs pc,lr,#4代替b HandlerIRQ。 首先我不太确定发生向量中断时,CPU是否执行b HandlerIRQ,还是直接转向相应中断源的向量地址。如果不执行b HandlerIRQ,那么subs pc,lr,#4有何意义?如果执行b HandlerIRQ,那subs pc,lr,#4岂不是又使CPU从IRQ模式转换回SVC模式,继续执行被中断了的代码,这又是何意义? 另外,我按照他的方法试过,设置INTCON为向量中断,但一运行系统就重起,改回非向量中断一切正常。