In this article, learn how to use interrupts in Raspberry Pi Pico & Pico W. A pushbutton will be used to explain how interrupts work.
We can interface a push button using either Polling or Interrupt. Here, you can learn the difference between the two methods and which one to use. The code in this article is written in MicroPython.
The Raspberry Pi Pico & Pico W boards have 26 General Purpose Input/Output (GPIO) pins that can be accessed using the header pins All GPIO pins of Raspberry Pi Pico can be configured as either input or output. All GPIO pins can also be configured as external interrupts.
Polling Vs interrupt
Polling and Interrupts are the two methods used to interface external switches such as push-buttons, keypads, Numpad, etc.
ⓘ Push buttons or tactile switches act as input devices for a microcontroller and we can read their status. A button connected to the positive supply voltage of a microcontroller will be read as logical-high(1) and a button attached to the ground will be read as logical-low(0).
Polling is used when the application is not time-sensitive. Interrupts are used when a device needs the attention of a microcontroller instantly.
Polling | Interrupt |
Polling is when the CPU executes code sequentially to check if any change in state has occurred. | In case of Interrupts, a device or a register notifies the CPU that it needs immediate attention. |
CPU takes care of Polling. | An interrupt handler handles interrupts. |
Polling occurs at regular intervals due to the sequential execution of code. | Interrupts can occur at any time. |
Interrupts are handled by parts of software called Interrupt Service Routine(ISR). When an interrupt occurs, the CPU starts executing code within this routine. When the task in the routine is completed, the processor continues executing code from where it left off.
Prerequisites For This Tutorial
- A Raspberry Pi Pico or Pico W.
- Push button(such as a momentary tactile switch).
- Connecting wires and breadboard.
Your Raspberry Pi Pico needs to be preloaded with a MicroPython UF2 file to program it in MicroPython. You can read our getting started guide for Raspberry Pi Pico where we show all the steps required to start programming Raspberry Pi Pico in MicroPython.
Read Buttons using Polling in Raspberry Pi Pico
Let us first look at the polling method of reading a button. Connect a push button to your Pico as shown in the diagram below.
The Raspberry Pi Pico has 26 easily accessible GPIOs which is explained in our in-depth article Raspberry Pi Pico Pinout Guide. You can use any of these GPIO pins to connect push buttons. Here is the pin diagram of Raspberry Pi Pico W for reference.
We shall write a script to detect if the button is HIGH or LOW. Pico can pull a pin to the positive voltage with an internal resistor. Why do we need a pullup resistor? Because without the pull-up configured, a pin will give noisy input as it will be in a floating state.
The steps to upload code are explained here using Thonny IDE. You can also use any other IDE such as the uPyCraft IDE.
With the connections done as shown in the schematic above, connect your Pico to your computer. Open Thonny IDE and set the interpreter to use MicroPython on Raspberry Pi Pico.
Create a new file (go toFile>New).
Paste the following script:
from machine import Pin
counter=0
pin = Pin(5, Pin.IN, Pin.PULL_UP)
while True:
if pin.value()==0:
print("Button Pressed")
counter+=1
print("Count={}".format(counter))
Code language: Python (python)
Click on File>Save as and select the save location as Raspberry Pi Pico.
Name the code file as main.py.
Run the code by clicking the Run icon or by pressing the F5 key.
Now, when you press the push button, you should see an output as shown below:
Alternatively, if you wish to detect a button press when the button is HIGH instead of LOW, use PULL_DOWN instead of PULL_UP and modify your code as shown below:
from machine import Pin
counter=0
pin = Pin(5, Pin.IN, Pin.PULL_DOWN)
while True:
if pin.value() is 1:
counter+=1
print("Button Pressed")
print("Count={}".format(counter))
Code language: Python (python)
Code Explained:
- We import the
Pin
class from themachine
module which is required to interact with the GPIO pins. Then we create a variable calledcounter
to keep count of how many times the switch has been pressed.
from machine import Pin
counter=0
Code language: JavaScript (javascript)
- An object
pin
is created which takes 3 arguments. We designate GPIO 5 as an input withPULL_UP
enabled.
pin = Pin(5, Pin.IN, Pin.PULL_UP)
Code language: Python (python)
- Inside the while loop, we see if
pin.value()
is equal to 0, which denotes that the pin has been connected to the ground(i.e. the pushbutton is pressed). If a button press is detected, we increment thecounter
variable and print the value of the counter.
if pin.value() is 1:
counter+=1
print("Button Pressed")
print("Count={}".format(counter))
Code language: Python (python)
Also read: Arduino vs MicroPython vs CircuitPython: Which One Will You Choose?
Debouncing a Pin in Raspberry Pi Pico
Did you notice any shortcomings in the script we wrote above? A single button press may increment the counter by many counts. This is due to the nature of mechanical contacts in a button and is called Bouncing.
Debouncing is a process of eliminating bouncing by using software or hardware. For example, a resistor-capacitor combination can be used for debouncing a switch. In software, the time elapsed between two consecutive button presses is measured, and if multiple inputs occur within a certain time interval, only one input is registered.
To implement debouncing, we need to modify our script so that button presses are detected with a small amount of delay in between. For this, we shall use the time module in MicroPython. It contains the function time.ticks_ms() which is described as “Returns an increasing millisecond counter with an arbitrary reference point, that wraps around after some value.”
So the function returns a random value of time (in milliseconds) that increments up to a certain value, and we can use it to measure the time interval between two events.
Upload this modified script and save the file on Raspberry Pi Pico with a ‘.py’ filename extension:
from machine import Pin
import time
counter=0
debounce_time=0
pin = Pin(5, Pin.IN, Pin.PULL_UP)
while True:
if ((pin.value() is 0) and (time.ticks_ms()-debounce_time) > 300):
counter+=1
debounce_time=time.ticks_ms()
print("Button Pressed")
print("Count={}".format(counter))
Code language: Python (python)
We import the time module and create a variable called debounce_time
. Then, we check if the time elapsed since the last button press is more than 300 milliseconds, using (time.ticks_ms()-debounce_time) > 300)
. If this condition is satisfied, then we increment the button press counter and set the debounce_time
to the current value returned by time.ticks_ms()
. This is very similar to using the millis()
function in Arduino code.
External Interrupts in Raspberry Pi Pico & Pico W
All GPIO pins in Raspberry Pi Pico support interrupts. The interrupts can be classified into three types:
- Level High: An interrupt occurs when a pin is HIGH or at logic 1.
- Level Low: An interrupt occurs when a pin is LOW or at logic 0.
- Rising Edge: Interrupt occurs when a pin transitions from a LOW to HIGH.
- Falling Edge: Interrupt occurs when a pin transitions from HIGH to LOW.
The level interrupts in RPi Pico do not latch, i.e. the interrupt becomes inactive as soon the GPIO changes its state from HIGH to LOW or vice versa.
Also read: Raspberry Pi Pico Serial Communication Example(MicroPython)
Using Push-Buttons to Trigger Raspberry Pi Pico Interrupts
Let us now try to interface a pushbutton using an interrupt. For this example, we shall use the Falling Edge(or Edge Low) of a pin voltage to trigger an interrupt. The connections will be the same as we used in the previous example for Polling a button input i.e. connect a pushbutton between GPIO5 and GND pin.
- In Thonny IDE, create a new project and upload the following interrupt example script to your Pi Pico.
from machine import Pin
interrupt_flag=0
pin = Pin(5,Pin.IN,Pin.PULL_UP)
def callback(pin):
global interrupt_flag
interrupt_flag=1
pin.irq(trigger=Pin.IRQ_FALLING, handler=callback)
while True:
if interrupt_flag is 1:
print("Interrupt has occured")
interrupt_flag=0
Code language: Python (python)
- Save and run the script.
When the script runs, you must see the line “Interrupt has occurred” displayed in your Thonny shell window when the pushbutton is pressed.
External Interrupt Code Explained
We first import the Pin
class and initialize a global variable called interrupt_flag
to 0. We will use this variable to keep track of the occurrence of interrupts. Global variables in MicroPython can be accessed by all functions. Then we create a pin
object of the Pin
class. GPIO 5 is set as an input with PULL_UP
enabled.
from machine import Pin
interrupt_flag=0
pin = Pin(5,Pin.IN,Pin.PULL_UP)
Code language: Python (python)
Next, we define a function called callback()
to handle interrupts. The code inside this function should not perform any complex task, as it needs to hand over CPU usage to the main program quickly. We set the interrupt_flag
variable as 1. Note that we need to use the global
keyword when we change a global variable inside a function.
def callback(pin):
global interrupt_flag
interrupt_flag=1
Code language: Python (python)
To attach the interrupt to the pin, we use the irq()
function. This function takes two arguments :
trigger : trigger can be of the following types-
(1) Pin.IRQ_FALLING– interrupt on falling edge.
(2) Pin.IRQ_RISING– interrupt on rising edge.
(3) Pin.IRQ_LOW_LEVEL – interrupt on LOW level.(Not supported in MicroPython for Pico at the time of publishing this article.)
(4) Pin.IRQ_HIGH_LEVEL -
interrupt on HIGH level.(Not supported in MicroPython for Pico at the time of publishing this article.)
(5) Pin.IRQ_FALLING | Pin.IRQ_RISING – interrupt on both the rising edge and the falling edge.
handler : handler specifies the function which will be called when an interrupt occurs.
pin.irq(trigger=Pin.IRQ_FALLING, handler=callback)
Code language: Python (python)
In the while loop, we continuously check the value of the interrupt_flag
variable. If this value is detected as ‘1’, that means an interrupt has occurred. We reset the variable to ‘0’ so that the variable can be set again when an interrupt occurs.
You might have noticed that the line “Interrupt has occurred” is printed in multiple lines with just a single button press. This is similar to the button-bouncing case that we discussed earlier in this article. We shall solve this problem in the next step.
Using External Interrupt to Toggle an LED
In the following example let us try to toggle the onboard LED of the Raspberry Pi Pico, and debounce the external interrupt button input. The code should be self-explanatory as if we have already discussed similar code earlier in this article.
# Source: Electrocredible.com, Language: MicroPython
from machine import Pin
import time
interrupt_flag=0
debounce_time=0
pin = Pin(5, Pin.IN, Pin.PULL_UP)
led = Pin("LED", Pin.OUT)
count=0
def callback(pin):
global interrupt_flag, debounce_time
if (time.ticks_ms()-debounce_time) > 500:
interrupt_flag= 1
debounce_time=time.ticks_ms()
pin.irq(trigger=Pin.IRQ_FALLING, handler=callback)
while True:
if interrupt_flag is 1:
interrupt_flag=0
print("Interrupt Detected")
led.toggle()
Code language: Python (python)
Now, when the pushbutton is pressed, the onboard LED must change between ON and OFF states.
Polling or Interrupt? Which One Should I Use?
If you are interfacing external devices that are time-sensitive, you should use interrupts. If your program is simple, you can use polling to interface buttons. Some microcontrollers have few external interrupt-compatible pins. If you have to interface many buttons, such as a keypad, you sometimes have to opt for polling instead of using interrupts.
I hope you found this article on using external interrupts in Raspberry Pi Pico to be useful. Please share your thoughts in the comments below. Thank you for reading.
Also read:
Leave a Reply