1. Objectives
-
Understand interrupts in LPC1768
-
Using external interrupts
2. Parts List
-
LPC1768 mbed board
-
USB A-Type to Mini-B cable
-
Breadboard
-
LEDs
-
Push-buttons or switches
-
330-Ohm Resistors
-
Jumper wires
3. Background
Interrupts are essential for embedded systems. The information in this experiment will be used in all future experiments in this lab. |
3.1. Interrupts in LPC1768
Interrupts allow for suspending the currently executing code, and having the CPU switch to execute a routine associated with the received interrupt request.
In LPC1768, there are 35 hardware interrupts. Each interrupt is
identified by a number called IRQn
, or Interrupt ID as called in
the LPC1768 manual. Below are some examples of hardware interrupts and
their IDs:
Interrupt ID | Interrupt Source |
---|---|
1 |
Timer 0 |
2 |
Timer 1 |
5 |
UART 0 |
6 |
UART 1 |
13 |
SPI |
18 |
EINT0 |
19 |
EINT1 |
20 |
EINT2 |
21 |
EINT3 |
33 |
USB Activity Interrupt |
In CMSIS, interrupts are numbered from 0 to 34. |
In addition to these 35 interrupts, there are 8 exceptions with negative numbers. Exceptions are not discussed in this experiment. |
3.1.1. Setting up Interrupts in LPC1768
There are four main steps to correctly setup any interrupt in LPC1768:
-
Configure the required peripheral to generate interrupt requests. For example, to be able to use a timer interrupt, a timer must be activated and configured to generate interrupt requests.
-
Enable the interrupt in the Nested Vectored Interrupt Controller (NVIC). See Enabling the Interrupt in the NVIC for more information about the NVIC.
-
Write an interrupt service routine (ISR): the routine that needs to be executed when an interrupt request is received.
-
Clear the interrupt request at the end of the ISR to allow future requests of the same interrupt.
Some peripherals that are capable of generating interrupts can also be used without interrupts. There are valid uses for either approach. That’s why it is required to configure devices to generate interrupt requests when that behavior is desired (step 1 in the list above). |
Upon setting up an interrupt as described above, the LPC1768 microcontroller will be responsible for:
-
Detecting the interrupt request generated by a peripheral. This request will be generated by the peripheral that has been enabled by software.
-
Jumping to the interrupt service routine associated with that request.
3.2. Configuring Microcontroller Interrupts
This section elaborates the four steps listed in the previous section (Setting up Interrupts in LPC1768) to configure the LPC1768 microcontroller to handle interrupt requests generated by a given device.
The programmer must perform four main steps:
-
Enable a peripheral to generate a hardware interrupt request (not to be discussed here because it is peripheral dependent)
-
Enable the required interrupt in the NVIC
-
Write an interrupt service routine (ISR)
-
Clear the interrupt request at the end of the ISR
3.2.1. Enabling the Interrupt in the NVIC
The Nested Vectored Interrupt Controller (NVIC) offers very fast interrupt handling and provides the vector table [keil-nvic].
In addition, the NVIC:
-
Saves and restores automatically a set of the CPU registers (R0-R3, R12, PC, PSR, and LR).
-
Does a quick entry to the next pending interrupt without a complete pop/push sequence.
-
Provides many other advanced features.
The CMSIS core module defines a set of interrupt helper functions. For example, to enable interrupts for a given interrupt ID, you can use the function:
NVIC_EnableIRQ(IRQn); // IRQn is the interrupt ID
For example, for UART1
, IRQn
is 6 (see Example Interrupt
IDs Table). You can use this number or use the given name in
lpc17xx.h
: UART1_IRQn
.
3.2.2. The ISR
Whenever an interrupt request is generated, the CPU will jump to the corresponding ISR. When using CMSIS, the ISR is a C function that has the following prototype format:
void __peripheral___IRQHandler();
void TIMER2_IRQHandler() {
// Your code goes here
// Clear the interrupt request at the end of the ISR
}
If you are using mbed online compiler, you need to wrap the interrupt handler by an
|
3.2.3. Clearing the Interrupt Request
As indicated in the format of the ISR above, the last statement in any ISR should be to clear the request that has just been served. This is required to allow future requests of the same interrupt.
This step is peripheral-dependent and is usually done by clearing a bit in one of the peripheral registers.
3.2.4. Other Interrupt-Related Operations
Interrupts will not function at all without the above four steps. There are other issues, however, that are not essential in simple applications, but can be very useful and even essential in some applications, especially when you have multiple interrupts. We will discuss two such issues here:
-
Interrupt status
-
Interrupt priority
Interrupt Status
Sometimes, you need to check the status of a specific interrupt. For example, is it pending, active or disabled.
When using CMSIS, the status of interrupts can be checked by calling one of the following functions, depending on the application:
-
uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)
-
If the interrupt status is not pending, the function returns
0
. -
If the interrupt status is pending, the function returns
1
.
-
-
uint32_t NVIC_GetActive(IRQn_Type IRQn)
-
If the interrupt status is not active, the function returns
0
. -
If the interrupt status is active, the function returns
1
.
-
Interrupt Priority
When using CMSIS, you can set interrupt priorities by calling the function:
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
-
The fist argument is the interrupt ID.
-
The second argument represents the priority, where 0 is the highest priority and 31 is the lowest priority.
To assign a different priority for each interrupt, you need to call this function for every interrupt you are using. |
3.3. External Interrupts
To practice interrupts, we will concentrate on external interrupts only in this experiment, since other hardware interrupts require understanding the functions with which they are associated, which we did not cover yet. |
One type of interrupts that is easy to experiment with is external interrupts. They are the interrupts that are generated by a device outside the microcontroller. An external interrupt should be connected to one of the I/O port pins.
In external interrupts, an interrupt request is generated by a pulse at a pin that has been enabled to accept external interrupt requests. A simple way to implement that is to use a push-button to generate that request.
The difference between such implementation and what you did in Experiment 2 is that in Experiment 2, we used polling, where the CPU is always busy reading the pin in order to detect a change that would trigger some action. When using interrupts, however, the CPU is available to execute other code. When the push-button is pressed, the CPU stops whatever it is doing and jumps to the routine associated with that interrupt request.
There are four external interrupt channels available to the developer,
called EINT0
, EINT1
, EINT2
and EINT3
. In older ARM versions, a
pin’s function must be set for the pin to act as an external
interrupt. This is done using the PINSELx
register.
However, one of the new features of the newer
Cortex family is accepting external interrupts from some GPIO pins!
Any GPIO pin used for external interrupts will be using external
interrupt channel 3 (EINT3
).
You can use GPIO pins from ports 0 and 2 only for external interrupts. You have about 24 different pins to choose from. Compare that, for example, to ARM7 where only 7 pins are available for external interrupts.
External interrupts can be enabled on two sets of pins:
|
In the two following sections, we will discuss and practice:
|
3.3.1. GPIO external interrupts
GPIO external interrupts share the same ISR for EINT3. As discussed in Setting up Interrupts in LPC1768, you always need to enable the NVIC and write an ISR.
For GPIO external interrupts, that leaves two more steps:
-
Activating GPIO external interrupts
-
clearing a GPIO interrupt request at the end of the ISR
Activating GPIO External Interrupts
To activate external interrupts on a GPIO pin, you only need to configure whether the pin is to generate an interrupt request on the rising edge or on the falling edge.
You can set external interrupts to be generated on the rising edge
on a GPIO pin by setting the IO0IntEnR
and IO2IntEnR
registers,
depending on the port to which the pin belongs. These names refer to
32-bit registers. Setting a bit to 1
enables rising-edge interrupts
at the corresponding pin.
To generate interrupts on the falling edge, you can use the
IO0IntEnF
and IO2IntEnF
registers instead.
In LPC17xx.h
, the structure that deals with GPIO external interrupts
is LPC_GPIOINT
, which includes a few fields that control the GPIO
pins when acting as an external interrupt.
LPC_GPIOINT->IO2IntEnR = 1;
Clearing External GPIO Interrupt Requests
To clear the interrupts of a port pin, set the corresponding bit to
1
in register IO0IntClr
or IO2IntClr
, depending on the
port. Both registers are fields of the LPC_GPIOINT
structure.
Other issues related to GPIO interrupts
You may have noted that, to enable GPIO interrupts, you have to select whether they are triggered by the rising or falling edge of the pulse at the pin.
You can check for pending GPIO interrupts by reading the appropriate
status register. There are four status registers for ports 0 and 2
that indicate whether an interrupt is pending, and whether it is
triggered by a rising edge or a falling edge. They are IO0IntStatR
,
IO2IntStatR
, IO0IntStatF
, and IO2IntStatF
.
For Example, if bit 9 of IO2IntStatR
is 1
, then P2.09 has a
pending rising-edge interrupt request.
This is particularly important when you have multiple interrupts sharing the same interrupt channel (EINT3 in our case). Any one of them can result in executing the same ISR. Now, If you want to perform different actions for each interrupt, you need to identify the source interrupt in order to perform the corresponding action. You can do that by checking the status registers in your ISR.
3.3.2. Non-GPIO external interrupts
External interrupt requests can be generated using any of the 4
dedicated external interrupt pins, named EINT1
, EINT2
, EINT3
,
and EINT4
:
EINT0 |
P2.10 |
EINT1 |
P2.11 |
EINT2 |
P2.12 |
EINT3 |
P2.13 |
However, These four pins are not available in LPC1768 mbed board.
4. Tasks
4.1. One External Interrupt
Use a push-button to generate an external interrupt using a GPIO pin. Do something interesting in the ISR!
4.2. Two External Interrupts
Use two external interrupts, where each interrupt triggers a different task. For example, each interrupt could blink an LED 10 times at a different rate.
All tasks must be completed during the lab session. |
5. Grading Sheet
Task | Points |
---|---|
Task 1: External interrupt using a GPIO pin |
3.5 |
Task 2: Two external interrupts |
3.5 |
Discussion |
3 |
Resources
-
Cortex™-M3 'Technical Reference Manual'
https://www.keil.com/dd/docs/datashts/arm/cortex_m3/r1p1/ddi0337e_cortex_m3_r1p1_trm.pdf -
NXP Semiconductors. UM10360 — LPC176x/5x User Manual. Rev. 3.1. 4 April 2014.
https://www.waveshare.com/w/upload/0/07/LPC176x5x_User_manual_EN.pdf