PrécédentLa photo-résistance11 / 24SuivantLes écrans OLED
3. Les microcontrôleurs

Les broches d'interruption

Les broches d'interruption

Présentation

Dans certains cas, il est souhaitable de récupérer la valeur d'une broche à tout moment du programme, même quand celui ci est occupé dans une tâche et même dans une fonction de temporisation  [1].
Pour remédier à ce problème, on peut utiliser les broches d'interruption qui permettent de récupérer la main sur l'ensemble du programme lorsque'un évènement survient sur une broche.
Concrètement, lorsque un évènement e survient sur la broche b, la fonction f est appelée, quelque soit l'état du programme principal.
Un évènement est une modification de l'état de la broche.

Évènements d'interruption

Il existe différents états pour les broches :

Chronogrammes d'interruption

Lorsqu'un évènement déterminé survient sur une broche, le code principal va se mettre en pause et la fonction spéciale sera appelée. Une fois cette fonction terminée, le code principale reprend son traitement là où il s'est arrêté.
Exemple avec mode RISING BOUTON loop EXECUTION PAUSE REPRISE onEvent INACTIVE EXECUTION INACTIVE
Exemple avec mode RISING
Exemple avec mode FALLING BOUTON loop EXECUTION PAUSE REPRISE onEvent INACTIVE APPEL INACTIVE
Exemple avec mode FALLING
Exemple avec mode CHANGE BOUTON loop EXECUTION PAUSE REPRISE PAUSE REPRISE onEvent INACTIVE EXECUTION INACTIVE EXECUTION INACTIVE
Exemple avec mode CHANGE

Les broches disponibles

Notes

  1. Voir delay(), delayMicroseconds()

Exemple avec les capteur PIR

Cette exemple est valable avec une carte Arduino.

Par exemple, on souhaite réagir prioritairement à un capteur PIR quel que soit l'état du programme.
Les capteurs PIR détectent les rayonnements infrarouges émis par un objet.
Puisque tout objet émet un rayonnement infrarouge, le capteur PIR est muni de deux cellules sensibles aux infrarouges qui vont détecter ces rayons infrarouges réfléchit ou émit par l'objet.

Un capteur PIR
Un capteur PIR
Lorsqu’il n’y a pas de mouvement, le niveau d’infrarouge reçu est le même pour les deux cellules. Lors du passage d’un objet, l’émission de ces rayons va être modifiée sur une cellule puis sur l’autre ce qui va permettre de détecter le mouvement.
Le cache blanc, qui couvre et protège généralement le capteur, est une lentille de Fresnel avec plusieurs facettes qui permet de concentrer le rayonnement infrarouge sur les cellules.

Utilisation

Ces capteurs possèdent une broche de sortie qui est mise à l'état HAUT pendent une certaine durée  [1] lorsqu'il y a détection d'un mouvement.
Diagramme temporel du capteur Présence OUT
Diagramme temporel du capteur

Nous allons présenter un code réalisé sans interruption et un autre qui utilise les interruptions.

Code PIR sans interruption

Voici un code d'exemple pour réagir dès qu'une présence est détectée.

C++

Code sans interruption

#define LED 13    //Broche de la LED
#define OUT 2     //Broche du capteur PIR
    
void setup() 

      
  pinMode(LED, OUTPUT); //LED en sortie
  pinMode(OUT, INPUT);  //Broche du capteur en entrée
  Serial.begin(9600);   //Vitesse de communication à 9600 bauds

//End setup void loop(){ outValue = digitalRead(OUT); //Lire létat du capteur if (outValue == HIGH) //Détection d'un mouvement Serial.println("Detection"); //End else }//End loop

Maintenant, supposons que la carte exécute un programme qui prend plusieurs secondes. Comment faire ?

On peut utiliser une interruption externe.

Code PIR avec interruption

C++

Code avec interruption

#define LED 13    //Broche de la LED
    #define OUT 2     //Broche du capteur PIR
       
    void setup() 
      
      Serial.begin(9600);//Vitesse de communication à 9600 bit/s
      
      pinMode(LED, OUTPUT); //LED en sortie
      pinMode(OUT, INPUT);  //Broche du capteur en entrée
      
      attachInterrupt(digitalPinToInterrupt(OUT), presence, RISING);  //Appel de la fonction presence à chaque changement de front du bouton
      Serial.println("Init");
    
    
    void loop() 
      
     delay(5000); //Pause du programme principal
      
    
    
    void presence() 
      
      Serial.println("Presence !");
    

Notes

  1. Cette durée est réglable avec le potentiomètre sur le capteur

Mise en pratique avec la carte CREPP.io

En dernier exemple, prenons le cas d'un bouton qui doit changer l'état d'une LED à n'importe quel moment du programme :

Boutons de la carte
Boutons de la carte

