0% found this document useful (0 votes)
8 views

Interrupts (2)

This document is a tutorial on programming interrupts for LPC2148 microcontrollers, explaining the concept of interrupts, their types, and how to handle them using the Vectored Interrupt Controller (VIC). It details the registers involved in managing interrupts, the process of configuring interrupt service routines (ISRs), and provides examples of coding ISRs for various scenarios. The tutorial emphasizes the distinction between vectored and non-vectored interrupts and outlines the steps required to set up and manage these interrupts effectively.

Uploaded by

tekeba lake
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

Interrupts (2)

This document is a tutorial on programming interrupts for LPC2148 microcontrollers, explaining the concept of interrupts, their types, and how to handle them using the Vectored Interrupt Controller (VIC). It details the registers involved in managing interrupts, the process of configuring interrupt service routines (ISRs), and provides examples of coding ISRs for various scenarios. The tutorial emphasizes the distinction between vectored and non-vectored interrupts and outlines the steps required to set up and manage these interrupts effectively.

Uploaded by

tekeba lake
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

Introduction to Interrupts

This is a Basic Tutorial on Interrupts for LPC2148 MCUs and how to program them.

“An interrupt is a signal sent to the CPU which indicates that a system event has occurred
which needs immediate attention“. An ‘Interrupt Request‘ i.e an ‘IRQ‘ can be thought of as
a special request to the CPU to execute a function(small piece of code) when an interrupt occurs.
This function or ‘small piece of code’ is technically called an ‘Interrupt Service Routine‘ or
‘ISR‘. So when an IRQ arrives to the CPU , it stops executing the current code and start
executing the ISR. After the ISR execution has finished the CPU gets back to where it had
stopped.

Interrupts in LPC2148 are handled by Vectored Interrupt Controller (VIC) and can be
classified into 2 types based on the priority levels.

1. Fast IRQs or FIQs : which has highest priority

2. Normal IRQs or IRQs which can be further classified as : Vectored IRQ and Non-
Vectored IRQ.
In computing the term ‘Vectored‘ means that the CPU is aware of the address of the ISR when
the interrupt occurs and Non-Vectored means that CPU doesn’t know the address of the ISR nor
the source of the IRQ when the interrupt occurs and it needs to be supplied by the ISR address.
For the Vectored, the System internally maintains a table called IVT or Interrupt Vector
Table which contains the information about Interrupts sources and their corresponding ISR
address. In LPC214x this is facilitated by a register called ‘VICDefVectAddr‘. The user must
assign the address of the default ISR to this register for handling Non-Vectored IRQs.

Note : VIC , as per its design , can take 32 interrupt request inputs but only 16 requests can be
assigned to Vectored IRQ interrupts in its LCP2148 Implementation. We are given a set of 16
vectored IRQ slots to which we can assign any of the 22 requests that are available in LPC2148.
The slot numbering goes from 0 to 15 with slot no. 0 having highest priority and slot no. 15
having lowest priority.

VIC has plenty of registers. Most of the registers that are used to configure interrupts or read
status have each bit corresponding to a particular interrupt source and this mapping is same for
all of these registers.
Here is the complete table which says which bit corresponds to which interrupt source as given
in Datasheet:
Table 1:

Bit 22 21 20 19 18 17 16 15 14 13 12 11
#

IR US AD BOD I2C1 AD0 EINT EINT EINT1 EINT0 RT PLL SPI1/SS


Q B 1 3 2 C P

Bit 10 9 8 7 6 5 4 3 2 1 0
#

IR SPI I2C PW UART UART TIMR TIMR ARMC ARMC N/A WD


Q 0 0 M 1 0 1 0 1 0 T

Note : TIMR0 = TIMER0 , TIMR1 = TIMER1 , ARMC1 = ARMCore1 , ARMC2 = ARMCore2.

LPC2148 Interrupt Related Registers


Now we will have a look at some of the important Registers that are used to implement interrupts
in lpc214x:

