Download as pdf or txt
Download as pdf or txt
You are on page 1of 13

Community

Register

News

Contact Us

Reviews

Guides & Tutorials

More

HOME EMBEDDED LPC2148 INTERRUPT TUTORIAL

FOLLOW US

LPC2148 Interrupt Tutorial

Search ...

FACEBOOK

OCFreaks.com
1.7Klikes

LikePage

Bethefirstofyourfriendstolikethis

Email Address

Subscribe

Introduction to Interrupts
This is a Basic Tutorial on Interrupts for LPC214x MCUs and how to program them for those who are
new to interrupts. To start with , rst lets see : what interrupts, IRQs and ISRs are. As per wiki : An
interrupt is a signal sent to the CPU which indicates that a system event has a 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 code current code and start executing the ISR. After the ISR execution has
nished the CPU gets back to where it had stopped.
Interrupts in LPC214x are handled by Vectored Interrupt Controller(VIC) and are classied into 3 types
based on the priority levels.(Though Interrupts can classied in many dierent ways as well)
1. Fast Interrupt Request i.e FIQ : which has highest priority
2. Vectored Interrupt Request i.e Vectored IRQ : which has middle or priority between FIQ and
Non-Vectored IRQ.
3. Non-Vectored IRQ : which has the lowest priority.

But Id like to Classify them as 2 types :


1. Fast IRQs or FIQs
2. Normal IRQs or IRQs which can be further classied as : Vectored IRQ and Non-Vectored IRQ.
Okay .. so what does Vectored mean ?
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 doesnt 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
Stu , the System internally maintains a table called IVT or Interrupt Vector Table which contains the
information about Interrupts sources and their corresponding ISR address.
Well , in my opinion you can literally use the meaning of the term Vector which comes from
mathematics & physics which indicates a quantity having direction and magnitude. In our case
we can consider the magnitude as the ID of the interrupt source i.e the processor knows what
is source of the interrupt request (IRQ). Similarly for direction you can consider that Vectored
IRQ Points to'(which is like direction) its own unique ISR. On the other hand each Non-Vectored
ISRs doesnt point to a unique ISR and instead the CPU needs to be supplied with the address of
the default or say.. a common ISR that needs to be executed when the interrupt occurs. 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.
If you are still confused than here it is in a nutshell : The dierence between Vectored IRQ(VIRQ) and
Non-Vectored IRQ(NVIRQ) is that VIRQ has dedicated IRQ service routine for each interrupt source

Posted By Umang Gajera Posted date: August 09, 2013 in: Embedded, LPC2148 Tutorials
11 Comments

SUBSCRIBE VIA EMAIL

Non-Vectored IRQ(NVIRQ) is that VIRQ has dedicated IRQ service routine for each interrupt source
which while NVIRQ has the same IRQ service routine for all Non-Vectored Interrupts. You will get a clear
picture when I cover some examples in examples section.
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 LCP214x 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.
Example: For example if you working with 2 interrupt sources say.. UART0 and TIMER0. Now if you
want to give TIMER0 a higher priority than UART0 .. then assign TIMER0 interrupt a lower number
slot than UART0 Like .. hook up TIMER0 to slot 0 and UART0 to slot 1 or TIMER0 to slot 4 and UART
to slot 9 or whatever slots you like. The number of the slot doesnt matter as long TIMER0 slot is
lower than UART0 slot.
VIC has plenty of registers. Most of the registers that are used to congure interrupts or read
status have each bit corresponding to a particular interrupt source and this mapping is same
for all of these registers. What I mean is that bit 0 in these registers corresponds to Watch dog
timer interrupt , bit 4 corresponds to TIMER0 interrupt , bit 6 corresponds to UART0 interrupt ..
and so on.
Here is the complete table which says which bit corresponds to which interrupt source as given in
Datasheet:
Table 1:
Bit# 22
IRQ

20

USB AD1 BOD

Bit# 10
IRQ

21

19

18

17

16

15

14

13

12

I2C1

AD0

EINT3

EINT2

EINT1

EINT0

RTC PLL

11
SPI1/SSP

