http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3709.htmlWriting interrupt handlersApplies to: Software Development Toolkit (SDT) Description The level of complexity of your 'top-level' interrupt handler depends on whether it needs to call subroutines (with the BL instruction) and whether it needs to be re-entrant. In a typical system, there may be several interrupt sources, with different priorities, connected via IRQ. A re-entrant interrupt handler would allow e.g. a higher priority IRQ to interrupt the processing of a lower-priority IRQ. A re-entrant interrupt handler (at least its 'top-level') *must* be written in assembler. Solution The __irq function handles all these details, for example, __irq void IRQHandler (void) { volatile unsigned int *base = (unsigned int *) 0x80000000; if (*base == 1) // which interrupt was it? { C_int_handler(); // process the interrupt } *(base+1) = *base; // clear the interrupt } compiled with armcc gives: IRQHandler 0x000000: STMFD sp!,{r0-r4,r12,lr} 0x000004: MOV r4,#0x80000000 0x000008: LDR r0,[r4,#0] 0x00000c: CMP r0,#1 0x000010: BLEQ C_int_handler 0x000014: LDR r0,[r4,#0] 0x000018: STR r0,[r4,#4] 0x00001c: LDMFD sp!,{r0-r4,r12,lr} 0x000020: SUBS pc,lr,#4 It is important to clear the source of the interrupt somewhere within the interrupt handler, otherwise the current pending interrupt will be taken again immediately after returning from the interrupt handler, creating an endless loop. This is typically done by accessing an 'interrupt acknowledged' register in the interrupt controller hardware. The IRQHandler C function above must be compiled with armcc, however, C_int_handler() may be a C function compiled for ARM or Thumb. The linker can add any necessary ARM/Thumb interworking veneers to perform the change of state. Re-entrant interrupt handlers, however, are rather more complex. If an interrupt handler re-enables interrupt, and another interrupt occurs during execution of a BL instruction, then the return address of the subroutine (stored in LR_IRQ) WILL BE CORRUPTED when the 2nd IRQ exception is taken. Before the BL to the subroutine/C function, you should switch to another mode, typically System mode (supported in architecture 4 onwards, e.g. in ARM7TDMI) by changing the CPSR, so that the User mode registers are used with privileged protection, then immediately push the User mode LR on the User mode stack. It is then safe to call the subroutine with BL. On return you can restore the User mode LR, switch back to IRQ mode and leave as normal. For more information on System mode see SDT 2.11 User Guide, section 11.13, or SDT 2.50 User Guide, section 9.12. Again, it is important to clear the source of the interrupt before re-enabling interrupts, otherwise the current pending interrupt will be taken again immediately after re-enabling interrupts, creating an endless loop. On entry:
This is best implemented in ARM assembler. Try this: AREA INTERRUPT, CODE, READONLY IMPORT C_irq_handler IRQ SUB lr, lr, #4 STMFD sp!, {lr} ; save adjusted LR_IRQ MRS r14, SPSR STMFD sp!, {r12, r14} ; save workreg & SPSR_IRQ ; add instructions to clear the interrupt here MSR CPSR_c, #0x1F ; go to System mode, IRQ & FIQ enabled STMFD sp!, {r0-r3, lr} ; save LR_USR and non-callee saved registers BL C_irq_handler ; call C irq handler LDMFD sp!, {r0-r3, lr} ; restore MSR CPSR_c, #0x92 ; go back to IRQ mode, disable IRQ (FIQ still enabled) LDMFD sp!, {r12, r14} ; restore workreg & SPSR_IRQ MSR SPSR_cf, r14 LDMFD sp!, {PC}^ ; and return END Again, C_irq_handler() may be
a C function compiled for ARM or Thumb. The linker can add any
necessary ARM/Thumb interworking veneers to perform the change of state. In the above example, please note:
See also Application Note 30: Software Prioritization of Interrupts. Article last edited on: 2008-09-09 15:47:33 |