1) VICIntSelect (R/W) :
This register is used to select an interrupt as IRQ or as FIQ. Writing a 0 at a given bit location(as
given in Table 1) will make the corresponding interrupt as IRQ and writing a 1 will make it FIQ.
For e.g if you make Bit 4 as 0 then the corresponding interrupt source i.e TIMER0 will be IRQ
else if you make Bit 4 as 1 it will be FIQ instead. Note than by default all interrupts are selected
as IRQ. Note that here IRQ applies for both Vectored and Non-Vectored IRQs. [Refer Table 1]

2) VICIntEnable (R/W) :
This is used to enable interrupts. Writing a 1 at a given bit location will make the corresponding
interrupt Enabled. If this register is read then 1’s will indicated enabled interrupts and 0’s as
disabled interrupts. Writing 0’s has no effect. [Refer Table 1]

3) VICIntEnClr (R/W) :
This register is used to disable interrupts. This is similar to VICIntEnable expect writing a 1 here
will disabled the corresponding Interrupt. This has an effect on VICIntEnable since writing at bit
given location will clear the corresponding bit in the VICIntEnable Register. Writing 0’s has no
effect. [Refer Table 1]

4) VICIRQStatus (R) :
This register is used for reading the current status of the enabled IRQ interrupts. If a bit location
is read as 1 then it means that the corresponding interrupt is enabled and active. Reading a 0 is
unless here lol.. [Refer Table 1]

5) VICFIQStatus (R) :
Same as VICIRQStatus except it applies for FIQ. [Refer Table 1]

6) VICSoftInt :
This register is used to generate interrupts using software i.e manually generating interrupts
using code i.e the program itself. If you write a 1 at any bit location then the correspoding
interrupt is triggered i.e. it forces the interrupt to occur. Writing 0 here has no effect. [Refer Table
1]

7) VICSoftIntClear :
This register is used to clear the interrupt request that was triggered(forced) using VICSoftInt.
Writing a 1 will release(or clear) the forcing of the corresponding interrupt. [Refer Table 1]

8) VICVectCntl0 to VICVectCntl15 (16 registers in all) :


These are the Vector Control registers. These are used to assign a particular interrupt source to a
particular slot. As mentioned before slot 0 i.e VICVectCntl0 has highest priority and
VICVectCntl15 has the lowest. Each of this registers can be divided into 3 parts : {Bit0 to bit4} ,
{Bit 5} , {and rest of the bits}.

The first 5 bits i.e Bit 0 to Bit 4 contain the number of the interrupt request which is assigned to
this slot. The interrupt source numbers are given in the table below :
Table 2:

Interrupt Source Source number Interrupt Source Source number


In Decimal In Decimal

WDT 0 PLL 12

N/A 1 RTC 13

ARMCore0 2 EINT0 14

ARMCore1 3 EINT1 15

TIMER0 4 EINT2 16

TIMER1 5 EINT3 17

UART0 6 ADC0 18

UART1 7 I2C1 19

PWM 8 BOD 20

I2C0 9 ADC1 21

SPI0 10 USB 22

SPI1 11

The 5th bit is used to enable the vectored IRQ slot by writing a 1.

Note that if the vectored IRQ slot is disabled it will not disable the interrupt but will change the
corresponding interrupt to Non-Vectored IRQ. Enabling the slot here means that it can generate
the address of the ‘dedicated Interrupt handling function (ISR)’ and disabling it will generate the
address of the ‘common/default Interrupt handling function (ISR)’ which is for Non-Vectored
ISR. In simple words if the slot is enabled it points to ‘specific and dedicated interrupt handling
function’ and if its disable it will point to the ‘default function’. This will get more clear as we do
some examples.
Note : The Interrupt Source Number is also called as VIC Channel Mask.

The rest of the bits are reserved.

9) VICVectAddr0 to VICVectAddr15 (16 registers in all) :


For Vectored IRQs these register store the address of the function that must be called when an
interrupt occurs. Note – If you assign slot 3 for TIMER0 IRQ then care must be taken that you
assign the address of the interrupt function to corresponding address register .. i.e VICVectAddr3
in this example.