SPI0 I2C0 PWM UART1 UART0 TIMR1 TIMR0 ARMC1 ARMC0 N/A WDT

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 1s will indicated enabled interrupts and 0s as
disabled interrupts. Writing 0s has no eect. [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 eect on VICIntEnable since writing at bit given
location will clear the corresponding bit in the VICIntEnable Register. Writing 0s has no eect.

[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 eect. [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]

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 rst 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

PLL

12

N/A

RTC

13

ARMCore0

EINT0

14

ARMCore1

EINT1

15

TIMER0

EINT2

16

TIMER1

EINT3

17

UART0

ADC0

18

UART1

I2C1

19

PWM

BOD

20

I2C0

ADC1

21

SPI0

10

USB

22

SPI1

11

The 5th bit is used to enable the vectored IRQ slot by writing a 1.
Attention! : 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
specic 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. For this tutorial
Ill be calling it as Interrupt Source Number to keep things simple and clear. (^_^)

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
nished execution. In this tutorial the only place well 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 NonVectored IRQ occurs.

Now , that weve seen all the Registers , lets consider another Example with a Simple Diagram : Here we
have 4 IRQs Congured. TIMER0 and SPI0 are congured as Vectored IRQs with TIMER0 having Highest
Priority. UART0 and PWM IRQs are setup as Non-Vectored IRQs. The whole setup is as given below:

Conguring and Programming Interrupts (ISR)


To explain How to congure Interrupts and dene ISRs Ill use Timers as an example , since this Timers
are easy to play. Hence , I Assume you are comfortable with using timers with LPC214x ;).
First lets see how to dene 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 well use a special
keyword called __irq which is a function qualier. If you use this keyword with the function denition
then compiler will automatically treat it as an ISR. Here is an example on how to dene an ISR in Keil :

__irqvoidmyISR(void)
{
...
}
//====OREquivalently====
voidmyISR(void)__irq
{
...
}

Note that both are perfectly valid ways of dening the ISR. I prefer to use __irq before the function
name.
Now lets see how to actually setup the interrupt. Consider we wanna assign TIMER0 IRQ and ISR to slot
X. Here is a simple procedure like thing to do that :

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 nally replace myISR with your own ISRs function Name .. and your Done!

VICIntEnable|=(1<<Y);
VICVectCntlX=(1<<5)|Y;
VICVectAddrX=(unsigned)myISR;

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

VICIntEnable|=(1<<4);//EnableTIMER0IRQ
VICVectCntl0=(1<<5)|4;//5thbitmust1toenabletheslot(seetheregisterdefinitionabove)
VICVectAddr0=(unsigned)myISR;
//VectoredIRQforTIMER0hasbeenconfigured

VICVectAddr0=(unsigned)myISR;
//VectoredIRQforTIMER0hasbeenconfigured

Attention! : 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.
Having done that now its time to write the code for the ISR.
More Attention plz! : 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 dierent 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 congured to trigger an interrupt. Hence such devices have a
dedicated interrupt register which contains a ag bit for each of these source(For Timer block
its T0IR). So , when the ISR is called rst 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 nished we also need to acknowledge or signal that the ISR has nished executing for
the current IRQ which triggered it. This is done by clearing the the ag(i.e the particular bit) in
the devices interrupt register and then by writing a zero to VICVectAddr register which signies
that interrupt has ISR has nished execution successfully.

Programming the Interrupt Service Routine (ISR) :


Okay to keep things keep simple lets consider two simple cases for coding an ISR (Ill use TIMER0 for
generating IRQs since its easy to understand and play with) :
Case #1) First when we have only one internal source of interrupt in TIMER0 i.e an MR0 match
event which raises an IRQ.
Case #2) And when we have multiple internal source of interrupt in TIMER0 i.e. say a match
event for MR0 , MR1 & MR2 which raise an IRQ.
In case #1 things are pretty straight forward. Since we know only one source is triggering an interrupt
we dont need to identify it though its a good practice to explicitly identify it. The ISR then will be
something like :

__irqvoidmyISR(void)
{

longintregVal;

regVal=T0IR;//readthecurrentvalueinT0'sInterruptRegister

//...MR0matcheventhasoccured..dosomethinghere

T0IR=regval;//writebacktocleartheinterruptflag

VICVectAddr=0x0;//TheISRhasfinished!
}

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

#defineMR0I_FLAG(1<<0)
#defineMR1I_FLAG(1<<1)
#defineMR2I_FLAG(1<<2)
__irqvoidmyISR(void)
{

longintregVal;

regVal=T0IR;//readthecurrentvalueinT0'sInterruptRegister

if(T0IR&MR0I_FLAG)

//dosomethingforMR0match

elseif(T0IR&MR1I_FLAG)

//dosomethingforMR1match

elseif(T0IR&MR2I_FLAG)

//dosomethingforMR2match

T0IR=regVal;//writebacktocleartheinterruptflag

VICVectAddr=0x0;//AcknowledgethatISRhasfinishedexecution
}

Okay Did you Notice a Second use of Case #2 ? If not then here it is .. : Case #2 actually provides
a general method of using Timers as PWM generators! You can use any one of the match registers
as PWM Cycle generator and then use other 3 match registers to generate 3 PWM signals! Since
LPC214x already has PWM generator blocks on chip I dont see any use of Timers being used as
PWM generators. But for MCUs which dont have PWM generator blocks this is very useful.
Soo , Now its time to go one step further and pop-out Case #3 and Case #4 out of the blue :P. Both of

Soo , Now its time to go one step further and pop-out Case #3 and Case #4 out of the blue :P. Both of
them deal with IRQs from dierent blocks viz. TIMER0 and UART0.
Case #3) When we have Multiple Vectored IRQs from dierent Devices. Hence Priority comes
into picture here.
Case #4) Lastly when we have Multiple Non-Vectored IRQs from dierent Devices.
Dont worry if your not acquianted with with UARTs , Ill be Posting a Tutorial on UART with LPC214x
shortly Its in the pipeline :).
For Case #3 , Consider we have TIMER0 and UART0 generating interrupts with TIMER0 having higher
priority. So in this case well need to write 2 dierent 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 :

