Tuesday, January 1, 2013

Interrupts on LPC2148

LPC2148 is an ARM7 series micro-controller. It has a Vectored Interrupt Controller (VIC) which takes the 32 interrupt requests and assigns them into the following three categories programmably:
  1. Fast Interrupt Request (FIQ) - This is the highest priority interrupt request and usually only one request is assigned to FIQ
  2. Vectored IRQs - These have middle priority and 16 of the 32 requests can be assigned to this category. They are called Vectored IRQs because each request is a vector of the Interrupt Flag and the address of the Interrupt Service Routine (ISR), which is the piece of code to be executed when the interrupt request is triggered.
  3. Non-vectored IRQs - These have the lowest priority. Unlike the vectored IRQs, all the non-vectored IRQs share a default ISR. Hence the name non-vectored.
Having known all this, let us see how we can code it in C using the Keil uVision IDE. We shall be using only Vectored and Non-vectored IRQs in this post. The next post shall describe how to use FIQ.

The following are the registers that would be used for interrupt requests:
  • VICIntEnable - This register specifies which of the interrupt requests contribute to FIQ and IRQ. If a bit is set to 1, then the corresponding interrupt is enabled.

  • VICIntSelect - This register classifies each request contributing to FIQ or IRQ. It follows the same bit pattern as VICIntEnable. If a bit is set to 1, the corresponding request is assigned to FIQ. Since we aren't using FIQs for now, we shall set this register to 0.
  • VICVectCntl0-15 - Each of these Control Registers controls one of the 16 Vectored IRQs. Slot 0 has the highest priority and slot 16 has the least.

  • VICVectAddr0-15 - These registers hold the address of the ISRs for the 16 Vectored IRQs in the same order as the Control Registers. In a C code, the address is passed as a function pointer.
  • VICDefVectAddr - This register holds the address of the ISR for the Non-vectored IRQs
  • VICVectAddr - When an IRQ interrupt occurs, the address of the corresponding ISR is loaded into this register. Clearing this register (writing a 0 to it) at the end of the ISR resets the priority hardware.

Example
Suppose we wish to use External Interrupt 0 as a Vectored IRQ and External Interrupt 1 as a Non-vectored IRQ, then the code would look as follows:
#include <lpc214x.h>

void handler_Ext0 (void) __irq;
void handler_def (void) __irq;

int main(void)
{
 PINSEL0 |= 1 << 29;                       // Configure P0.14 (EINT1) to receive External Interrupt;
 PINSEL1 |= 1 << 0;                       // Configure P0.16 (EINT0) to receive External Interrupt;

 VICIntEnable = (1 << 14) | (1 << 15);   // Enable EINT0 and EINT1
 VICIntSelect = 0;                                   // None of the enabled interrupt requests is assigned to FIQ
 VICVectCntl0 = 0x2E;                      // 0xE = 14 (Number of the EINT0) - Assigns EINT0 to Vectored IRQ with highest priority
 VICVectAddr0 = (unsigned long) handler_Ext0;    // Specifies the address of the ISR of EINT0 as a kind of function pointer
 VICDefVectAddr = (unsigned long) handler_def;    // Specifies the address of the ISR of EINT1 (Non-vectored IRQ) as a kind of function pointer

 while(1);
}

void handler_Ext0 (void) __irq
{
        // Write code for EINT0 ISR here

 EXTINT |= 1 << 0;       // Clear the EINT0 flag
 VICVectAddr = 0;        // Write to VICVectAddr to update the priority hardware
}

void handler_def (void) __irq
{
        // Write code for EINT1 ISR here

 EXTINT |= 1 << 1;       // Clear the EINT1 flag
 VICVectAddr = 0;        // Write to VICVectAddr to update the priority hardware
}

3 comments:

  1. why is __ irq written?

    and also, could u please explain the order in which the statements and ISR would execute if we use ADC as an interrupt?

    ReplyDelete
    Replies
    1. Thanks abdo!

      Hi Kalyani, Apologies for missing your comment. Assuming you want to set ADC as a Vectored Interrupt, you would need to follow the following steps (assuming AD0 interrupt):
      1. Set bit 18 of VICIntEnable to 1
      2. Assign it to one of the VIC slots, i.e., by setting one of VICVectCntl0 to 15 to 0x32 (or (1<<5)|(18))
      3. Assign the corresponding ISR function pointer to the VICVectAddr0-15 (same slot as VICVectCntl)

      The order of execution depends on the priority. Slot 0 has the highest priority among Vectored Interrupts. If you do not have an FIQ configured, then setting AD0 to VICVectCntl0 will ensure that the ISR is serviced before all others in queue.

      Delete
  2. __irq is written in order to indicate to the compiler that the function is an interrupt handler so the return to the user context will be secured

    ReplyDelete