10) VICVectAddr :
This must not be confused with the above set of 16 VICVecAddrX registers. When an interrupt
is Triggered this register holds the address of the associated ISR i.e the one which is currently
active. Writing a value i.e dummy write to this register indicates to the VIC that current Interrupt
has finished execution. In this tutorial the only place we’ll use this register .. is at the end of the
ISR to signal end of ISR execution.

11) VICDefVectAddr :
This register stores the address of the “default/common” ISR that must be called when a Non-
Vectored IRQ occurs.

Configuring and Programming Interrupts (ISR)


To explain How to configure Interrupts and define ISRs I’ll use Timers as an example.

First lets see how to define the ISR so that compiler handles it carefully. For this we need to
explicitly tell the compiler that the function is not a normal function but an ISR. For this we’ll
use a special keyword called “__irq” which is a function qualifier. If you use this keyword with
the function definition then compiler will automatically treat it as an ISR. Here is an example on
how to define an ISR in Keil :

__irq void myISR (void)

{ ...}

//====OR Equivalently====
void myISR (void) __irq

{ ...}

Now lets see how to actually setup the interrupt.

A Simple 3 Step Process to Setup / Enable a Vectored IRQ

1. First we need to enable the TIMER0 IRQ itself! Hence , from Table 1 we get the bit
number to Enable TIMER0 Interrupt which is Bit number 4. Hence we must make bit 4
in VICIntEnable to ‘1’.

2. Next , from Table 2 we get the interrupt source number for TIMER0 which is decimal 4
and OR it with (1<<5) [i.e 5th bit=1 which enables the slot] and assign it to
VICVectCntlX.

3. Next assign the address of the related ISR to VICVectAddrX.


Here is a simple Template to do it:
Replace X by the slot number you want .., then Replace Y by the Interrupt Source Number as
given in Table 2 and finally replace myISR with your own ISR’s function Name .. and you’r
Done!

VICVectAddrX = (unsigned) myISR;

VICVectCntlX = (1<<5) | Y ;

VICIntEnable |= (1<<Y) ;

Using above steps we can Assign TIMER0 Interrupt to Slot number say.. 0 as follows :

VICVectAddr0 = (unsigned) myISR;

VICVectCntl0 = (1<<5) | 4 ; //5th bit must 1 to enable the slot (see the register definition above)

VICIntEnable |= (1<<4) ; // Enable TIMER0 IRQ//Vectored-IRQ for TIMER0 has been


configured

Note the Correspondence between the Bit number in Table 1 and the Source Number in Table 2.
The Bit Number now becomes the Source Number in decimal.
How to write the code for the ISR.

First thing to note here is that each device(or block) in lpc214x has only 1 IRQ associated with
it. But inside each device there may be different sources which can raise an interrupt (or an
IRQ). Like the TIMER0 block(or device) has 4 match + 4 capture registers and any one or
more of them can be configured to trigger an interrupt. Hence such devices have a dedicated
interrupt register which contains a flag bit for each of these source(For Timer block its
‘T0IR‘). So , when the ISR is called first we need to identify the actual source of the interrupt
using the Interrupt Register and then proceed accordingly. Also just before , when the main ISR
code is finished we also need to acknowledge or signal that the ISR has finished executing for
the current IRQ which triggered it. This is done by clearing the the flag(i.e the particular bit) in
the device’s interrupt register and then by writing a zero to VICVectAddr register which
signifies that interrupt has ISR has finished execution successfully.
Programming the Interrupt Service Routine (ISR) :
lets consider two simple cases for coding an ISR

 Case #1) First when we have only one ‘internal’ source of interrupt in UART0 i.e an
U0IIR&0x02 match event which raises an IRQ.

 Case #2) And when we have multiple ‘internal’ source of interrupt in UART0 i.e. say a
match event for [011] for Receive Line Status(RLS) , [010] for Receive Data
Available(RDA) , [001] for THRE Interrupt. which raise an IRQ.
In case #1 things are pretty straight forward. Since we know only one source is triggering an
interrupt we don’t need to identify it – though its a good practice to explicitly identify it. The ISR
then will be something like :

__irq void myISR(void)

