6.8 Interruption

Qu’est-ce qu’une interruption ?

C’est ce qui nous arrive à tous dans notre vie de tous les jours. Imaginez que vous soyez au téléphone et que l’on sonne à votre porte. Vous demandez à votre interlocuteur de patienter, vous posez votre téléphone puis vous allez ouvrir. C’est le facteur qui vous apporte un colis. Une fois celui-ci réceptionné et la porte refermée, vous retournez prendre votre téléphone pour continuer votre conversation. L’interruption (sonnerie) vous a fait vous détourner de votre programme principal (conversation téléphonique) pour aller réaliser un sous-programme (ouvrir la porte) puis vous avez repris votre programme principal là où vous l’aviez laissé (si vous n’avez pas perdu le fil de votre conversation !).


Un microcontrôleur fonctionne de la même façon : un événement interne (timer) ou bien externe (broche) demande une interruption au microcontrôleur (voir aussi « Les interruptions (Arduino) »). Celui-ci va s’interrompre dans son programme principal, puis va exécuter un sous-programme (on parle de routine d’interruption). Une fois que cette routine est exécutée, le microcontrôleur reprend son programme principal, là où il s’était arrêté, et continue son exécution. Pour cela, il a fallu que le microcontrôleur sauve différents registres sur une pile et les rétablisse en fin de routine d’interruption. Il faut aussi que le programmeur ait autorisé les interruptions ; si le programme doit réaliser une tâche selon une chronologie très rigoureuse, il peut être nécessaire de ne pas les autoriser pour ne pas déranger le microcontrôleur et c’est donc au programmeur de décider d’autoriser ou non les interruptions. La fonction sei() permet d’autoriser les interruptions alors que la fonction cli() permet de les ignorer ; on peut aussi utiliser les fonctions interrupts() et noInterrupts() du langage Arduino.


Sous Flowcode



Sous Flowcode c'est  le pictogramme   qu'il faut utiliser

Aide: https://www.flowcode.co.uk/wiki/index.php?title=Interrupt_Icon_Properties  


Introduction aux interruptions sous Flowcode

Les interruptions sont appelées pour réagir à un événement tel qu'un stimulus externe ou un événement de synchronisation.

Lorsqu'une interruption est activée, elle exécute une macro. La macro à appeler est spécifiée par l'utilisateur lors de la création de l'interruption.


Le nombre et les types d'interruptions disponibles dépendent de l'appareil utilisé. Certains appareils ont un certain nombre d'interruptions alors que d'autres n'en ont que quelques-unes.


Les détails exacts, les propriétés et le fonctionnement d'une interruption varient d'une interruption à l'autre. Les utilisateurs devront se référer aux boîtes de dialogue d'interruption pour les détails exacts d'une interruption particulière. Cependant, il existe quatre types principaux utilisés par Flowcode :


    • TMR<x> - débordement - réagit à l'événement de débordement cadencé du temporisateur intégré x.
    • INT - réagit à un changement d'entrée sur une broche d'interruption externe spécifique.
    • IOC (Interrupt On Change) - réagit à tout changement sur une collection spécifiée d'entrées
    • Custom - définit une procédure d'interruption personnalisée (Utilisateur).


Cliquez ici pour quelques exemples de programmes utilisant des interruptions.


Exemple d'une utilisation de l'interruption IOC (pour un Arduino Uno 328P)

Dans cet exemple nous allons activer l'interruption IOC du Port B, plus précisément des lignes PB4, PB3, PB2 et PB1

Toute modification de ces lignes (front montant ou descendant) déclenchera la routine d'interruption appelée ici "IOC_Interrupt"


Voir l' activation de l'interruption



Dans cette routine d'interruption, nous allons comptabiliser les fronts montants sur PB1, PB2, PB3 et PB4, Il faut donc:


lire le Port B.

PORTB -> .NewPort   // (.NewPort est une variable locale)


ensuite on regarde si PB1 a changé

