Monday, January 28, 2013

Fast Interrupt Request on LPC2148

In this blog post, we shall be looking at how to use the Fast Interrupt reQuest (FIQ) on the NXP ARM micro-controller LPC2148. If you are new to interrupts on LPC2148, then please go through the previous post which describes the basic Interrupt mechanism and goes in depth into Vectored Interrupt Requests and Non-Vectored Interrupt Requests.

Implementing FIQs using Keil uVision is a little more complicated compared to Vectored and Non-vectored IRQs because the IDE defines a default (dummy) Interrupt Service Routine for FIQs. This can be seen in the startup.s file that is created when a new project is made. In order to replace this with a custom ISR, follow the steps mentioned below.
  1. Configure the Interrupt as FIQ - Set the bit corresponding to the desired interrupt to 1 in VICIntEnable (to enable the interrupt) and VICIntSelect (to configure as FIQ) registers. Also modify the GPIO registers and the concerned peripheral registers accordingly.
  2. Defining the ISR - Make a new function in the main.c file with no arguments and a void return value. This will act as the ISR. Let the function prototype be void FIQ_ISR (void);
  3. Add the ISR to startup.s file - Search for the following portion of the code in startup.s
Vectors         LDR     PC, Reset_Addr         
                LDR     PC, Undef_Addr
                LDR     PC, SWI_Addr
                LDR     PC, PAbt_Addr
                LDR     PC, DAbt_Addr
                NOP                            ; Reserved Vector 
;               LDR     PC, IRQ_Addr
                LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr
                LDR     PC, FIQ_Addr

Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0                      ; Reserved Address 
IRQ_Addr        DCD     IRQ_Handler
FIQ_Addr        DCD     FIQ_Handler

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler
The line FIQ_Addr DCD FIQ_Handler (line 18 above) gives the name of the default address for the FIQ ISR. The following changes need to be made to the file in order to use the custom ISR.
  • Replace the name FIQ_Handler with the name of the ISR in the code. In our case the name is FIQ_ISR.
  • Since the compiler does not know the location of FIQ_ISR during compilation, we need to indicate that it is located in some other file in the project and can be found during linking. we use the IMPORT directive (anywhere before the FIQ_Addr) for this purpose.
The modified file looks as follows:
Vectors         LDR     PC, Reset_Addr         
                LDR     PC, Undef_Addr
                LDR     PC, SWI_Addr
                LDR     PC, PAbt_Addr
                LDR     PC, DAbt_Addr
                NOP                            ; Reserved Vector 
;               LDR     PC, IRQ_Addr
                LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr
                LDR     PC, FIQ_Addr

    IMPORT FIQ_ISR
Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0                      ; Reserved Address 
IRQ_Addr        DCD     IRQ_Handler
FIQ_Addr        DCD     FIQ_ISR

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler 
Note the tab before the IMPORT statement. The compiler gives a syntax error in the absence of the tab.

Save the file, build the code and you are all set to go!

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
}