Branchements

Jumpers de la carte
Jumpers de la carte

Il ne faut pas oublier de mettre les jumpers sur la carte (D5, D6 et D7)

Les variables volatiles

Avant d'examiner le code, il est essentiel de comprendre l'utilité du mot-clé volatile. Dans notre exemple de contrôle d'une LED, une variable partagée sera modifiée à la fois par le programme principal et par une interruption (ISR).

Sans indication explicite, le compilateur pourrait optimiser l'accès à cette variable en la mettant en cache dans un registre, supposant que sa valeur ne change qu'au sein du flux d'exécution actuel. Or, une ISR peut modifier cette variable de manière imprévisible pour le programme principal. Pour empêcher cette optimisation et garantir que chaque accès lit toujours la valeur actuelle en mémoire, nous utilisons le mot-clé volatile.

Ainsi, le compilateur sera contraint de lire systématiquement la valeur en mémoire à chaque utilisation, assurant la cohérence des données entre le programme principal et l'interruption.

C++

Déclaration d'une variable volatile

volatile int state = LOW;

Code pour les interruptions

Mise en pratique avec une carte ESP12 hors CREPP.io

Nous souhaitons réagir de manière prioritaire à l'appui d'un bouton poussoir. Cet appui inversera l'état actuel de la LED interne (broche ).

Schéma de l'exemple

Branchement pour un ESP12
Branchement pour un ESP12

Code C++ basique

C++

Code basique avec interruption

const uint8_t buttonPin = D2;
  const uint8_t ledPin = D4;
  
  volatile bool stateLed = false; //State of LED
  
  //ISR = Interrupt Service Routine
  void IRAM_ATTR blinkLed()
  
    stateLed =! stateLed;         //Swap led state 
    digitalWrite(ledPin, stateLed);  //Update state
    Serial.println("Changing led state");
  
  
  
  void setup() 
  
  
    pinMode(ledPin, OUTPUT);      //Set led on OUTPUT
    attachInterrupt(digitalPinToInterrupt(buttonPin), blinkLed, RISING); //Start interrupt service
  
    Serial.begin(9600);
    
  
  
  
  void loop() 
  
    //Nothing in this loop...
  
  

Code C++ avancé

Dans l'exemple suivant, l'anti-rebond est fait de manière logicielle. [1]

Nous allons définir une durée pendant laquelle nous allons ignorer les fronts. Il s'agit de la période d'anti-rebond appelée DEBOUNCE\_TIME dans notre programme.

C++

La valeur de l'anti-rebond

#define DEBOUNCE_TIME 200

le principe réside dans la lecture de la variable exprimant le temps passée depuis le démarrage de la carte.

La fonction millis() renvoie le temps écoulé en milliseconde depuis le démarrage.

Ainsi, au bout d'une seconde, milli() renvoie la valeur 10000...

A chaque appel de l'ISR, on va regarder si le temps depuis le dernier évènement est supérieur à notre délai. Si c'est le cas, cela veut dire que nous pouvons mettre à jour l'état de la LED.

C++

L'anti-rebond

//Update
  previousTime = currentTime;
  currentTime = millis();

if(currentTime > (previousTime + DEBOUNCE_TIME) ) stateLed =! stateLed; //Swap led state digitalWrite(ledPin, stateLed); //Update state Serial.println("Changing led state");

Le code complet est le suivant :

C++

Code avancée pour l'anti-rebond

#define DEBOUNCE_TIME 200      //'Anti-rebond'

const uint8_t buttonPin = D2; const uint8_t ledPin = D4; volatile bool stateLed = false; //State of LED uint16_t currentTime = 0; //Current time uint16_t previousTime = 0; //ISR = Interrupt Service Routine void IRAM_ATTR blinkLed() { //Update previousTime = currentTime; currentTime = millis(); if(currentTime > (previousTime + DEBOUNCE_TIME) ) stateLed =! stateLed; //Swap led state digitalWrite(ledPin, stateLed); //Update state Serial.println("Changing led state"); } void setup() pinMode(ledPin, OUTPUT); //Set led on OUTPUT attachInterrupt(digitalPinToInterrupt(buttonPin), blinkLed, RISING); //Start interrupt service Serial.begin(9600); void loop() //Nothing in this loop...

💡

Si vous placer votre ISR après l'appel de la fonction attachInterrupt, vous aurez une erreur du compilateur car la fonction n'est pas déclarée (elle est juste définie).

Notes

  1. Opposition à la méthode hardware, faite avec un condensateur et une résistance.

[MCUQuiz]

Une interruption externe se déclenche 2 1 Via un appel de fonction 0 Via un évènement sur une broche 0 De manière aléatoire

PrécédentLa photo-résistance11 / 24SuivantLes écrans OLED