{ long int regVal;

regVal = U0IIR; // read the current value in UART0's Interrupt Register and clear the
interrupt flag

//... MR0 match event has occured .. do something here

VICVectAddr = 0x0; // The ISR has finished!

}
Even in case #2 things are simple except we need to identify the ‘actual’ source of interrupt.

#define RLS_FLAG (1<<1)

#define RDA_FLAG (1<<2)

#define THRE_FLAG (1<<4)

__irq void myISR(void)

{ long int regVal;

regVal = U0IIR; // read the current value in T0's Interrupt Register

if( U0IIR & RLS_FLAG )

{//do something for MR0 match

else if ( U0IIR & RDA_FLAG )

{ //do something for MR1 match

else if ( U0IIR & THRE_FLAG )

{//do something for MR2 match

VICVectAddr = 0x0; // Acknowledge that ISR has finished execution

So , Now its time to go one step further and pop-out Case #3 and Case #4. Both of them deal
with IRQs from different blocks.

 Case #3) When we have Multiple Vectored IRQs from different Devices. Hence Priority
comes into picture here.

 Case #4) Lastly when we have Multiple Non-Vectored IRQs from different Devices.
For Case #3 , Consider we have TIMER0 and UART0 generating interrupts with TIMER0
having higher priority. So in this case we’ll need to write 2 different Vectored ISRs – one for
TIMER0 and one for UART0. To keep things simple lets assume that we have only 1 internal
source inside both TIMER0 and UART0 which generates an interrupt. The ISRs will be
something as given below :

__irq void myTimer0_ISR(void)

long int regVal;

regVal = T0IR; // read the current value in T0's Interrupt Register

//... MR0 match event has occured .. do something here

T0IR = regval; // write back to clear the interrupt flag

VICVectAddr = 0x0; // The ISR has finished!

__irq void myUart0_ISR(void)

{ long int regVal;

regVal = U0IIR; // read the current value in U0's Interrupt Register which also clears it!

//Something inside UART0 has raised an IRQ

VICVectAddr = 0x0; // The ISR has finished!

For Case #4 too we have TIMER0 and UART0 generating interrupts. But here both of them are
Non-Vectored and hence will be serviced by a common Non-Vectored ISR. Hence, here we will
need to check the actual source i.e device which triggered the interrupt and proceed accordingly.
This is quite similar to Case #2. The default ISR in this case will be something like :
__irq void myDefault_ISR(void)

{ long int T0RegVal , U0RegVal;

T0RegVal = T0IR; // read the current value in T0's Interrupt Register

U0RegVal = U0IIR; // read the current value in U0's(Uart 0) Interrupt Identification


Register

if( T0IR )

{ //do something for TIMER0 Interrupt

T0IR = T0RegVal; // write back to clear the interrupt flag for T0

if( ! (U0RegVal & 0x1) )

{//do something for UART0 Interrupt

//No need to write back to U0IIR since reading it clears it

VICVectAddr = 0x0; // The ISR has finished!

Note than UART0’s Interrupt Register is a lot different than TIMER0’s. The first Bit in U0IIR
indicates whether any interrupt is pending or not and its Active LOW! The next 3 bits give the
Identification for any of the 4 Interrupts if enabled.
In case of FIQ
To covert a Vectored IRQ to FIQ just make the bit for corresponding IRQ
in VICIntSelect register to 1 and it will be become an FIQ. [Refer Table 1] Also Note that its
recommended that you only have one FIQ in your system. FIQs have low latency than VIRQs
and usually used in System Critical Interrupt Handling.

Example for Case #2:


This example uses 3 Match Registers i.e 3 Interrupts sources within TIMER0 itself. Each Match
register Interrupt is used to Turn on and off an LED which is connected to a particular GPIO Pin.
Here we have 3 LEDs connected to PIN0 , PIN1 and PIN2 of PORT0 of LPC2148. MR0 is used
for PIN0 i.e first LED , similarly MR2 and MR3 for PIN1 and PIN2 i.e for second and thrid LED
respectively. MR0 has been configured to Trigger an Interrupt when 500ms have elapsed after
TC is reset. MR1 has been configured to Trigger an Interrupt when 1000ms have elapsed after
TC is reset. MR2 has been configured to Trigger an Interrupt when 1500ms have elapsed at it
Resets the TC so the cycle starts again. Given a period of 1.5 seconds i.e 1500ms here is what
happens :

1) MR0 Toggles PIN0 of PORT0 i.e P0.0 at 500ms


2) MR1 Toggles PIN1 of PORT0 i.e P0.1 at 1000ms
3) MR2 Toggles PIN2 of PORT0 i.e P0.2 at 1500ms and also Resets the TC
*) This cycle of Toggling each of the LEDs one by one keeps on repeating…

/*(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-13.

More Embedded tutorials @ www.ocfreaks.com/cat/embedded

LPC2148 Interrupt Example.

License : GPL.

*/

#include <lpc214x.h>

#define PLOCK 0x00000400

#define MR0I (1<<0) //Interrupt When TC matches MR0

#define MR1I (1<<3) //Interrupt When TC matches MR1

#define MR2I (1<<6) //Interrupt When TC matches MR2

#define MR2R (1<<7) //Reset TC when TC matches MR2

#define MR0I_FLAG (1<<0) //Interrupt Flag for MR0

#define MR1I_FLAG (1<<1) //Interrupt Flag for MR1

#define MR2I_FLAG (1<<2) //Interrupt Flag for MR2

#define MR0_DELAY_MS 500 //0.5 Second(s) Delay

#define MR1_DELAY_MS 1000 //1 Second Delay

#define MR2_DELAY_MS 1500 //1.5 Second(s) Delay


#define PRESCALE 60000 //60000 PCLK clock cycles to increment TC by 1

void delayMS(unsigned int milliseconds);

void initClocks(void);

void initTimer0(void);

__irq void myTimer0_ISR(void);

void setupPLL0(void);

void feedSeq(void);

void connectPLL0(void);

int main(void)

initClocks(); //Initialize CPU and Peripheral Clocks @ 60Mhz

initTimer0(); //Initialize Timer0

IO0DIR = 0xFFFFFFFF; //Configure all pins on Port 0 as Output

IO0PIN = 0x0;

T0TCR = 0x01; //Enable timer

while(1); //Infinite Idle Loop

//return 0; //normally this wont execute ever :P

void initTimer0(void)

{
/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/

//----------Configure Timer0-------------

T0CTCR = 0x0;

T0PR = PRESCALE-1; //60000 clock cycles @60Mhz = 1 mS

T0MR0 = MR0_DELAY_MS-1; // 0.5sec (Value in Decimal!) Zero Indexed Count -


hence subtracting 1

T0MR1 = MR1_DELAY_MS-1; // 1sec

T0MR2 = MR2_DELAY_MS-1; // 1.5secs

T0MCR = MR0I | MR1I | MR2I | MR2R; //Set the Match control register

//----------Setup Timer0 Interrupt-------------

//I've just randomly picked-up slot 4

VICVectAddr4 = (unsigned)myTimer0_ISR; //Pointer Interrupt Function (ISR)

VICVectCntl4 = 0x20 | 4;

VICIntEnable = 0x10; //Enable timer0 int

T0TCR = 0x02; //Reset Timer

__irq void myTimer0_ISR(void)

long int regVal;

regVal = T0IR; // read the current value in T0's Interrupt Register

if( T0IR & MR0I_FLAG )

{
IO0PIN ^= (1<<0); // Toggle GPIO0 PIN0 .. P0.0

else if ( T0IR & MR1I_FLAG )

IO0PIN ^= (1<<1);// Toggle GPIO0 PIN1 .. P0.1

else if ( T0IR & MR2I_FLAG )

IO0PIN ^= (1<<2);// Toggle GPIO0 PIN2 .. P0.2

T0IR = regVal; // write back to clear the interrupt flag

VICVectAddr = 0x0; // Acknowledge that ISR has finished execution

Note: TC is reset only when MR2 matches TC. Though this can be used using simple delay
but the example presented here does this by purely using Interrupts!

You might also like