Les interruptions sur ESP32
(Mis à jour le 20/10/2022)
Une interruption est un processus qui est déclenché de manière asynchrone par un évènement extérieur, qui interrompt momentanément l’exécution du code en cours, pour exécuter du code plus critique.
À quoi ça sert ?
Imaginez que vous vouliez allumer une LED lorsque vous appuyez sur un bouton qui est relié à un pin GPIO de l’ESP32. Le plus simple est de regarder en permanence dans la fonction loop()
si vous avez appuyé sur le bouton :
const int buttonPin = 33;
const int ledPin = 2;
// Etat du bouton poussoir
int buttonState = 0;
void setup() {
Serial.begin(115200);
//Configuration du pin en entrée pullup
pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
}
void loop() {
buttonState = digitalRead(buttonPin);
if (buttonState == LOW) {
digitalWrite(ledPin, HIGH);
} else if(buttonState == HIGH){
digitalWrite(ledPin, LOW);
}
}
Le problème est que le processeur du microcontrôleur est totalement occupé par cette tâche. Alors on peut dire au microcontrôleur de faire d’autres tâches dans la loop()
, mais dans ce cas le microcontrôleur ne regardera l’état du bouton qu’une seule fois à chaque itération de la fonction loop()
. Il se peut qu’on manque un évènement. On ne peut pas traiter en temps réel des évènements extérieurs. Les interruptions permettent de détecter un évènement en temps réel tout en laissant le processeur du microcontrôleur faire d’autres tâches. Ainsi le fonctionnement d’une interruption est le suivant :
Détection d’un évènement → Interruption du programme principal → Exécution du code de l’interruption → Le processeur reprend là où il s’est arrêté.
Note
Avec les interruptions, il n’y a plus besoin de regarder en permanence la valeur d’un pin : lorsqu’un changement est détecté, une fonction est exécutée.
Les modes de détection
La détection d’un événement est basée sur l’allure du signal qui arrive au pin.
On peut choisir le mode de détection de l’interruption :
LOW
: Déclenche l’interruption dès que le signal est à 0VHIGH
: Déclenche l’interruption dès que le signal est à 3.3VRISING
: Déclenche l’interruption dès que le signal passe deLOW
àHIGH
(0 à 3.3V)FALLING
: Déclenche l’interruption dès que le signal passe deHIGH
àLOW
(3.3V à 0)CHANGE
: Déclenche l’interruption dès que le signal passe deLOW
àHIGH
ou deHIGH
àLOW
.
Note
Les modes RISING
et FALLING
sont les plus utilisés. Noter que si vous utilisez les modes LOW
et HIGH
, l’interruption se déclenchera en boucle tant que le signal ne change pas d’état.
Utilisation sur l’ESP32
L’utilisation des interruptions sur l’ESP32 est similaire à celle sur l’Arduino avec la fonction attachInterrupt()
. N’importe quel pin GPIO peut être utilisé pour les interruptions.
Ainsi pour créer une interruption sur un pin , il faut :
-
Attribuer un pin pour détecter l’interruption
attachInterrupt()
attachInterrupt(GPIOPin, fonction_ISR, Mode);
Avec
Mode
, le mode de détection qui peut êtreLOW
,HIGH
,RISING
,FALLING
ouCHANGE
-
Créer la fonction qui va être exécutée lorsque l’interruption est déclenchée
void IRAM_ATTR fonction_ISR() { // Contenu de la fonction }
Note
Il est conseillé d’ajouter le flag
IRAM_ATTR
pour que le code de la fonction soit stocké dans la RAM (et non pas dans la Flash), afin que la fonction s’exécute plus rapidement.Le code entier sera de la forme :
void IRAM_ATTR fonction_ISR() { // Code de la fonction } void setup() { Serial.begin(115200); pinMode(23, INPUT_PULLUP); attachInterrupt(23, fonction_ISR, FALLING); } void loop() { }
Dès que la tension passera de 3.3V à 0V, la fonction
fonction_ISR()
sera exécutée. On peut ensuite faire d’autres tâches dans la fonctionloop()
.
Il faut garder en tête que la fonction d’une interruption doit s’exécuter le plus rapidement possible pour ne pas perturber le programme principal. Le code doit être le plus concis possible et il est déconseillé de dialoguer par SPI, I2C, UART depuis une interruption.
Avertissement
On ne peut pas utiliser la fonction delay()
ni Serial.println()
avec une interruption. On peut néanmoins afficher des messages dans le moniteur série en remplaçant Serial.println()
par ets_printf()
qui est compatible avec les interruptions.
Le code ci-dessous affiche « Boutton pressé » lorsqu’on presse sur un bouton relié au pin 33.
void IRAM_ATTR fonction_ISR() {
ets_printf("Boutton pressé\n");
// Code de la fonction
}
void setup() {
Serial.begin(115200);
pinMode(33, INPUT_PULLUP);
attachInterrupt(33, fonction_ISR, FALLING);
}
void loop() {
}
Mini-Projet
Nous allons refaire le premier mini-projet qui consistait à faire clignoter une LED lorsqu’on appuie sur un bouton . On va utiliser les interruptions pour gérer l’événement et libérer le processeur pour qu’il puisse faire d’autres tâches.
Schéma électrique
Code
Solution
const int buttonPin = 32;
const int ledPin = 23;
int buttonState = 0;
int lastMillis = 0;
void IRAM_ATTR fonction_ISR() {
if(millis() - lastMillis > 10){ // Software debouncing buton
ets_printf("ISR triggered\n");
buttonState = !buttonState;
digitalWrite(ledPin,buttonState);
}
lastMillis = millis();
}
void setup() {
Serial.begin(115200);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin,OUTPUT);
attachInterrupt(buttonPin, fonction_ISR, CHANGE);
digitalWrite(ledPin, buttonState);
}
void loop() {
// Code ...
}