__irqvoidmyTimer0_ISR(void)
{

//Sameasincase#1
}
__irqvoidmyUart0_ISR(void)
{

longintregVal;

regVal=U0IIR;//readthecurrentvalueinU0'sInterruptRegister

//whichalsoclearsit!

//SomethinginsideUART0hasraisedanIRQ

VICVectAddr=0x0;//TheISRhasfinished!
}

For Case #4 too we have TIMER0 and UART0 generating interrupts. But here both of them are NonVectored 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 :

__irqvoidmyDefault_ISR(void)
{

longintT0RegVal,U0RegVal;

T0RegVal=T0IR;//readthecurrentvalueinT0'sInterruptRegister

U0RegVal=U0IIR;//readthecurrentvalueinU0's(Uart0)InterruptIdentificationRegister

if(T0IR)

//dosomethingforTIMER0Interrupt

T0IR=T0RegVal;//writebacktocleartheinterruptflagforT0

if(!(U0RegVal&0x1))

//dosomethingforUART0Interrupt

//NoneedtowritebacktoU0IIRsincereadingitclearsit

VICVectAddr=0x0;//TheISRhasfinished!
}

Attention Plz!: Note than UART0s Interrupt Register is a lot dierent than TIMER0s. The rst Bit
in U0IIR indicates whether any interrupt is pending or not and its Active LOW! The next 3 bits
give the Identication for any of the 4 Interrupts if enabled. There is more to it which Ill explain
in detail in Upcoming Dedicated Tutorial on Uarts and Interrupt Programming related to it.

But Wait! What about FIQ ?


Well , you can think FIQ as a promoted version of a Vectored IRQ. To promote or 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.

In Case Interrupts are Not working in Keil UV4 / Crossworks :


For those of you who are using Keil UV Version4(or higher) youll need to edit Target Option settings
and those who are using Crossworks for ARM etc .. you need to manually enable global interrupt. Ive
posted a solution for this @ http://www.ocfreaks.com/lpc2148-interrupt-problem-issue-x. Please go
through that post to make Interrupts work.

Example Source for all Cases:


Example 1 for Case #1:
For 1st example Ill cover an interrupt driven blinky using timers which Ive also use in the LPC214x

Timer Tutorial which can be found here : http://www.ocfreaks.com/lpc2148-timer-tutorial/ You can get
the Complete Source and KeilUV4 Project les from the given link itself. Here is a snippet of the code
for Case #1:

