Microcontrollers aren’t imaginable without interrupts. The arm isn’t an exception. There were SWI exceptions mentioned in earlier articles, but there are two more sources of exceptions: IRQ(General Purpose Interrupt) and FIQ(Fast Interrupt).
ARM Pin Connect Block
All I/O pins of LPC2000 ARM can be multiplexed to several functions via pin select block. Pin selection bloc allows selections up to three more other functions except for GPIO. Pin Connect block gives flexibility to ARM MCU because each PIN can have different functionality. After reset, all pins are configured as GPIO.
As the example above, you can see that the P.1 pin function can be assigned by PINSEL0 register 3 and 2-bit configurations. So if you write PINSEL0|=(1<<3)|(1<<2), then the pin will be assigned to the EINT0 function. Pretty simple. So before using External interrupt EINT0, first, you have to select the pin function for the P0.1 pin.
After pins are set up, then selecting Interrupt mode For external interrupt mode selection, an EXMODE register is used to select whether interruptions occur on level change or on rising or falling edges. EXPOL register is used to select the polarity of the interrupt signal that triggers an interrupt.
Interrupt structure of LPC
LPC21xx microcontrollers have four external interrupt lines. Each of them can be connected to FIQ or IRQ mode. Generally, there should be one interrupt that is connected to FIQ mode. So if one interrupt is connected to FIQ, then logically other has to be IRQ. To manage IRQ’s easier, there were Vector Interrupt Controller (VIC) added. Vectored interrupt controller allows the system optimally to interrupt without any additional checking the sources of interrupts that would take more cycles.
Generally, VIC takes 32 interrupt request inputs (LPC21xx), and programmable assigns them to 3 categories: FIQ, vectored IRQ, and non-vectored IRQ. Each Interrupt there can be assigned a priority that can be adjusted programmable.
Logic says that FIQ interrupts have the highest priority. If more than one interrupt is assigned to FIQ, the VIC has to ask what adds additional latency. So best results are achieved when one interrupt is assigned to FIG.
Vectored IRQ has middle priority. Only 16 of any from 32 interrupt requests can be assigned to vectored IRQ mode. Slot number from 0 to 15 defines vectored IRQ priority.
Non-vectored interrupts have the lowest priority.
Generally speaking, when one or more interrupts occur, VIC logic OR’s one interrupts and gives the highest priority interrupt service routine address to the ARM processor. Otherwise, VIC provides a default routine address to check another VOC register to see if there are other IRQ active.
FIQ and vectored IRQ interrupt setup example
Let’s say we want to set up EINT1 as FIQ interrupt and EINT0 as vectored IRQ.
void setupInterrupts(void)
{
PINSEL0=0x000000CC; //P0.1 as EINT0 P0.3 as EINT1
VICIntSelect=0x00008000; //Enable VIC channel as FIQ for EINT1
VICVectCnt0=0x0000002E; //bit 5 enables slot (14 for EINT0) for EINT0
/*
Each VIC control register consists of two fields: a channel field (0:4 bits) and enables bit (5). This way each channel can be connected to any given slot.
*/
VICVectAddr0=(unsigned long)EINT0_routine;// Pass address of IRQ in to VIC slot
/*
VICVectAdd register should contain an address of C function to run when interrupt occure.
*/
VICIntSelect=0x0000C000; //Enable EINT1 and EINT0 interrupts in VIC
}
Using ARM GCC toolset IRQ and FIQ ISR can be defined as follows:
void __attribute__ ((interrupt(“IRQ”))) EINT0_routine(void)
{
EXTINT=0x00000001; //Clear EINT0 interrupt flag
VICVectAddr=0; //Dummy write to signal end of interrupt
}
void __attribute__ ((interrupt(“FIQ”))) EINT1_routine(void)
{
EXTINT=0x00000002; //Clear EINT2 interrupt flag
}
VIC can handle 16 vectored interrupts and at least one FIQ. But there are more than 17 interrupt sources on the MCU. Extra interrupt sources can be services as non-vectored interrupts. A single ISR serves Non-vectored interrupts. The address of this ISR are stored in an additional vector address register. If an interrupt occurs and is not assigned to FIQ or vectored IRQ, it will act as a non-vectored interrupt. When a non-vectored interrupt occurs default ISR address is loaded to the CPU. This ISR has to check by itself what type of interrupt occurred. For instance:
void setInt(void)
{
PINSEL0=0x20000000; /enable EINT1;
VICDefVectAddr=(unsigned long)NonVectored; // ISR address to VIC slot
VICIntEnable=0x00008000; //Enable EINT in the VIC
}
Then ISR may look like:
void __attribute__ ((interrupt(“IRQ”))) NonVectored(void)
{
if(VICIRQStatus&&0x00008000) //test for interrupt source
{
EXTINT=0x00000002; //Clear EINT0 interrupt flag
}
VICVectAddr=0; //Dummy write to signal end of interrupt
}
Using VIC, you get another benefit – the ability to generate software interrupts on any given channel(not the same as SWI). This is handy for simulation during development. Also, VIC has a protected mode when USER cannot access VIC registers. If the application code wants to access VIC, it must enter privileged mode, usually in FIQ or IRQ or even by SWI instruction.
As VIC doesn’t support nested interrupts, we need to execute some code to enable nested interrupts. First o all, when entering ISR, we should preserve SPSR_irq by placing it in the stack, then Enable IRQ, and then the Link register has to be stored in stack. This way area for nested IRQ is prepared. When exiting from IRQ all has to be restored in a reverse way. These operations are handy to have stored in macros.