Using interrupts to read the push-button state
The previous recipe showed how to read digital signals with the GPIO peripheral. However, the proposed solution is inefficient because the CPU wastes clock cycles waiting for the button to be pressed while it could perform other tasks in the meantime. Furthermore, this could be a situation where we would keep the CPU in low-power mode when there are no other tasks to run.
Therefore, this recipe will show you how to change the sketch developed in the previous recipe to read the push-button state efficiently using interrupts.
Getting ready
Let’s prepare this recipe by learning what an interrupt is and which Mbed OS API we can use to read the push-button efficiently.
Working with interrupts using the Mbed OS API
An interrupt is a signal that temporarily pauses the main program to address an event through a dedicated function known as an interrupt handler or interrupt service routine (ISR). When the ISR ends the execution, the processor resumes the main program from the point at which it was interrupted, as shown in the following diagram:

An interrupt is a very efficient mechanism to save energy because we can put the CPU in a sleep state and wait for an event before starting the computation.
A microcontroller typically has multiple interrupt sources, and we can write a dedicated ISR for each one. Despite being a function, certain constraints limit the implementation of an ISR, such as:
- It does not have input arguments.
- It does not return a value. Therefore, we need to use global values to report status changes.
- It must be short to not steal too much time from the main program. We want to remind you that the ISR is not a thread since the processor can only resume the computation when the ISR finishes.
When using GPIO peripherals to read digital signals, the mbed::InterruptIn
object (https://os.mbed.com/docs/mbed-os/v6.15/apis/interruptin.html) can be employed to trigger an event whenever the logical level on the pin changes, as shown in Figure 2.49:

Figure 2.49: Rising interrupt versus falling interrupt
As we can observe from the preceding diagram, the mbed::InterruptIn
object can trigger interrupts when the logical level on the pin goes from LOW to HIGH (rising interrupts) or HIGH to LOW (falling interrupt). The initialization of this routine is the same as the mbed::DigitalIn()
one.
How to do it...
Open the sketch of the previous recipe and follow the next steps to control the LED light with the GPIO interrupt.
Step 1:
Define and initialize the mbed::InterruptIn
object with the PinName
of the GPIO pin connected to the push-button:
mbed::InterruptIn button(gpio_pin_in);
You can remove the mbed::DigitalIn
object from the sketch, as it is not required anymore.
Step 2:
Write the ISR for handling the interrupt request on the rising edge (LOW to HIGH) of the input signal:
void rise_ISR() {
led = 0;
}
The rise_ISR()
ISR routine will turn the LED off (led = 0
) when the digital signal on the pin changes from LOW to HIGH.
Step 3:
Write the ISR for handling the interrupt request on the falling edge (HIGH to LOW) of the input signal:
void fall_ISR() {
led = 1;
}
The fall_ISR()
ISR routine will turn the LED on (led = 1
) when the digital signal on the pin changes from HIGH to LOW.
Step 4:
Initialize the button
variable in the setup()
function:
void setup() {
button.mode(PullUp);
button.rise(&rise_ISR);
button.fall(&fall_ISR);
}
In the previous code, we configured the mbed::InterruptIn
object by doing the following:
- Enabling the internal pull-up resistor (
button.mode(PullUp)
) - Attaching the ISR function to run when the rising interrupt occurs (
button.rise(&rise_ISR)
) - Attaching the ISR function to run when the falling interrupt occurs (
button.fall(&fall_ISR)
)
Now, replace the code in the loop()
function with delay(4000)
:
void loop() {
delay(4000);
}
In the previous code snippet, we use the delay()
function to pause the program for four seconds. In theory, we could leave the loop()
function empty. However, we recommend using the delay()
routine when there are no tasks to be performed, as it can put the system in low-power mode, thus saving energy.
Compile the sketch and upload the program to the microcontroller. You should still be able to control the LED light with the push-button.
There’s more…
In this recipe, we learned how to read the GPIO input signal using the efficient mechanism of interrupts. If we use the Mbed OS API, we only need to replace the mbed::DigitalIn()
function with the mbed::InterruptIn()
one, write the ISRs, and that’s it. This type of interrupt is just one example of the many interrupt sources accessible within a microcontroller. For instance, another extensively used interrupt originates from the Timer peripheral, which allows tasks to be executed at specified intervals.