if (.NewPort & 0x02) != (Old_Port & 0x02)   // (&0x02 masque ET avec 0b00000010)

si Oui alors 

si PB1=1 il sagit d'un front montant sur PB1

if (.NewPort & 0x02)

 si Oui alors

 on incrémente le compteur de PB1

 CountRB1 = CountRB1 + 1


on fait de même pour PB2, PB3 et PB4 en adaptant le masque ET


  • PB2 ->  (&0x04 masque ET avec 0b00000100


  • PB3 ->  (&0x08 masque ET avec 0b00001000


  • PB4 ->  (&0x10 masque ET avec 0b00010000


Voir la simulation sur Flowcode


Simulation sous Proteus 8



 Ensemble des ficiers de cet exemple:  Flowcode_328P_IOC_Pulse_Counter.rar

Sous Arduino

Les interruptions (Arduino)

La façon dont un Arduino se programme a été présentée dans « La structure d'un programme ». Vous savez donc que le logiciel qui vient avec l’Arduino appelle une fois la fonction setup() puis appelle ensuite fonction loop() de manière répétitive.


Généralement, pour mettre en œuvre un système, on va, dans loop(), lire des entrées de l’Arduino, qu’elles soient analogiques ou numériques, exécuter un certain nombre de calculs en fonction de ces entrées puis modifier l’état du système, ce qui aboutira éventuellement à positionner des sorties.


Ces lectures se font donc de manière répétitive. On appelle ceci de la scrutation ou polling en anglais. La période de scrutation dépend du temps que loop() met à s’exécuter et peut d’ailleurs être variable si loop() contient des instructions conditionnelles qui font que d’une itération à l’autre de loop() le nombre d’instructions exécutées n’est pas le même.


Dans certaines circonstances la scrutation n’est pas adaptée car l’entrée qui est scrutée peut changer plusieurs fois de valeur pendant la période de scrutation. Dans ce cas ces changements de valeur ne sont pas vus par le logiciel et le système ne fonctionne pas correctement.


La solution réside dans l’utilisation des interruptions.

Principe de fonctionnement des interruptions

On a vu que l’Arduino exécutait son programme en séquence, instruction par instruction, dans l’ordre. Certaines instructions permettent de rompre cet ordre : les instructions conditionnelles comme le if ... else ou les instructions de boucle comme le for(...). Mais l’Arduino reste sur ses rails et respecte les instructions qui lui sont données.

Une interruption, comme son nom l’indique, consiste à interrompre momentanément le programme que l’Arduino exécute pour qu’il effectue un autre travail. Quand cet autre travail est terminé, l’Arduino retourne à l’exécution du programme et reprend à l’endroit exact où il l’avait laissé.

Cet autre travail s’appelle le programme d’interruption ou la routine d’interruption ou encore une ISR pour Interrupt Service Routine en anglais.

Il est très important que les ISR aient un temps d’exécution le plus court possible. On ne fera donc aucun calcul compliqué et aucun appel à des fonctions longues comme un affichage sur un écran LCD.

Il existe plusieurs sources d’interruption, on dit également vecteur d’interruption, sur l’AVR ATmega 328 qui équipe l’Arduino Uno, 26 au total. Nous n’allons pas toutes les passer en revue. Nous allons seulement nous concentrer sur celles que nous pouvons utiliser dans nos sketchs. Il s’agit des 5 interruptions liées au changement de la tension présente sur une broche numérique qui sont donc ce que l’on appelle des interruptions externes. Les interruptions liées au timers, des compteurs internes à l’Arduino qui permettent de compter le temps de manière précise sont également intéressantes et feront l’objet d’un autre article.

Que se passe-t-il si une nouvelle interruption survient alors que l’ISR déclenchée par la précédente n’est pas terminée ?

Une ISR n’est pas interrompue par une nouvelle interruption. La nouvelle interruption ne sera prise en compte que lorsque l’ISR en cours se terminera.

Le corollaire est qu’il ne faut pas appeler de fonctions qui se mettent en attente d’une interruption à partir d’une ISR. Comme l’interruption attendue ne peut pas déclencher une nouvelle ISR, la fonction attendra indéfiniment et tout le système se bloquera. C’est ce que l’on appelle un deadlock.

Les fonctions de Serial qui permettent d’afficher, via la ligne série et l’USB dans le moniteur série font exactement cela. Leur appel à partir d’une ISR est donc interdit.

Que se passe-t-il si plusieurs interruptions surviennent en même temps ?

Les Interruptions ont chacune une priorité. Par exemple, les interruptions externes sont plus prioritaires que les interruptions des Timers. L’Arduino exécutera les ISR dans leur ordre de priorité. L’ordre de priorité est donné dans la table ci-dessous. La source d’interruption située la plus en haut de la table est la plus prioritaire.

Source

Rôle

INT0

Interruption externe sur la broche 2

INT1

Interruption externe sur la broche 3

PCINT0

Interruption externe sur les broches 8 à 13

PCINT1

Interruption externe sur les broches A0 à A5

PCINT3

Interruption externe sur les broches 0 à 7

Les interruptions externes INT0 et INT1

Ces deux interruptions sont les plus faciles à utiliser. INT0 correspond à la broche 2 des Arduino a base d’AVR ATmega 328 et INT1 correspond à la broche 3. Pour accrocher une routine d’interruption à un état ou un changement d’état de l’une de ces broches on va utiliser la fonction attachInterrupt(...).

Cette fonction prend 3 arguments : le numéro d’interruption externe, la fonction à appeler quand l’interruption survient et enfin la condition selon laquelle l’interruption survient. Son prototype est le suivant :


attachInterrupt(numero, ISR, mode);

Où numero est le numéro de l’interruption externe. Il s’agira de 0 ou 1 sur un Arduino Uno/Nano, ce qui correspond respectivement aux broches 2 et 3. Sur un Arduino Mega, les numéros 0 à 5 seront possibles et correspondent, dans l’ordre, aux broches 21, 20, 19, 18, 2 et 3.

Mais on préférera l’appeler en utilisant la fonction d’aide digitalPinToInterrupt qui prend comme argument le numéro de broche et renvoie le numéro d’interruption. Ceci permet de ne pas avoir à se référer à la documentation pour retrouver à quelle broche correspond une interruption. L’appel de attachInterrupt devient donc :


attachInterrupt(digitalPinToInterrupt(broche), ISR, mode);

Détaillons ses arguments.

  • broche est la broche correspondant à l’interruption externe. C’est à dire 2 ou 3 sur un Arduino Uno/Nano et 2, 3, 18, 19, 20 et 21 sur un Arduino Mega.
  • ISR est la routine d’interruption qui sera appelée lorsque l’interruption surviendra. Cette routine d’interruption est une fonction qui ne renvoi rien et qui ne prend aucun argument, comme ceci : void maRoutine() { ... }.
  • mode indique ce qui va provoquer l’interruption. Les valeurs possibles pour mode sont :
    • LOW : l’interruption est déclenchée quand la broche concernée est LOW. Comme il s’agit d’un état et non d’un événement, l’interruption sera déclenchée tant que la broche est LOW. Par conséquent, dès que l’ISR aura terminé son exécution, elle la recommencera. Pendant ce temps, loop() ne sera pas exécuté.
    • CHANGE : l’interruption est déclenchée quand la broche concernée change d’état, c’est à dire passe de LOW à HIGH ou bien de HIGH à LOW. Il s’agit d’un événement.
    • RISING : l’interruption est déclenchée quand la broche concernée passe de LOW à HIGH. Il s’agit également d’un événement.
    • FALLING : l’interruption est déclenchée quand la broche concernée passe de HIGH à LOW. Il s’agit encore d’un événement.

Les modes de déclenchement sont le reflet direct des capacités du matériel et à ce titre permettent la meilleure réactivité. Ayez tout de même à l’esprit qu’il s’écoule presque 3μs entre le déclenchement de l’interruption et l’exécution de la première instruction de la routine d’interruption.

Supposons que l’on veuille provoquer l’exécution de la routine d’interruption maRoutine lorsque le signal sur la broche 2 passe de HIGH à LOWmaRoutine est définie comme suit :


/* Change l'état de la LED embarquée sur la carte */void maRoutine(){ digitalWrite(LED_BUILTIN, ! digitalRead(LED_BUILTIN));}

On appellera attachInterrupt de la manière suivante :


attachInterrupt(digitalPinToInterrupt(2), maRoutine, FALLING);

Une seconde fonction, detachInterrupt(...) permet de déconnecter la routine d’interruption de la source d’interruption. Son prototype est le suivant :


detachInterrupt(numero);

Où numero est le numéro d’interruption, 0 ou 1 sur un Arduino Uno, 0 à 5 sur un Arduino Mega.

Ici aussi, il est préférable d’utiliser digitalPinToInterrupt pour se dispenser d’avoir à se rappeler de la correspondance broche → numéro d’interruption. Il est donc recommandé d’appeler detachInterrupt de la manière suivante :


detachInterrupt(digitalPinToInterrupt(broche));

Les interruptions externes PCINT0, PCINT1 et PCINT2

Ces interruptions externes offrent moins de possibilités que celles que nous avons vues au paragraphe précédent.

Tout d’abord, les 3 sources PCINT0, PCINT1 et PCINT2 sont partagées par plusieurs broches. PCINT0 correspond aux interruptions externes sur les broches 8 à 13, PCINT1 aux interruptions externes sur les broches A0 à A5 et PCINT3 aux interruptions externes sur les broches 0 à 7. On peut noter que les broches 2 et 3 sont donc partagées entre PCINT3 et INT0 et INT1. On ne peut donc à la fois bénéficier de INT0 et INT1 et des interruptions PCINT3 sur les broches 2 et 3

Comme la même ISR est exécutée pour toutes les broches qui partagent sa source d’interruption, la discrimination de la broche qui a engendré l’interruption, ou des broches en cas de changements simultanés, doit être faite dans la routine d’interruption.

Ensuite, la richesse des modes de déclenchement de INT0 et INT1 n’existent pas pour les sources PCINTx. Le seul mode est l’interruption sur changement d’état, LOW vers HIGH et HIGH vers LOW. Par conséquent si seul l’un des changements d’état intéresse le programme, il faut également discriminer en allant lire l’état de la broche qui a déclenché l’interruption.

Heureusement il existe une bibliothèque qui gère toutes ces situations et qui permet, d’une part, de retrouver une souplesse et une richesse comparable à celle de INT0 et INT1 et, d’autre part, de masquer la complexité du matériel.

Il faut toutefois garder à l’esprit que, comparé aux interruptions INT0 et INT1, l’usage des interruptions PCINTx conduira à une exécution plus fréquente de l’ISR et à une réactivité moindre puisque la bibliothèque prendra du temps pour déterminer la broche source et le type de changement avant d’appeler votre fonction. On privilégiera donc les premières. Le corollaire est que la fonction que vous écrivez n’est pas directement une ISR mais une fonction appelée par l’ISR. En effet, il n’y a que 3 ISR, une pour chacune des trois sources PCINT0, PCINT1 et PCINT2, mais vous pouvez définir autant de fonctions qu’il y a de broches grâce à la bibliothèque.

La bibliothèque PinChangeInt

Avant d’utiliser cette bibliothèque, il faut l’installer. L’installation est classique. Comme d’habitude, il faut veiller à ce que les noms des dossiers soient cohérents. Pour ma part, j’ai installé la version 2.19beta que l’on trouve ici : http://code.google.com/p/arduino-pi.... Une fois l’archive zip décompressée, donnant un dossier nommé pinchangeint-v2.19beta. À l’intérieur, vous trouverez un dossier PinChangeInt qu’il faut déplacer dans le dossier bibliothèque de votre dossier de travail. Relancez ensuite l’IDE Arduino.

La bibliothèque fournit les fonctions :


PCintPort::attachInterrupt(pin, ISR, mode);

On voit que alors que les fonctions attachInterrupt et detachInterrupt relatives à INT0 et INT1 prennent comme premier argument le numéro de l’interruption, PCintPort::attachInterrupt et PCintPort::detachInterrupt prennent comme premier argument le numéro ou l’identifiant de la broche. Les autres arguments sont identiques.

et


PCintPort::detachInterrupt(pin);

où l’unique argument est la broche.

Un exemple est fourni dans le programme ci-dessous où les broches auxquelles les routines d’interruption (ISR) sont attachées sont A1, A2, A3 et A4. Une exploration plus détaillée de cette bibliothèque fera l’objet d’un futur article.

Un exemple d’utilisation des interruptions PCINTx est la surveillance d’un grand nombre de pédales qui sont fréquemment mises en œuvre par des ILS (Interrupteur à lames souples). Voici par exemple un programme test qui gère 4 ILS. Les ILS sont connectés sur les entrées A1 à A4 programmées en entrées numériques avec pull-up. Voici une implantation sur breadboard de ce montage très simple.

PNG - 230.5 ko

Le programme installe 4 fonctions qui seront appelées par les ISR des vecteurs PCINTx, une pour chaque ILS. Les ILS tirant l’entrée à 0V quand ils collent, le mode de déclenchement choisi est FALLING. Chaque fonction incrémente une variable. Il faut toujours que les fonctions exécutées lors d’une interruption soient les plus courtes possibles.

Les ILS étant des dispositifs mécaniques, ils engendrent des rebonds quand ils collent ou décollent. C’est à dire que, au lieu de générer un seul FALLING, ils vont en générer plusieurs. La réactivité des ISR est grande, elles sont capables de capturer des événements qui se succèdent toutes les 10µs environ, Les rebonds sont plus lents, on est dans la centaine de µs. Il est donc nécessaire de filtrer les déclenchements trop rapprochés qui correspondent aux rebonds. La manière habituelle de faire consiste à enregistrer la date de déclenchement et, quand une seconde ISR survient, de n’en tenir compte que si le temps écoulé depuis la dernière date est supérieur à un seuil. 1ms permet de gommer les rebonds tout en conservant une bonne réactivité.

Voici par exemple la fonction correspondant à un déclenchement sur le premier ILS :

void interruptILS1(){
 static unsigned long dateDernierChangement = 0
 unsigned long date = millis();

 if ((date - dateDernierChangement) > dureeAntiRebond) {
  comptageILS1++;

 dateDernierChangement = date;
 }

}

Afin de visualiser l’activation des ILS, un Afficheur LCD (LCDKeypad shield) est employé. Dans loop(), le programme affiche, toutes les 20 ms, la valeur des 4 variables mémorisant le nombre de déclenchements sur chaque ILS.

#include <LiquidCrystal.h>
#include <LCDKeypad.h>
#include <PinChangeInt.h>
LCDKeypad lcd;
const byte pinILS1 = A1;
const byte pinILS2 = A2;
const byte pinILS3 = A3;
const byte pinILS4 = A4;
byte comptageILS1 = 0;
byte comptageILS2 = 0;
byte comptageILS3 = 0;
byte comptageILS4 = 0;
const unsigned long dureeAntiRebond = 1;


void interruptILS1()
{
  static unsigned long dateDernierChangement = 0;
  unsigned long date = millis();
  if ((date - dateDernierChangement) > dureeAntiRebond) {
    comptageILS1++;
    dateDernierChangement = date;
  }
}

 
void interruptILS2()
{
  static unsigned long dateDernierChangement = 0;
  unsigned long date = millis();
  if ((date - dateDernierChangement) > dureeAntiRebond) {
    comptageILS2++;
    dateDernierChangement = date;
  }
}

 
void interruptILS3()
{
  static unsigned long dateDernierChangement = 0;
  unsigned long date = millis();
  if ((date - dateDernierChangement) > dureeAntiRebond) {
    comptageILS3++;
    dateDernierChangement = date;
  }
}
 
void interruptILS4()
{
  static unsigned long dateDernierChangement = 0;
 
  unsigned long date = millis();
  if ((date - dateDernierChangement) > dureeAntiRebond) {
    comptageILS4++;
    dateDernierChangement = date;
  }
}


void setup()
{
  lcd.begin(16, 2);
  lcd.clear();
  lcd.print("Test PCINT");
 
  /*
   * Programme les broches des ILS en entree
   * et active le pullup interne
   */
  pinMode(pinILS1, INPUT_PULLUP);
  pinMode(pinILS2, INPUT_PULLUP);
  pinMode(pinILS3, INPUT_PULLUP);
  pinMode(pinILS4, INPUT_PULLUP);
 
  /*
   * accroche les ISR aux pins
   */
  PCintPort::attachInterrupt(pinILS1, interruptILS1, FALLING);
  PCintPort::attachInterrupt(pinILS2, interruptILS2, FALLING);
  PCintPort::attachInterrupt(pinILS3, interruptILS3, FALLING);
  PCintPort::attachInterrupt(pinILS4, interruptILS4, FALLING);
}
 
void loop()
{
  lcd.setCursor(0,1);
  lcd.print(comptageILS1);
 
  lcd.setCursor(4,1);
  lcd.print(comptageILS2);
 
  lcd.setCursor(8,1);
  lcd.print(comptageILS3);
 
  lcd.setCursor(12,1);
  lcd.print(comptageILS4);
 
  delay(20);  
}

Voici une vidéo montrant cette application de test en action. (site  "locoduino"=

L’interruption du timer 2

Toutes les demandes d’interruption arrivent sur une porte OU et sont éventuellement bloquées par le signal GIE (Global Interrupt Enable) activé par la fonction sei(). La figure suivante nous permet de comprendre comment le timer 2 peut faire une demande d’interruption.

Architecture de pilotage d&apos;un timer


Architecture de pilotage d’un timer

Ce circuit se trouve à l’intérieur du microcontrôleur qui équipe le module Arduino Uno.


La figure se lit de la droite vers la gauche : le signal d’horloge de 16 MHz arrive au prédiviseur qui divise la fréquence en fonction des bits 0 à 2 du registre TCCR2B (voir la première partie de l’article). Le timer TCNT compte ainsi moins rapidement. Lorsqu’il déborde, une bascule est positionnée à 1 : c’est le flag TOV2 que l’on retrouve dans le bit 0 du registre TIFR2. Pour autoriser une interruption par le timer, il faut que le flag TOIE2 (Timer/Counter2 Overflow Interrupt Enable) qui est le bit 0 du registre TIMSK2 (Timer/Counter2 Interrupt Mask Register), soit à 1 ; c’est ce qu’on appelle une autorisation d’interruption locale. Pour que notre timer déclenche une interruption, il faut qu’il y soit autorisé ( TOIE2 à 1) ET qu’il déborde ( TOV2 à 1), ces deux conditions arrivant sur une porte ET en amont de la porte OU. Mais comme on l’a dit plus haut, il faut aussi que les interruptions soient autorisées de façon générale : c’est le rôle du flag GIE , le bit 7 du registre SREG (Status Register) qui alimente une porte ET en aval de la porte OU.


Si toutes les conditions que nous venons de voir sont réunies, l’interruption arrive au microcontrôleur qui va donc interrompre son programme principal pour aller exécuter sa routine d’interruption (ISR pour Interrupt Routine Service). Le système Arduino a prévu une routine au nom réservé qu’il suffit de compléter : celle-ci commence par ISR(TIMER2_OVF_vect) signifiant qu’il s’agit d’une routine d’interruption déclenchée par le vecteur d’overflow du timer 2. Cette routine remet automatiquement à 0 le flag TOV2 , ce qu’il fallait faire soi-même lorsqu’on surveillait le flag sans faire appel aux interruptions (programme de la première partie de l’article).


Programme par interruption

Nous allons donc modifier le programme vu dans la première partie pour que l’inversion de la DEL se fasse par une interruption provenant du timer 2. Les autres conditions restent identiques : initialisation, réglage du prédiviseur (voir le précédent programme). La boucle principale peut accueillir le programme de l’utilisateur ; à titre d’exemple, nous avons mis un programme très classique faisant clignoter une autre DEL (branchée sur la broche 5 d’Arduino) en utilisant la fonction delay(). Bien que cette fonction soit bloquante, cela n’empêche pas la DEL reliée à la broche 13 de clignoter.


Si vous avez bien compris le premier programme, donné dans l’article Les Timers (I), il n’y a aucune difficulté à comprendre celui-ci.


Conclusion

Utiliser les interruptions générées par les timers permet de laisser le programme principal s’occuper d’une autre tâche. Il faut bien sûr autoriser les interruptions d’une façon générale en positionnant à 1 le flag GIE (bit 7 du registre SREG) par la fonction sei() par exemple. Il faut également autoriser les interruptions en provenance du timer (flag TOIE du registre TMSK). Lorsque le timer déborde, le timer génère l’interruption et le programme exécute la routine d’interruption liée à ce débordement. Le flag TOV est alors automatiquement remis à 0 et il suffit alors de réinitialiser le timer avec la valeur adéquate pour son comptage. Le compilateur de l’environnement de développement d’Arduino connaît le nom des routines d’interruptions, ainsi que le nom des registres et des différents flags, ce qui simplifie bien le travail du programmeur. Dans un prochain article, nous verrons comment utiliser d’autres registres liés aux timers.


Simulation sous Proteus

Exemple: Arduino_328P_Timer_Ex_01.pdsprj

     /*
      Timer et interruptions
    */
     
    const byte Led1 = 15; // LED1 du module
    const byte Led0 = 14; // LED0 du module
       
    #define Led1Toggle digitalWrite (Led1, !digitalRead(Led1))
    #define Led0Toggle digitalWrite (Led0, !digitalRead(Led0))    

    byte Compteur = 0;  //Compteur pour interruption
     
    void setup(){
         pinMode(Led1, OUTPUT);    
         pinMode(Led0, OUTPUT);         
      // Initialise le Timer 2 pour déclencher les interruptions à intervalle régulier
      TCCR2A = 0; //default 
      TCCR2B = 0b00000110; // clk/256 est incrémenté toutes les 16µs  
      TIMSK2 = 0b00000001; // TOIE2 
      sei();               // autorise les interruptions
     
    }
     
    // Boucle principale
    void loop() {
    
    }
     
    // routine d'interruption du timer
    ISR (TIMER2_OVF_vect) 
    {  
      // 256-6 --> 250x16µs = 4ms  
      // Recharge le timer pour que la prochaine interruption se déclenche dans 4ms
      TCNT2 = 6;
      if (Compteur== 50) { Led0Toggle; } //50x4mS = 0.2s - la Led0 est allumée 0.2s et éteinte 0.2s
      if (Compteur++ == 100) {    //100x4mS = 0.4s - la Led1 est allumée 0.4s et éteinte 0.4s
          Compteur=0;  
          Led1Toggle;Led0Toggle;
      }  
    } 



Les fonctions en langage C permettant de gérer les interruptions du microcontrôleur sont :

External Interrupts

attachInterrupt()
detachInterrupt()


Interrupts

interrupts()           sei()
noInterrupts()         cli()

Créé avec HelpNDoc Personal Edition: Optimisez sans effort votre site Web de documentation pour les moteurs de recherche