/*
(C)UmangGajera|Power_user_EXwww.ocfreaks.com201113.
MoreEmbeddedtutorials@www.ocfreaks.com/cat/embedded
SoruceforInterruptTutorialCase#1.
License:GPL.
*/
#include<lpc214x.h>
#defineMR0I(1<<0)//InterruptWhenTCmatchesMR0
#defineMR0R(1<<1)//ResetTCwhenTCmatchesMR0
#defineDELAY_MS500//0.5Second(s)Delay
#definePRESCALE60000//60000PCLKclockcyclestoincrementTCby1
voidinitClocks(void);
voidinitTimer0(void);
__irqvoidT0ISR(void);
voidinitClocks(void);
intmain(void)
{

initClocks();//InitializeCPUandPeripheralClocks@60Mhz

initTimer0();//InitializeTimer0

IO0DIR=0xFFFFFFFF;//ConfigureallpinsonPort0asOutput

IO0PIN=0x0;

T0TCR=0x01;//Enabletimer

while(1);//InfiniteIdleLoop

//return0;//normallythiswontexecuteever

:P

voidinitTimer0(void)
{

/*AssumingthatPLL0hasbeensetupwithCCLK=60MhzandPCLKalso=60Mhz.*/

//ConfigureTimer0

T0CTCR=0x0;

T0PR=PRESCALE1;//(ValueinDecimal!)IncrementT0TCatevery60000clockcycles
//Countbeginsfromzerohencesubtracting1
//60000clockcycles@60Mhz=1mS

T0MR0=DELAY_MS1;//(ValueinDecimal!)ZeroIndexedCounthencesubtracting1

T0MCR=MR0I|MR0R;//Setbit0&bit1toHighwhichisto:Interrupt&ResetTConMR0

//SetupTimer0Interrupt

VICVectAddr4=(unsigned)T0ISR;//PointerInterruptFunction(ISR)

VICVectCntl4=0x20|4;//0x20(i.ebit5=1)>toenableVectoredIRQslot

//0x4(bit[4:0])>thisthesourcenumberhereitstimer0whichhasVICchannelmask#as4

//YoucangettheVICChannelnumberfromLpc214xmanualR2pg58/sec5.5

VICIntEnable=0x10;//Enabletimer0int

T0TCR=0x02;//ResetTimer

__irqvoidT0ISR(void)
{

longintregVal;

regVal=T0IR;//ReadcurrentIRvalue

IO0PIN=~IO0PIN;//ToggleallpinsinPort0

T0IR=regVal;//WritebacktoIRtoclearInterruptFlag
VICVectAddr=0x0;//Thisistosignalendofinterruptexecution

voidinitClocks(void)
{
//ThisfunctionisusedtoconfigPPL0andsetupboth
//CPUandPeripheralclock@60Mhz
//Youcanfinditsdefinitionintheattachedfilesorcase#2source
}

Download Project Source for Case #1 @ OCFreaks.com_LPC214x_TimerIRQ_Tutorial.zip


[Successfully tested on Keil UV4.70a]

Example 2 for Case #2:


This is an extended version of blinky which uses 3 Match Registers i.e 3 Interrupts sources within
TIMER0 itself. Each Match register Interrupt is used to Turn on and o 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 rst LED , similarly MR2 and MR3 for PIN1 and PIN2 i.e for second and thrid LED
respectively. MR0 has been congured to Trigger an Interrupt when 500ms have elapsed after TC is

respectively. MR0 has been congured to Trigger an Interrupt when 500ms have elapsed after TC is
reset. MR1 has been congured to Trigger an Interrupt when 1000ms have elapsed after TC is reset.
MR2 has been congured 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)UmangGajera|Power_user_EXwww.ocfreaks.com201113.
MoreEmbeddedtutorials@www.ocfreaks.com/cat/embedded
LPC2148InterruptExample.
License:GPL.
*/
#include<lpc214x.h>
#definePLOCK0x00000400
#defineMR0I(1<<0)//InterruptWhenTCmatchesMR0
#defineMR1I(1<<3)//InterruptWhenTCmatchesMR1
#defineMR2I(1<<6)//InterruptWhenTCmatchesMR2
#defineMR2R(1<<7)//ResetTCwhenTCmatchesMR2

#defineMR0I_FLAG(1<<0)//InterruptFlagforMR0
#defineMR1I_FLAG(1<<1)//InterruptFlagforMR1
#defineMR2I_FLAG(1<<2)//InterruptFlagforMR2
#defineMR0_DELAY_MS500//0.5Second(s)Delay
#defineMR1_DELAY_MS1000//1SecondDelay
#defineMR2_DELAY_MS1500//1.5Second(s)Delay
#definePRESCALE60000//60000PCLKclockcyclestoincrementTCby1
voiddelayMS(unsignedintmilliseconds);
voidinitClocks(void);
voidinitTimer0(void);
__irqvoidmyTimer0_ISR(void);
voidsetupPLL0(void);
voidfeedSeq(void);
voidconnectPLL0(void);
intmain(void)
{

initClocks();//InitializeCPUandPeripheralClocks@60Mhz

initTimer0();//InitializeTimer0

IO0DIR=0xFFFFFFFF;//ConfigureallpinsonPort0asOutput

IO0PIN=0x0;

T0TCR=0x01;//Enabletimer

while(1);//InfiniteIdleLoop

//return0;//normallythiswontexecuteever

:P

voidinitTimer0(void)
{

/*AssumingthatPLL0hasbeensetupwithCCLK=60MhzandPCLKalso=60Mhz.*/

//ConfigureTimer0

T0CTCR=0x0;

T0PR=PRESCALE1;//60000clockcycles@60Mhz=1mS

T0MR0=MR0_DELAY_MS1;//0.5sec(ValueinDecimal!)ZeroIndexedCounthencesubtracting1

T0MR1=MR1_DELAY_MS1;//1sec

T0MR2=MR2_DELAY_MS1;//1.5secs

T0MCR=MR0I|MR1I|MR2I|MR2R;//SettheMatchcontrolregister

//SetupTimer0Interrupt

//I'vejustrandomlypickedupslot4

VICVectAddr4=(unsigned)myTimer0_ISR;//PointerInterruptFunction(ISR)

VICVectCntl4=0x20|4;

VICIntEnable=0x10;//Enabletimer0int

T0TCR=0x02;//ResetTimer

__irqvoidmyTimer0_ISR(void)
{

longintregVal;

regVal=T0IR;//readthecurrentvalueinT0'sInterruptRegister

if(T0IR&MR0I_FLAG)
{

//dosomethingforMR0match

IO0PIN^=(1<<0);//ToggleGPIO0PIN0..P0.0

}
elseif(T0IR&MR1I_FLAG)

elseif(T0IR&MR1I_FLAG)
{

//dosomethingforMR1match

IO0PIN^=(1<<1);//ToggleGPIO0PIN1..P0.1

}
elseif(T0IR&MR2I_FLAG)

//dosomethingforMR0match

IO0PIN^=(1<<2);//ToggleGPIO0PIN2..P0.2

T0IR=regVal;//writebacktocleartheinterruptflag
VICVectAddr=0x0;//AcknowledgethatISRhasfinishedexecution

voidinitClocks(void)
{

setupPLL0();

feedSeq();//sequenceforlockingPLLtodesiredfreq.

connectPLL0();

feedSeq();//sequenceforconnectingthePLLassystemclock

//SysClockisnowticking@60Mhz!

VPBDIV=0x01;//PCLKissameasCCLKi.e60Mhz

//PLL0Nowconfigured!

//PLLRelatedFunctions:
//UsingPLLsettingsasshownin:http://www.ocfreaks.com/lpc214xplltutorialforcpuandperipheralclock/
voidsetupPLL0(void)
{

//Note:Assuming12MhzXtalisconnectedtoLPC2148.

PLL0CON=0x01;

PLL0CFG=0x24;
}
voidfeedSeq(void)
{

PLL0FEED=0xAA;

PLL0FEED=0x55;
}
voidconnectPLL0(void)
{

while(!(PLL0STAT&PLOCK));

PLL0CON=0x03;
}

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!
Download Project Source for Case #2 @ OCFreaks.com_LPC214x_Interrupt_Tutorial_Case2.zip
[Successfully tested on Keil UV4.70a]

Example 3 for Case #3:


Note that Case #3 and Case #4 are just some Fictitious-Typo cases which Ive randomly made to
Explain Multiple Vectored and Non-Vectored IRQs. I havent tried them out on my Development
Board yet , but since the Simulation seems to work perfect I assume both will work as expected
if you try them out. If any doesnt work please let me know.
Consider we have TIMER0 generating interrupt every 500ms and UART0 generates an interrupt when
some data arrives i.e when you press a key in the terminal. TIMER0 ISR will Toggle P0.2 and UART0 ISR
will Toggle P0.3. Here TIMER0 ISR will have a higher priority over UART0 ISR. Hence when TIMER0
interrupt occurs and at the same time UART0 interrupt occurs , rst TIMER0 ISR will be serviced and
then UART0.
Here Im only giving the code to Enable both the Interrupts and its ISR. You can nd the complete code
in les attached below.
Code for Conguring Both TIMER0 and UART0 Interrupts is as given Below:

//SetupTIMER0Interrupt
//UsingSlot0forTIMER0
VICVectAddr0=(unsigned)myTimer0_ISR;//PointerInterruptFunction(ISR)
VICVectCntl0=0x20|4;
VICIntEnable|=(1<<4);//Enabletimer0int,4thbit=1
//SetupUART0Interrupt
//AnySlotwithLowerPrioritythanTIMER0'sslotwillsuffice

//AnySlotwithLowerPrioritythanTIMER0'sslotwillsuffice
VICVectAddr1=(unsigned)myUart0_ISR;//PointerInterruptFunction(ISR)
VICVectCntl1=0x20|6;

VICIntEnable|=(1<<6);//EnableUart0interrupt,6thbit=1

Both the ISRs will be as follows :

__irqvoidmyTimer0_ISR(void)
{

longintregVal;

regVal=T0IR;//readthecurrentvalueinT0'sInterruptRegister

IO0PIN^=(1<<2);//Toggle3rdPininGPIO0..P0.2

T0IR=regVal;//writebacktocleartheinterruptflag
VICVectAddr=0x0;//AcknowledgethatISRhasfinishedexecution

__irqvoidmyUart0_ISR(void)
{

longintregVal;

regVal=U0IIR;//ReadingU0IIRalsoclearsit!

//RecieveDataAvailableInterrupthasoccured

regVal=U0RBR;//dummyread

IO0PIN^=(1<<3);//Toggle4thPininGPIO0..P0.3

VICVectAddr=0x0;//AcknowledgethatISRhasfinishedexecution

Download Project Source for Case #3 @ OCFreaks.com_LPC214x_Interrupt_Tutorial_Case3.zip


[Successfully tested on Keil UV4.70a]

Example 4 for Case #4:


Even Here Ill give the main code and as with above you can nd the complete code in the attached
les.
In the Non-Vectored Case the conguration code will be as shown below:

VICDefVectAddr=(unsigned)myDefault_ISR;//PointertoDefaultISR
//Enable(NonVectored)TIMER0Interrupt
VICIntEnable|=(1<<4);//Enabletimer0int,4thbit=1
//Enable(NonVectored)UART0Interrupt
VICIntEnable|=(1<<6);//EnableUart0interrupt,6thbit=1

And the code for the Non-Vectored ISR is as given Below:

__irqvoidmyDefault_ISR(void)
{

longintT0RegVal,U0RegVal;

T0RegVal=T0IR;//readthecurrentvalueinT0'sInterruptRegister

U0RegVal=U0IIR;

if(T0IR)

IO0PIN^=(1<<2);//Toggle3rdPininGPIO0..P0.2

T0IR=T0RegVal;//writebacktocleartheinterruptflag

if(!(U0RegVal&0x1))

//RecieveDataAvailableInterrupthasoccured

U0RegVal=U0RBR;//dummyread

IO0PIN^=(1<<3);//Toggle4thPininGPIO0..P0.3

VICVectAddr=0x0;//AcknowledgethatISRhasfinishedexecution
}

Download Project Source for Case #4 @ OCFreaks.com_LPC214x_Interrupt_Tutorial_Case4.zip


[Successfully tested on Keil UV4.70a]
FINAL Attention Plz! : When you play with Multiple Interrupts , Please take care of all the
situations that may arise else in most cases Weird System Behavior might creep in giving you
Debugging Nightmares. Id say keep on experimenting with Interrupts .. the more youll do the
more youll learn and the more clear it will become!

Advanced Interrupt Handling:


Nested Interrupts:
Interrupts too can be nested. But we need to take some special care when dealing with nested
interrupts since in this case the context and the stack come into picture. When a nested interrupt
has nished execution we must get back to the correct context. Similarly too much of nesting and the
stack will get overloaded. Covering Nested interrupt is not within the scope of this tutorial. Here are
some useful documents which will surely help you out with interrupt nesting.
For more on Nested Interrupts you can refer the following documents :
1. NXP Application Note : AN10381
2. http://www.keil.com/support/docs/3353.htm

Spurious Interrupts:
I like to call Spurious Interrupt as Zombie Interrupts

in my opinion that suits it more. These can

happen in virtually any interrupt driven system and must be identied and handled properly else
undened system behaviour may occur. Such interrupts happen when an IRQ is generated out of no
where (Magic! lol..). Here the IRQ comes into exsistence even when none of the conditions for
triggering that IRQ have been met. Normally speaking its an interrupt that was not required in the rst
place. There are many reasons as to why it can happen :
1) Inherent bug/aw in system design
2) Electrical Interference / Noise
3) Software Bug
4) Signal glitches on Level Sensitive interrupts
5) Faulty components
6) Miscongured Hardware
7) etc (Youll get plently of info just by googling the term)
In respect to LPC214x(also LPC2000) MCUs , the probability of occurrence of Spurious Interrupts is high
when Watch Dog Timer or UART is used. The datasheet(manual) has special note on Spurious
interrupts on page 61. Covering the handling of Spurious Interrupts is too not in the scope of this
article.
For more information on handling of spurious interrupts I would like my readers to go through the
following documents :
1. Page 61 of LPC214x Usermanul Rev 2.0
2. NXP Application Note : AN10414

Understanding VIC in Detail:


Finally , Id like to share one of the best guide Ive found for Understanding the general Architecture of
VIC in detail .. which is the following document from ARM itself :
1. http://infocenter.arm.com/help/topic/com.arm.doc.ddi0181e/DDI0181.pdf
Share this:

Share

Tags:

Like

embedded

lpc2148

Tweet

programming

Share

Share

tutorial

Share

Previous
Roccat Kone Pure Color edition Mouse review

ABOUT THE AUTHOR


Umang Gajera

Next
Pulse Width Modulation (PWM) Tutorial

12Comments

OCFreaks!

Recommend

Share

Jointhediscussion
onkarkarle22daysago

siractuallywehavetotakeinterruptfromrtctoadjustbrightness
levelofledsaccordingtorealtime....canuhelpuswithprogramming
tutorialplz..

Reply Share
vikasvikeee3monthsago

itis1<<4ithinkfor5thbittoenable

Reply Share
Mahdi.sh6monthsago

Verygoodandhelpful
Godblessyou:)

