========Les interruptions sur ESP32======== [[https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/intr_alloc.html|DOC Expressif : Allocation d'interruption EN]] [[https://www.upesy.fr/blogs/tutorials/what-are-interrupts-in-esp32-with-examples-for-arduino-code|Les interruptions sur ESP32 ]] [[start:esp32:alloc_interrup|DOC Expressif : Allocation d'interruption FR]] 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 ? {{ :start:esp32:nouvelelement171.png?direct&600 |}} 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é. 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. {{ :start:esp32:capture_du_2022-12-21_19-45-57.png?direct&600 |}} ====Différents modes de détection==== On peut choisir le mode de détection de l’interruption : ***LOW** : Déclenche l’interruption dès que le signal est à 0V ***HIGH** : Déclenche l’interruption dès que le signal est à 3.3V ***RISING** : Déclenche l’interruption dès que le signal passe de LOW à HIGH (0 à 3.3V) ***FALLING** : Déclenche l’interruption dès que le signal passe de HIGH à LOW (3.3V à 0) ***CHANGE** : Déclenche l’interruption dès que le signal passe de LOW à HIGH ou de HIGH à LOW . 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.** *[[ http://fablab37110.ovh/doku.php?id=start:esp32:interruptions#broches_du_gpio_de_l_esp32_compatibles_avec_les_interruptions|Voir ICI]] 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 être LOW , HIGH , RISING , FALLING ou CHANGE 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 } 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 fonction loop() . 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. 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. {{ :start:esp32:capture_du_2022-12-21_20-03-24.png?direct&600 |}} ====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 ... } ====== Broches du GPIO de l’ESP32 compatibles avec les interruptions====== Les interruptions fonctionnent uniquement avec les entrées numériques. Les entrées numériques pouvant déclencher une interruption sont entourée d’un cercle sur le schéma ci-dessous. {{ :start:esp32:capture_du_2022-12-21_20-46-37.jpg?direct&600 |}} Attention toutefois car certaines broches passent à l’état haut (HIGH) ou émettent des signaux PWM au démarrage ou lors de la réinitialisation. D’autres broches sont utilisées par le système pour accéder à la mémoire flash ou téléverser le programme. N’utilisez pas les broches colorées en orange ou en rouge. Votre programme pourrait avoir un comportement inattendu en utilisant celles-ci. {{ :start:esp32:capture_du_2022-12-21_20-56-52.png?direct&700 |}} ====== Micropython ESP32 Interruptions ====== ====Exemple 1 de programme d'interruption avec l'ESP32==== A chaque action sur le bouton poussoir (front montant), la LED change d'état même si le programme principal est occupé à une autre tâche. {{ :start:esp32:nouvelelement190.png?direct&600 |}} ESP32 Micropython programme avec interruption sur front montant de la broche GPIO26 from machine import Pin from time import * led = Pin(25, Pin.OUT) bp = Pin(26, Pin.IN) # Le programme d'interruption def fct_interruption(pin): print("Appui sur BP detecté") if led.value()==1: led.value(0) else: led.value(1) # Spécifie la fonction à appeler lorsqu'une interruption externe survient bp.irq(trigger = Pin.IRQ_RISING, handler = fct_interruption) # Le programme principal while True: sleep(5) "bp.irq(trigger=Pin.IRQ_RISING, handler = fct_interruption)" Spécifie la fonction à appeler lorsqu'une interruption externe survient. La fonction fct_interruption serra appelée chaque fois qu'un front montant est détecté sur le bouton poussoir bp (GPIO26) ====Exemple 2 de programme d'interruption avec l'ESP32==== Fonctionnement identique à l'exemple 1 mais avec un temps d’exécution le plus court possible dans la fonction fct_interruption {{ :start:esp32:nouvelelement190.png?direct&600 |}} ESP32 Micropython programme avec interruption sur front montant de la broche GPIO26 from machine import Pin impulsion = False etat_led = False led = Pin(25, Pin.OUT) bp = Pin(26, Pin.IN) # Le programme d'interruption  def fct_interruption(pin): global impulsion impulsion = True # Spécifie la fonction à appeler lorsqu'une interruption externe survient  bp.irq(trigger = Pin.IRQ_RISING, handler = fct_interruption) # Le programme principal  while True: if impulsion: print("Appui sur BP detecte") if led.value()==1: led.value(0) else: led.value(1) impulsion = False impulsion = True La variable impulsion de type booléen passe à True quand on lance le programme d'interruption (fonction fct_interruption ) et repasse à False quand il est terminé. Cette variable est utilisée dans le programme principal pour allumer ou éteindre la Led, afficher du texte sur la console.  Cela permet d'avoir un temps d’exécution le plus court possible dans la fonction fct_interruption