Skip to content

Utilisation des interruptions en MicroPython avec une RPi Pico

Qu'est-ce qu'une interruption ?

Définition

En électronique, une interruption est un signal envoyé à un processeur qui lui indique qu’une tâche importante doit être exécutée immédiatement, interrompant ainsi le programme en cours.

Les interruptions peuvent être déclenchées de manières diverses, par exemple par un événement matériel externe, un timer interne, etc... Elles permettent de réaliser des tâches de manière asynchrone, prioritairement au programme principal.

Les interruptions en MicroPython

Définition

En pratique, les interruptions sont généralement utilisées pour :

  • Exécuter des portions de code critique lorsqu’un évènement extérieur se produit (par exemple lorsque le signal issu d'un capteur change et que cela déclenche automatiquement une fonction Python).
  • Exécuter périodiquement des fonctions (par exemple pour faire clignoter une LED toutes les 5 secondes).

Sans utiliser les interruptions, il est possible de créer un script qui allume une LED quand on appuie sur un bouton. Cependant, cette méthode a deux défauts majeurs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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 pin_button.value() == 1:
        pin_led.on()
    else:
        pin_led.off()

L'inconvénient est que le script passe son temps à surveiller la valeur de la broche pin_button pour savoir si le bouton a été pressé.

Le script ne peut pas faire grand-chose en plus.

Si le script effectue d’autres tâches dans la boucle, il risque de manquer la pression ponctuelle du bouton.

Intêret des interruptions

L’avantage d’utiliser une interruption matérielle est qu'il n’est plus nécessaire de surveiller constamment la valeur d’un pin.

Une fonction est automatiquement exécutée lorsqu’un changement est détecté.

Avec une interruption, la boucle while du script peut être vide.

Que ce soit avec un événement externe ou un timer, l’interruption est déclenchée lorsqu’il y a un changement de signal.

Déclencher une interruption matérielle : les modes de détection

Evénement déclenchant une interruption

La détection d’un événement est basée sur l’allure du signal qui arrive à la broche.

Image title

Voici les différents types de détection d’interruption possibles :

  • Pin.IRQ_LOW_LEVEL : Déclenche l’interruption lorsque le signal est à 0V
  • Pin.IRQ_HIGH_LEVEL : Déclenche l’interruption lorsque le signal est à 3.3V
  • Pin.IRQ_RISING : Déclenche l’interruption lorsque le signal passe de LOW à HIGH (de 0V à 3.3V)
  • Pin.IRQ_FALLING : Déclenche l’interruption lorsque le signal passe de HIGH à LOW (de 3.3V à 0V)

Note

Les modes RISING (front montant) et FALLING (front descendant) sont les plus couramment utilisés.

Notez que si vous utilisez les modes LOW (état bas) et HIGH(état haut), l’interruption se déclenchera en boucle tant que le signal ne change pas d’état.

Configuration et utilisation des interruptions en MicroPython sur la Pico

Squelette de script

Voici un squelette de script en MicroPython qui permet de déclencher une interruption externe via un signal reçu par la Pi Pico :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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:
    ...

Le code utilise la fonction Pin.irq() pour créer une demande d’interruption à partir d’un signal descendant appliqué sur la broche pin_button.

Note

irq signifie Interrupt Request, ou en français, demander une requête d’interruption.

On emploie également le mot isr pour désigner la routine d’interruption, c’est-à-dire la fonction qui va être exécutée suite à l’interruption (ici elle s’appelle interruption_handler())

Quand une interruption se déclenche, la fonction interruption_handler() s’exécutera automatiquement avec en argument d’entrée, le pin sur lequel l’événement a été détecté.

Il est préconisé de rédiger une fonction d’interruption (isr) la plus rapide possible afin d’éviter de perturber le programme principal.

Par exemple, il est déconseillé d’envoyer des données via I2C, SPI directement depuis une interruption. On peut plutôt utiliser des flags, sous la forme de booléen pour stocker la détection d’un événement et le traiter ensuite dans la boucle principale.

Application : Allumer une LED quand un bouton-poussoir est pressé

Objectif

Soit le schéma de câblage suivant :

Image title

Voici le script complet qui permet de détecter lorsque l’on appuie sur un bouton avec une interruption et qui allume la LED en conséquence :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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:
None      # Instruction indiquant que rien ne doit être exécuté

Exercice 8.1

Exécuter le programme précédent.

Note

Dans cet exemple, l’interruption se déclenche sur un front descendant.

Il est possible d’utiliser l’opérateur OU | pour combiner les modes et que l’interruption se déclenche à la fois sur un front montant et descendant :

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

Application : Utiliser des variables pour gérer les événements dans le programme principal

Objectif

On essaye de limiter le nombre d’actions effectuées au sein d’une interruption.

Il est courant d’incrémenter une variable à l’intérieur de l’ISR et d’effectuer les tâches longues dans le code principal en fonction de la valeur de cette variable.

Voici un exemple qui compte le nombre de fois que vous appuyez sur un bouton-poussoir.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import time
from machine import Pin

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

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

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

On utilise une variable globale pour pouvoir écrire dans celle-ci à l’intérieur de la routine d’interruption.

Avertissement

Même si la variable est définie tout en haut du script Python, vous devez ajouter le mot-clé global lorsque la variable est utilisée dans une fonction. Cela permet d’indiquer à l’interpréteur Python d’utiliser la variable globale plutôt que de créer localement une variable (avec le même nom) qui serait utilisée uniquement dans le contexte d’exécution de la fonction.

Image title

Exercice 8.2

Exécuter le programme précédent.