| |||
| Home > Handling Processor Exceptions > Interrupt handlers > Simple interrupt handlers in C | |||
You can write simple C interrupt handlers by using the __irq function
declaration keyword. You can use the __irq keyword
both for simple one-level interrupt handlers, and interrupt handlers
that call subroutines. However, you cannot use the __irq keyword for reentrant interrupt
handlers, because it does not cause the SPSR to
be saved or restored. In this context, reentrant means that the
handler re-enables interrupts, and can itself be interrupted. See Reentrant interrupt handlers for more information.
The __irq keyword:
preserves all AAPCS corruptible registers
preserves all other registers used by the function, other than any NEON/VFP extension registers
exits the function by setting the PC to lr-4 and
restoring the CPSR to its original value.
If the function calls a subroutine, __irq preserves
the link register for the interrupt mode in addition to preserving
the other corruptible registers. See Calling subroutines from interrupt
handlers for more information.
C interrupt handlers cannot be produced in this way when compiling
Thumb-1 C code. When compiling for Thumb, any functions specified
as __irq are compiled for ARM.
However, the subroutine called by an __irq function
can be compiled for Thumb-1, with interworking enabled. See Chapter 4 Interworking ARM and Thumb for more information
on interworking.
This restriction does not apply to processors that support Thumb-2.
If you call subroutines from your top-level interrupt handler,
the __irq keyword also restores the value of lr_IRQ from
the stack so that it can be used by a SUBS instruction
to return to the correct address after the interrupt has been handled.
Example 6.14 shows how
this works. The top level interrupt handler reads the value of a
memory-mapped interrupt controller base address at 0x80000000.
If the value of the address is 1, the top-level handler branches
to a handler written in C.
Example 6.14.
__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) = 0; // clear the interrupt
}
Compiled with armcc, Example 6.14 produces the following code:
IRQHandler PROC
STMFD sp!,{r0-r4,r12,lr}
MOV r4,#0x80000000
LDR r0,[r4,#0]
SUB sp,sp,#4
CMP r0,#1
BLEQ C_int_handler
MOV r0,#0
STR r0,[r4,#4]
ADD sp,sp,#4
LDMFD sp!,{r0-r4,r12,lr}
SUBS pc,lr,#4
ENDP
Compare this with the result when the __irq keyword
is not used:
IRQHandler PROC
STMFD sp!,{r4,lr}
MOV r4,#0x80000000
LDR r0,[r4,#0]
CMP r0,#1
BLEQ C_int_handler
MOV r0,#0
STR r0,[r4,#4]
LDMFD sp!,{r4,pc}
ENDP