Skip to content

uPesy now ships to the entire European Union

Contents Menu Expand Light mode Dark mode Auto light/dark mode

How to use interrupts in MicroPython with a Pico RPi?

In electronics, an interrupt is a signal sent to a processor that tells it that an important task must be executed immediately, thus interrupting the program in progress.

Interrupts can be triggered in various ways, for example by an external event, a timer they allow to perform tasks asynchronously, outside the main program.

Interrupts in MicroPython

In practice, interrupts are generally used to:

  • Execute portions of critical code when an external event occurs, for example when a button is pressed and this automatically triggers a Python function.

  • Execute functions periodically, e.g. to flash an LED every 5 seconds.

Without using interrupts, it is possible to create a script that turns on an LED when a button is pressed. However, this method has two major drawbacks.

from machine import Pin

pin_button = Pin(14, mode=Pin.IN, pull=Pin.PULL_UP)
pin_led    = Pin(25, mode=Pin.OUT)

while True:
    if not pin_button.value():
        pin_led.on()
    else:
        pin_led.off()

The first drawback is that the script spends its time monitoring the value of the pin_button pin to know if the button has been pressed. The script can’t do much more, because otherwise the second problem will occur: missing events. If the script performs other tasks in the loop, it may miss the temporary button press.

The advantage of using a hardware interrupt is that the detection is completely separate from the processor (and therefore from the Python script). With an interrupt, the while loop of the script will be empty. The hardware responsible for the detection is also much more responsive than the MicroPython script.

Note

With interrupts, it is no longer necessary to constantly monitor the value of a pin: a function is automatically executed when a change is detected.

Whether it is with a timer or an external event, the interrupt is triggered when there is a signal change. Let’s discover the different possibilities 😊.

Triggering a hardware interrupt: detection modes

The detection of an event is based on the shape of the signal that arrives at the pin.

Event detection on a digital signal

Different detection modes

Note

The modes RISING and FALLING modes are the most commonly used. Note that if you use the LOW and HIGH modes, the interrupt will loop as long as the signal does not change state.

Here are the different types of interruption detection possible:

  • Pin.IRQ_LOW_LEVEL triggers the interrupt when the signal is at 0V

  • Pin.IRQ_HIGH_LEVEL triggers the interrupt when the signal is at 3.3V

  • Pin.IRQ_RISING : Triggers the interrupt when the signal goes from LOW à HIGH (From 0V to 3.3V)

  • Pin.IRQ_FALLING triggers the interrupt when the signal changes from HIGH à LOW (From 3.3V to 0V)

Configuring and using interrupts in MicroPython on the Pico

A skeleton Python script to understand

Here is a skeleton script in MicroPython that allows you to trigger an external interrupt via a signal received by your Pi Pico :

from machine import Pin

pin_button = Pin(14, mode=Pin.IN, pull=Pin.PULL_UP)

def interruption_handler(pin):
    ...

pin_button.irq(trigger=Pin.IRQ_FALLING,handler=interruption_handler)

while True:
    ...

The code uses the function Pin.irq() function to create an interrupt request from a down signal applied to the pin_button .

Note

irq stands for Interrupt Request or in French, demander une requête d’interruption. The word isr to designate the interrupt routine, i.e. the function that will be executed after the interrupt (here it is called interruption_handler() )

When an interrupt is triggered, the interruption_handler() function will automatically execute with as input argument the pin on which the event was detected.

It is recommended to write an interrupt function (isr ) as fast as possible to avoid disturbing the main program. For example, it is not recommended to send data via I2C, SPI directly from an interrupt. It is better to use flags in the form of boolean to store the detection of an event and then process it in the main loop.

Note

La gestion des interruptions en MicroPython sera toujours plus lente qu’en code Arduino ou en C pur ! Cependant, il est possible de minimiser cette latence en utilisant des paramètres avancés.

Example: Turn on an LED when a push button is pressed

Câblage sur plaque de prototypage du bouton-poussoir et de la led

Circuit électrique à réaliser

Câblage sur plaque de prototypage du bouton-poussoir et de la led

Circuit électrique à réaliser

Here is the complete script that detects when a button is pressed with an interrupt and turns on the LED accordingly:

import time
from machine import Pin

pin_button = Pin(14, mode=Pin.IN, pull=Pin.PULL_UP)
pin_led    = Pin(16, mode=Pin.OUT)


def button_isr(pin):
  pin_led.value(not pin_led.value())

pin_button.irq(trigger=Pin.IRQ_FALLING,handler=button_isr)

while True:
  ...

Note

In this example, the interrupt is triggered on a falling edge. It is possible to use the OR operator | operator to combine the modes so that the interrupt is triggered on both a rising and a falling edge:

pin_button.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING,handler=button_isr)

Here are some functions that may be useful to you:

  • irq.init() : Re-initialize the interrupt. It will be automatically reactivated.

  • irq.enable() : Enable the interrupt.

  • irq.disable() : Disable the interrupt.

  • irq() : Manually launch the call of the interrupt routine.

  • irq.flags() to know the type of event that triggered the interrupt. Can only be used in the``isr`` .

    def pin_handler(pin):
        print('Interrupt from pin {}'.format(pin.id()))
        flags = pin.irq().flags()
        if flags & Pin.IRQ_RISING:
            # handle rising edge
        else:
            # handle falling edge
        # disable the interrupt
        pin.irq().deinit()
    

To take advantage of these functions, you must use the pin variable that is attached to an interrupt, for example pin_button.irq().enable() .

You now know the basics of using interrupts in MicroPython. You may want to consult the official documentation to fully exploit their features, for example setting priorities between several interrupts that fire simultaneously. Tips and optimizations are also available in the advanced section 😎.

Use global variables to manage events in the main program

We try to limit the number of actions performed within an interrupt. It is common to increment a variable inside the ISR and perform the long tasks in the main code according to the value of this variable. Here is an example that counts the number of times you press a push button.

import time
from machine import Pin

button_pressed_count = 0 # global variable
pin_button = Pin(14, mode=Pin.IN, pull=Pin.PULL_UP)

def button_isr(pin):
    global button_pressed_count
    button_pressed_count += 1


if __name__ == "__main__":
    button_pressed_count_old = 0
    pin_button.irq(trigger=Pin.IRQ_FALLING,handler=button_isr)

    while True:
        if button_pressed_count_old != button_pressed_count:
           print('Button value:', button_pressed_count)
           button_pressed_count_old = button_pressed_count

        if button_pressed_count > 10: # heavy task here
            ...

We use a global variable to be able to write to it inside the interrupt routine.

Warning

Even if the variable is defined at the very top of the Python script, you must add the keyword global when the variable is used in a function. This tells the Python interpreter to use the global variable rather than creating a local variable (with the same name) that would only be used in the context of the function’s execution.

Result interrupt button

When we run the MicroPython script, we notice that the value of the increment is much larger than the number of presses made. Strange, isn’t it? This is due to the transitions of the logic levels at the push-button level, which are not perfect..

Improve reliability of outage detection

You have probably noticed that push buttons can give false positives: the interrupt routine then runs more times than it should. This is because the signal received by the Pico is not perfect: you get spurious pressure from the button.

bouncing effect of a push button

This is called the**bounce effect** (bounce in English). It is possible to reduce the bounce of a push-button via the Python script directly, which is called debouncing software . It consists in not taking into account the transient period between the two logical states by waiting a certain delay.

This section is available to premium members only. You still have 91% to discover.

Subscribe for only 5$/month

Already subscribed? Sign in

We use cookies to make your visit to the site as pleasant as possible. Privacy Policy