Reply Share
Dinu2yearsago

Simpleexplanations....Itstoogudforthebeginnerslikeme...

Reply Share

Login

SortbyNewest

Reply Share
Pratik2yearsago

Hi,
InSpuriousInterruptssectionlinktoNXPApplicationNote:AN10414isinvalid.
Thanksfornicetutorial.

Reply Share
UmangGajera>Pratik2yearsago

You'remostWelcomePratik.I'vefixedthelink.:)

Reply Share
AmarMore3yearsago

Thanksalotforadetailedandunderstandabletutorial.

Reply Share
rijanshrestha3yearsago

thisisthepostiamsearchingforawhile.ThanktoyouocFreak.com..

Reply Share
OMKAR3yearsago

thanksforpost,Averygoodtutorialindeed!pleasepostRTC(REALTIME
CLOCK)USINGINTERRUPT.

Reply Share
lalit3yearsago

verygood

Reply Share
Bananda3yearsago

Averygoodtutorialindeed!Thanksalotforuploadingthistutorial.

Reply Share
nileshpathak3yearsago

thanksforpost....thesethingsarereallyhelpfultome...thanksonceagain...

Reply Share
Subscribe d AddDisqustoyoursiteAddDisqusAdd

Privacy

(c) OCFreaks! 2014.

Community

Server Monitoring with Livewatch.de

Register

Contact Us

You might also like