LOCODUINO

Les interruptions

Les interruptions (1)

.
Par : Jean-Luc

DIFFICULTÉ :

La façon dont un Arduino se programme a été présentée dans « La programmation, qu’est ce que c’est » et les articles suivants de la série. Vous savez donc que le logiciel qui vient avec l’Arduino appelle une fois la fonction setup() puis appelle ensuite 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.

Article mis à jour le 18 septembre 2021 pour prendre en compte les évolutions de l’API du logiciel Arduino.

Principe de fonctionnement des interruptions

On a vu dans « La programmation, qu’est ce que c’est » 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.

SourceRô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(numéro, ISR, mode);

numéro 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 renvoit 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 l’ISR.

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 à LOW. maRoutine 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 l’ISR de la source d’interruption. Son prototype est le suivant :

detachInterrupt(numero);

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));

Dominique a présenté dans L’Arduino et le système de commande numérique DCC la façon dont l’information est transmise en DCC et notamment le fait que les bits d’une trame DCC étaient codés via le temps qui s’écoule entre deux fronts descendants du signal. La façon adéquate est donc de connecter le signal DCC, une fois mis en forme entre 0 et 5v, ou 0 et 3,3v pour un Arduino a base de micro-contrôleur ARM, sur une entrée d’interruption externe et d’attacher une ISR dont le mode est FALLING. À chacune de ses exécutions, l’ISR mesurera le temps écoulé depuis son exécution précédente et déterminera ainsi la valeur du bit qui vient d’être transmis.

Deux broches d’interruptions externes ne permettent pas de mettre en œuvre des systèmes de grande taille. Heureusement, toutes les broches numériques de l’Arduino Uno peuvent servir comme broches d’interruptions externes [1].

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 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. 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 kio

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éner 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 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.

[1C’est également le cas pour les autres Arduino à base d’AVR ATMega 328 et pour les Arduino à base de micro-contrôleurs ARM. Ce n’est pas le cas des Arduino à base d’ATMega 32u4 comme le Leonardo ou le Pro

77 Messages

  • Les interruptions (1) 27 décembre 2014 17:50, par Vauban38

    Bonsoir,

    Deux incompréhensions de ma part, après lecture de cet article :

    • Dans le détail des arguments d’attachInterrupt il est indiqué "numéro est le numéro de la broche d’interruption concernée. Il s’agira de 0 ou 1.." ne s’agit-il pas simplement du numéro d’interruption, au même titre que dans detachInterrupt ?
    • Les broches 2 & 3 sont indiquées correspondre à INT0 et INT1, et plus loin il est indiqué que PCINT3 correspond aux broches 0 à 7. Quid alors des 2 & 3 ?

    Désolé pour ces questions peut-être saugrenues, mais j’essaye de comprendre ! ;-)
    En tout cas bravo pour vos articles.

    Répondre

    • Les interruptions (1) 28 décembre 2014 10:32, par Jean-Luc

      Ce ne sont pas des questions saugrenues et les commentaires attachés aux articles sont là pour les poser et améliorer les articles :). Effectivement la façon dont le numéro de broche est présenté n’est pas clair. Je corrige.

      PCINT3 et INT0/INT1 se chevauchent. C’est à dire que les broches 2 et 3 de l’Arduino peuvent être soit utilisées en INT0 et INT1 soient utilisée avec PCINT3. À noter que la bibliothèque PinChangeInt permet de masquer la cuisine du matériel sous-jacent et je m’aperçois donc que j’ai dit une bêtise. Je complète et améliore l’article en ce sens.

      Répondre

      • Les interruptions (1) 28 décembre 2014 12:19, par Vauban38

        Merci pour ces précisions qui répondent totalement à mes interrogations.

        Répondre

        • Les interruptions (1) 8 mars 2018 21:28, par Mat13

          super article ! merci

          pour ne pas se mélanger les pinceaux entre numéro d’interruption et numéro de pin, la bonne pratique est d’utiliser digitalPinToInterrupt()
          attachInterrupt (digitalPinToInterrupt(n° pin), fonction_ISR, FALLING ) ;

          Répondre

          • Les interruptions (1) 9 mars 2018 16:34, par Jean-Luc

            Merci :-)

            Oui, il faudrait que je mette à jour avec ces informations ; l’article étant antérieur à l’apparition de digitalPinToInterrupt :)

            Répondre

  • Les interruptions (1) 2 janvier 2015 22:43, par RailyRabbit

    Hello,

    La déclaration suivante à l’entrée des fonctions Void me laisse perplexe :

    static unsigned long dateDernierChangement = 0 ;

    La valeur est systématiquement à 0 dès qu’on entre dans la fonction ? Je comprends l’utilisation de millis(), mais pas l’organisation du code :(

    Répondre

    • Les interruptions (1) 2 janvier 2015 23:31, par Jean-Luc

      Bonsoir,

      static utilisé pour préfixer une déclaration de variable à l’intérieur d’une fonction signifie que cette variable n’est connue que de la fonction mais se comporte comme une variable globale. C’est à dire que son initialisation n’est faite qu’une seule fois avant le commencement de l’exécution du programme et que la variable retient sa valeur d’une exécution à l’autre de la fonction.

      Répondre

      • Les interruptions (1) 5 janvier 2015 17:20, par RailyRabbit

        Merci pour cette précision ! ;)

        Répondre

      • Les interruptions (1) 13 janvier 2015 19:26, par Gwadavel

        Normalement ce n’est pas "volatile" qu’on utilise quand une variable peut être changé par une interruption ?

        Répondre

        • Les interruptions (1) 13 janvier 2015 20:44, par Jean-Luc

          Bonsoir,

          Effectivement, volatile est généralement utilisé pour les variables partagées entre une interruption et le programme principal. volatile force le compilateur à aller chercher la variable en mémoire à chaque fois qu’elle est utilisée. De cette manière la variable en mémoire est toujours à jour. En l’absence de volatile, le compilateur pourrait produire un code où une variable partagée est copiée dans un registre et la variable en mémoire n’est jamais mise à jour dans une partie du programme. Et, par exemple, des modifications faites dans le programme principal ne serait pas vues par la routine d’interruption.

          Mais, une variable globale non volatile est toujours lue de la mémoire au début d’une fonction et écrite dans la mémoire à la fin. Donc, dans le programme présenté, les ISR mettent à jour leur variable comptageILSx à la fin de la routine et loop() relit systématiquement les variables comptageILSx au début. volatile n’est donc pas nécessaire ici. Si par contre, dans loop(), on encadrait les affichages dans un while(1){}, l’exécution du programme principal resterait entièrement contenu dans loop() et, très probablement, les variables comptageILSx ne seraient pas relues et le LCD n’afficherait pas les bonnes valeurs.

          volatile fera l’objet d’un prochain article.

          Répondre

  • Les interruptions (1) 13 janvier 2015 20:54, par Gwadavel

    Merci pour votre réponse

    Répondre

  • Les interruptions (1) 6 mars 2016 21:06, par ekiki

    Super.
    C’est un vieil article mais j’ai bien aimé la présentation et les explications.
    Sympa également de laisser les commentaires et les réponses associées.
    Je me penche sur les interruptions, et, pour une fois, j’ai l’impression d’avoir compris.
    Y’a plus qu’a....

    Répondre

  • Les interruptions (1) question ILS anémometre 25 mars 2016 15:51, par Clement

    Bonjour, ce sujet date un peu mais j’espère que vous trouverez mon message.
    Je suis en terminale sti2d et j’étudie un anémomètre (je précise que je débute en programmation).
    Je suis actuellement bloqué pour une histoire de timer.
    Lorsque l’anemometre va tourner il va activer l’ILS, le programme va alors entrer dans la boucle compteur.
    Cependant je cherche un moyen de compter le nombre de fois que l’ILS va s’activer pendant une seconde. Pour cela il me faut enclencher un timer lorsque le programme entre dans la boucle compteur et incrémenter jusqu’a ce que le timer atteigne une seconde. Le problème étant que a chaque interruption la boucle recommence donc le timer se reset a chaque fois. Merci d’avance.
    voici la datasheet de l’anémomètre ainsi que le code que j’ai réaliser pour le moment. J’ai besoin d’aide merci.
    datasheet : https://www.sparkfun.com/datasheets...

    int anemometre = 3;           //pin pour compter le nombre d'impulsion 
    int compt = 0;                    //fonction pour compter le nombre d'impulsio
    float vitesse = 0;                //vitesse du vent
    float valeur = 2.4;              //2,4 km/h par activation de l'ILS par seconde
    
    void setup()
    {
      Serial.begin(9600);
      attachInterrupt(digitalPinToInterrupt(3),compteur,RISING); //fonction pour compter le nombre d'interruption
    }
    
    
    void loop()
    {
      Serial.println("ca tourne pas");  
    }  
    
    void compteur()
    {
     static unsigned long temps_dernier = 0;
     unsigned long temps = millis();
     
        while ((temps - temps_dernier) <= 1000);
        {
        compt++;
        temps_dernier = temps;
        }    
      affichage();
    }
    
    void affichage()
    {
      vitesse = valeur*compt; //calcul de la vitesse du vent
      Serial.print("vitesse du vent :");
      Serial.println(vitesse);
      compt = 0;
    }

    Répondre

  • Les interruptions (1) 25 mars 2016 16:26, par Jean-Luc

    Bonjour,

    Ah oui, c’est la période des projets sti2d :-)

    votre problème est assez éloigné du modélisme ferroviaire mais je vais quand même vous répondre. Votre programme ne va pas fonctionner car :

    • dans votre routine d’interruption vous faites une attente active sur le passage du temps via millis(). Or la variable que vous récupérez via millis() est incrémentée via une autre routine d’interruption, celle du TIMER0. Elle ne peut pas s’exécuter car on ne peut pas interrompre une routine d’interruption. Donc le temps ne passe plus et votre while boucle à l’infini.
    • votre affichage à l’intérieur de la routine d’interruption peut également engendrer des problèmes. En effet, les caractères vont dans un tampon et, si le tampon est plein, il est vidé via la routine d’interruption associée à l’interruption registre d’envoi vide. Routine qui ne s’exécute pas pour le même raison que précédemment.

    Bref, c’est tout faux.

    Il ne faut pas faire d’attente active, il faut lire l’instant à laquelle se produit l’interruption via millis(), la mémoriser dans une variable. La prochaine fois que la routine est exécutée, on lit l’instant de nouveau, on calcule l’intervalle de temps entre interruption et on le stocke. Tout le reste du traitement doit être fait dans loop : calcul de la vitesse instantanée en prenant l’inverse de l’intervalle de temps, filtrage éventuel, etc. Si vous avez d’autre question contactez moi à l’aide du formulaire « Jean-Luc »

    Répondre

    • Merci pour votre réponse cela m’a beaucoup apris, j’ai retravaillé mon programme avec l’aide de votre commentaire et j’aimerais savoir si celui ci est plus juste

      int anemometre = 3; // broche de l'anémometre   
      float vitesse = 0;  //vitesse du vent
      float valeur = 2.4; //2,4 km/h par interruption
      unsigned long temps;
      unsigned long temps_maintenant;
      int interrupt=0;  //fonction pour compter le nombre d'impulsion
      
      
      
      void setup()
      {
        pinMode(anemometre, INPUT);
        Serial.begin(9600);
        temps=millis();
        attachInterrupt(digitalPinToInterrupt(3),compteur,RISING); //fonction pour compter le nombre d'interruption
      }
      
      
      void loop()
      { 
          if (interrupt != 0)
            {
              temps_maintenant = millis();
              while (temps - temps_maintenant <= 1000)
              {
                vitesse = valeur*interrupt; //calcul de la vitesse du vent
                Serial.print("vitesse du vent :");
                Serial.println(vitesse);         
              }
              interrupt = 0;
            }
         Serial.print("Pas de vent");    
      }  
      
      
      void compteur()
      {
        
        interrupt++; // on incrémente interrupt
        
      }

      Répondre

    • Les interruptions (1) 31 mars 2016 10:05, par Jean-Luc

      Contactez moi via http://modelleisenbahn.triskell.org...
      Cordialement

      Répondre

  • Les interruptions (1) 21 mai 2016 10:42, par Cyril

    Une question concernant les fonctions ISR, pourquoi n’est il pas possible d’envoyer une variable à cette fonction ? Est ce protocolaire ou pour optimiser le temps d’exécution ? Cela éviterait d’écrire plusieurs fois la même fonction. D’avance merci :)

    Répondre

    • Les interruptions (1) 21 mai 2016 11:03, par Jean-Luc

      Bonjour,

      je suppose que vous parlez d’un argument à la fonction ? Quel argument ? Qui le passerait ?

      Répondre

      • Les interruptions (1) 21 mai 2016 11:10, par Cyril

        Oui, un identifiant qui serai passé en argument lors de l’initialisation mais je viens de lire sur le site arduino qu’effectivement on ne peut passer d’argument à ce type de fonction. Ce qui est dommage car cela oblige a réécrire la même fonction X fois.
        Si on se base sur votre exemple avec les ILS on pourrait ne l’écrire qu’une seule fois passer en argument le numéro d’ILS qui correspondrait à une case du tableau de comptage.

        Répondre

        • Les interruptions (1) 21 mai 2016 19:40, par Dominique

          Si vous voulez autant d’interruptions que d’ILS, il faut donc leur affecter une pin chacune et chaque interruption est relative à un seul ILS.

          Il me semble que c’est déjà un argument : chaque interruption connaît son ILS.

          Il suffit d’activer une variable globale (true dans un booléen par exemple) dans chaque routine d’interruption (c’est un tout petit code qui peut être répété dans chaque interruption), et laisser la LOOP traiter ces événements dans une routine unique.

          En règle générale, on en fait le moins possible dans les interruptions pour ne pas en perdre !

          Répondre

  • Les interruptions (1) 25 mai 2016 18:40, par Maxime

    Bonjour,
    Voila je suis sur un projet de train modélisme. Je voudrais calculer la vitesse de mon train j’ai un capteur à effet hall dessous un wagon et un aimant qui est sur l’essieu.
    Je calcule le temps entre deux interruptions pour ensuite calculer la vitesse avec le temps.

    voici mon code :

    int pin = 4;
    const byte interruptPin = 2;
    volatile byte compteur;
    float Temps;
    unsigned long timeold;
    float DP = 3.5;
    
    void setup()
    {
      pinMode(interruptPin, INPUT_PULLUP);
      attachInterrupt(digitalPinToInterrupt(interruptPin), compte, LOW);
      pinMode(pin, INPUT);
      Serial.begin(9600);
      timeold = 0;
    }
    
    void loop()
    {
      Temps = (millis() - timeold)*compteur;
      Serial.println(Temps);
    }
    
    void compte ( ) {
     compteur = compteur+1; 
    }

    Répondre

  • Les interruptions (1) 25 mai 2016 19:30, par Jean-Luc

    Bonjour,

    compteur compte les tours de roue, c’est donc une distance qui correspond à PI x D, D étant le diamètre de la roue.

    Par conséquent millis() x compteur (j’oublie timeold qui vaut tout le temps 0) est un temps multiplié par une distance. Ce n’est donc pas un temps et ce n’est pas non plus une vitesse. Ça ne correspond à aucune grandeur physique en fait.

    Si vous voulez calculer la vitesse, il faut calculer quelque chose qui ait la dimension d’une vitesse : une distance divisée par un temps.

    Ensuite il faut multiplier par les constantes qui vont bien pour avoir une vitesse dans une unité qui va bien pour le train miniature. Si vous voulez le résultat en millimètres par seconde, il faut, puisqu’on divise par des millisecondes, multiplier compteur par 1000 et multiplier par PI x D (D étant exprimé en millimètres).

    Répondre

  • Les interruptions (1) 25 mai 2016 21:19, par Maxime

    D’accord mais du coup que doit-je mettre dans mon interruptions pour récupérer le temps ?

    Répondre

    • Les interruptions (1) 25 mai 2016 23:05, par Dominique

      Vous ne changez rien dans l’interruption qui continue à incrémenter un compteur à chaque tour de roue.
      C’est le calcul dans la loop qu’il faut changer.

      Par exemple, vous pouvez compter le nombre de tour de roues NB par seconde, c’est à dire la distance PI x D x NB par seconde et ce sera donc la vitesse que vous cherchez à obtenir !

      int pin = 4;
      const byte interruptPin = 2;
      volatile int compteur;
      int compteurold;
      float vitesse;
      unsigned long time, timeold;
      float DP = 3.5;
       
      void setup()
      {
        pinMode(interruptPin, INPUT_PULLUP);
        attachInterrupt(digitalPinToInterrupt(interruptPin), compte, LOW);
        pinMode(pin, INPUT);
        Serial.begin(9600);
        timeold = millis();
        compteurold = 0;
      }
       
      void loop()
      {
        if ( (millis() - timeold) > 1000) { // une seconde s'est écoulée 
          timeold = millis();
          vitesse = (compteur - compteurold)*PI*DP;
          compteurold = compteur;
          Serial.println(vitesse);
        }
      }
       
      void compte ( ) {
       compteur = compteur+1; 
      }

      Répondre

  • Les interruptions (1) 26 décembre 2016 08:55

    Bonjour,

    J’ai un problème à la compilation de programme utilisant des interruptions. Le compilateur m’indique qu’a l’intérieur de attachInterrupt(numéro, ISR, mode) ;
    l’ISR n’est pas déclaré. Auriez vous une solution ?

    Merci par avance.

    Répondre

    • Les interruptions (1) 26 décembre 2016 09:20, par Jean-Luc

      Bonjour,

      Avec aussi peu d’informations, non. Quel Arduino, quel ISR, quel message d’erreur exactement ?

      Répondre

  • Les interruptions (1) 2 mars 2017 22:50, par jf

    Bonjour,

    Une incompréhension qui vous paraîtra peut être bête.....
    dans cette ligne de code :

    const unsigned long dureeAntiRebond = 1 ;

    le 1 signifie t-il 1 ms ? si oui cette valeur devrait être égal à 0.001

    merci pour votre réponse

    Répondre

    • Les interruptions (1) 3 mars 2017 08:04, par Jean-Luc

      Bonjour,

      oui, il s’agit d’une milliseconde. Et non ça ne devrait pas être 0,001 car la fonction millis() retourne la date comptée en millisecondes.

      Répondre

      • Les interruptions (1) 3 mars 2017 12:07, par jf

        Bjr,

        • Merci pour votre réponse, je viens de comprendre que (date - dateDernierChangement)donnait un chiffre proche de 1.
        • Une question qui n’est pas en rapport avec les locos......
          Sur un anémomètre comportant un ILS et quatre aimants (quatre déclenchement par tour)et tournant à une vitesse élevée n’y a t-il pas un risque que le rebond du premier déclenchement de l’ILS ne chevauche le deuxième déclenchement et du coup n’altère ou fausse la précision de cette vitesse ?
          1ms est elle une durée acceptable dans la plupart des cas ou doit-on tâtonner pour en trouver une acceptable ?
          cordialement merci

        Répondre

        • Les interruptions (1) 3 mars 2017 13:31, par Jean-Luc

          Si l’anémomètre tourne à plus de 250 tours/s, oui il y aura un problème. Ça fait quand même 15000 tours/minute. Ça tourne si vite un anémomètre ? Par ailleurs pourquoi mettre 4 aimants ? un seul ne suffit pas ? Enfin à une telle vitesse, il vaut mieux mettre un capteur à effet Hall qui ne nécessite pas d’antirebond

          Répondre

          • Les interruptions (1) 4 mars 2017 20:01, par fj
            • En fait il n’y a que deux aimants mais qui déclenchent 4 fois l’ILS par tour.
              Après calculs je trouve une période de déclenchement de l’ILS de 5ms pour une vitesse du vent de 40km/h (distance du centre d’une tasse à l’axe de rotation de 40mm)
              donc 1ms cela devrait suffire pour gommer le rebond mais j’en ai toujours....même en remplaçant mon anémomètre par un simple interrupteur.

            voici mon programme au cas ou

            #include <TimerOne.h>

            #define duree_mesure 10 //l’étiquette duree_mesure vaut 10
            unsigned int nombre_imp ;
            unsigned int seconde ;
            const unsigned long dureeAntiRebond = 1 ; //(1 ms)

            void setup()

            pinMode(2,INPUT_PULLUP) ; //active la résistance pull-up interne et déclaration du pin 2 comme entrée
            Serial.begin(9600) ;
            Serial.println("C’est parti !") ;
            attachInterrupt(0,compte_imp,FALLING) ;
            Timer1.initialize(1000000) ;
            Timer1.attachInterrupt(fin_comptage) ;
            nombre_imp=0 ;
            seconde=0 ;

            void loop()

            void compte_imp()

            static unsigned long dateDernierChangement = 0 ;

            unsigned long date = millis() ;
            if ((date - dateDernierChangement) > dureeAntiRebond)

            nombre_imp=nombre_imp+1 ; //équivalent à nombre_imp++ ;
            dateDernierChangement = date ;


            void fin_comptage()

            seconde++ ; //équivalent à seconde = seconde+1 ;
            if(seconde>=duree_mesure)

            Serial.println(nombre_imp) ;

            nombre_imp=0 ;
            seconde=0 ;

            Répondre

  • Les interruptions (1) 11 août 2017 20:43, par Guyon

    Bonjour

    Je voudrais utiliser la fonction interruption d’Arduino (micro) pour :

    * sur un front montant "faire 1"

    * sur une front descendant "faire 2"

    le programme (partiel) :

    attachInterrupt(0,front_montant,RISING))
    
    attachInterupt(0,front_descendant,FALLING)

    puis plus loin :

    void front_montant () {
      faire1
    }
    
    void front descendant () {
      faire2
    }

    Cela ne fonctionne pas : une seule interruption est prise en compte (la seconde)

    Une idée ???

    Merci d’avance

    Gilles

    Répondre

  • Les interruptions (1) 11 août 2017 21:06, par Jean-Luc

    Bonsoir,

    c’est normal. En effet, on ne peut associer qu’une seule routine d’interruption a une broches. Votre second attachInterrupt remplace le premier.

    Pour faire cela, il faut que la sensibilité soit CHANGE et que vous testiez via un digitalRead l’état de la broche.

    Répondre

  • Les interruptions (1) 1er décembre 2017 23:12, par Loco28

    Bonsoir,

    J’ai posé ma question dans une autre rubrique mais je pense qu’elle est mieux ici.
    La récupération des données DCC se fait à l’aide d’une interruption.
    On obtient ADDRESS et DATA.
    Le problème c’est que quand on traite ces deux données pour générer une action, on peut louper les données qui suivent.
    Il faudrait pouvoir stocker ces données dans une pile, puis les dépiler pour les traiter, sachant qu’à chaque interruption une nouvelle donnée serait entrée dans la pile.
    Problème : je n’ai aucune idée comment faire et je voudrais un peu d’aide.

    Répondre

    • Les interruptions (1) 2 décembre 2017 09:54, par bobyAndCo

      Bonjour,

       

      Dans une pile ou plus probablement dans une file d’ailleurs car il est vraisemblable que dans le cas que vous évoquez il vaille mieux un First In / First Out. C’est une question de programmation en C que vous trouverez traité sur de nombreux sites dont en particulier en français sur "OpenClassRoom". Ici, ça me semble très bien expliqué : https://openclassrooms.com/courses/...

       

      Les structures ne sont pas des notions bien compliquées. Quant aux pointeurs, je vous assure que ce n’est pas non plus bien compliqué mais il faut bien essayer de comprendre les mécanismes. Une fois fait, ça simplifie considérablement l’écriture offre de plus grandes possibilités.

       

      Reportez vous aux articles de Thierry : http://www.locoduino.org/spip.php?a...

       

      Commencez au moins à écrire le code et si vous rencontrez des difficultés, nous pourrons vous aider.

       

      Bien amicalement

      Répondre

      • Les interruptions (1) 2 décembre 2017 10:22, par Jean-Luc

        Perso, je n’irais pas vers le type de structure présenté sur OpenClassroom qui consomme pas mal de mémoire pour stocker 2 malheureux octets et suppose une allocation dynamique des éléments du tampon. Et on ne fait pas d’allocation dynamique dans une application temps réelle. Un tableau de struct (couples adresse / donnée) géré en tampon circulaire est mieux.

        Répondre

        • Les interruptions (1) 2 décembre 2017 11:59, par Loco28

          Bon, j’ai du travail, mais j’aime ça.

          Par contre : "Un tableau de struct (couples adresse / donnée) géré en tampon circulaire est mieux", cela se traduit comment en "français-arduino" ?

          Répondre

          • Les interruptions (1) 2 décembre 2017 13:16

            Ça se traduit par l’utilisation d’une bibliothèque qui va bien. Par exemple https://github.com/rlogiacco/Circul...

            Puis par la définition d’un type de donnée :

            typedef struct {
              byte adresse;
              byte commande;
            } PaquetDCC;

            Ce qui permet ensuite d’instancier un buffer de disons 10 éléments :

            CircularBuffer<PaquetDCC,10> buffer;

            Répondre

            • Les interruptions (1) 2 décembre 2017 22:19, par Loco28

              OK, je commence à comprendre le principe en ayant, entre autre, regardé la bibliothèque CircularBuffer.
              Par contre, c’est le point d’entrée que je n’arrive pas à situer.
              Ci-après mon programme de récupération des données DCC :

              #include <DCC_Decoder.h>
              #define kDCC_INTERRUPT    0    // pin 2 for UNO
              
              //Handler DCC
              void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data){
                // Conversion de l'adresse NMRA en adresse decodeur d'accessoire
                address -= 1;
                address *= 4;
                address += 1;
                address += (data & 0x06) >> 1;
                int action = (data & 0x01);
               //
               //
               // ici se situe le programme pour activer les sorties
               // en fonction des données reçues
               //
               //
                } 
              void setup() {      
                DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true); 
                DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );  
              }
              void loop() {
                DCC.loop();
              }

              Il faut que je récupère ADDRESS et ACTION pendant l’exécution de la routine d’interruption.
              Où dois-je placer mes instructions d’écriture dans le buffer ?

              Répondre

              • Les interruptions (1) 3 décembre 2017 08:29, par Jean-Luc

                Je dirais dans BasicAccDecoderPacket_Handler. Elle est curieuse cette fonction. On y calcule des choses qui finalement sont écrites dans data mais data étant une variable locale, la valeur est oubliée.

                Répondre

                • Les interruptions (1) 3 décembre 2017 13:44, par Loco28

                  Je ne pourrais pas répondre concernant la particularité de la fonction BasicAccDecoder_Handler, je me suis contenté de l’utiliser telle quelle.
                  Si je comprend bien ta réponse, tout ce qui est dans le "Void BasicAccDecoderPacket_Handler" est fait dans le cadre de la routine d’interruption ? Ce qui voudrait dire qu’actuellement je traite les données lors de l’interruption. Si c’est le cas, je comprend que je loupe des instructions.
                  Est-ce que cela veut dire que je dois mettre le "dépilage" et le traitement des données dans la partie "Void loop" ?

                  Répondre

                  • Les interruptions (1) 4 décembre 2017 20:17, par Loco28

                    Finalement, avec l’aide tous, j’ai réussi, je pense, à résoudre mon problème.
                    Ci-dessous mon code provisoire :


                    #include <DCC_Decoder.h>
                    #define kDCC_INTERRUPT    0    // pin 2 for UNO
                    #include <CircularBuffer.h>
                    
                    CircularBuffer <int, 100> storadresse;
                    CircularBuffer <byte, 100> storaction;
                    
                    int add = 0;
                    int act = 0;
                    int old_add = 0;
                    int old_act = 0;
                    int action = 0;
                    
                    //Handler DCC
                    void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data){
                      //Serial.print(address);
                      //Serial.print("    ");
                      //Serial.println(data);
                     storadresse.push (address);
                     storaction.push (data); 
                      } 
                    void setup() {      
                      DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true); 
                      DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );
                      Serial.begin (115200);  
                      Serial.println("Debut test");
                    }
                    void loop() {
                      DCC.loop();
                    
                      while(!storadresse.isEmpty()) {
                       add = storadresse.shift();
                       act = storaction.shift();
                    
                     if ((add != old_add) || (act != old_act)){
                        old_add = add;
                        old_act = act;
                    // Conversion de l'adresse NMRA en adresse decodeur d'accessoire 
                       add -= 1;
                       add *= 4;
                       add += 1;
                       add += (act & 0x06) >> 1;
                       action =(act & 0x01);
                       Serial.print(add);
                       Serial.print("   ");
                       Serial.println(action);
                       
                       } //fin nouvelle donnée
                      } //fin while   
                    } //fin loop 

                    Le traitement des données pour détermination des ordres à envoyer vers les aiguillages et les signaux se fera à l’emplacement des "Serial.print", dans la "loop".
                    Seul inquiétude : je travaille avec 2 fichiers que je remplis et vide en même temps mais je préférerais une solution plus synchrone.

                    Répondre

  • Les interruptions (1) 3 décembre 2017 20:12, par Dominique

    La fonction BasicAccDecoderPacket_handler est tronquée. Il manque ce qui permet de récupérer le resultat :

    for(int i=0; i<(int)(sizeof(gAddresses)/sizeof(gAddresses[0])); i++)
        {
            if( address == gAddresses[i].address )
            {
                Serial.print("Basic addr: ");
                Serial.print(address,DEC);
                Serial.print("   activate: ");
                Serial.println(enable,DEC);
                
                if( enable )
                {
                    gAddresses[i].output = 1;
                    gAddresses[i].onMilli = millis();
                    gAddresses[i].offMilli = 0;
                }else{
                    gAddresses[i].output = 0;
                    gAddresses[i].onMilli = 0;
                    gAddresses[i].offMilli = millis();
                }
            }
        } 

    Répondre

    • Les interruptions (1) 6 décembre 2017 20:47, par Loco28

      J’avoue ne pas tout comprendre.
      Je pense que c’est à mettre juste après la "void" BasicAcc.....
      L’adresse que l’on récupère n’est pas directement exploitable et, pour avoir la bonne adresse, il y a une formule avec DATA, que je ne vois pas.
      A quoi servent les lignes qui sont après le test de l’Enable ?

      Répondre

      • Les interruptions (1) 6 décembre 2017 22:43, par dominique

        Pourrais-tu transporter tes questions dans le forum (débuter ou vos projets) : ce n’est pas pratique ici, l’article n’est pas fait pour cela

        merci

        Répondre

  • Les interruptions (1) 28 janvier 2020 14:42, par ndiaye

    Bonjour,

    je voulais faire une interruption sur un front montant et j’en suis pas sur de mon programme :

    int monBouton = 2; // bouton en broche 2
    int compteur = 0;  // un compteur
    int etatBouton; // L'état du bouton
     
    void setup() {
      // le bouton en entrée
      pinMode(monBouton, INPUT); 
    }
     
    void loop() {
      // si on appuie sur le bouton
      etatBouton = digitalRead(monBouton);
      if(etatBouton == HIGH) {
        // alors on incrémente le compteur
        compteur = compteur+1;
      }
    }

    Répondre

  • Les interruptions (1) 2 avril 2020 22:35, par hicheri

    static unsigned long dateDernierChangement = 0 ;
    cette ligne remet à zero à chaque fois qu’il ya interruption, ya un probleme non ?

    Répondre

  • Évolution depuis l’écriture de l’article 12 octobre 2020 09:39, par frederic

    Bonjour

    dans ce très bon article il est mentionné

    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.”

    bien que ce soit une recommandation tout à fait pertinente, la classe HardwareSerial a évolué et maintenant ne dépend plus que des interruptions pour émettre des octets ce qui fait qu’un appel à print ne sera plus un risque de blocage du code dans l’ISR.

    il n’est dans l’absolu pas pertinent de faire ces affichages dans une interruption mais pour faire un peu de debug ça peut être vraiment utile,

    bonne continuation à toutes et tous, merci pour ce forum qui fourmille de bonnes idées

    Répondre

    • Évolution depuis l’écriture de l’article 18 septembre 2021 10:51, par Jean-Luc

      Bonjour.

      Je ne vous suis pas. HardwareSerial dispose d’un tampon en mémoire pour les écritures. Sur un Uno, il fait 64 octets. Le mécanisme utilisé dans le logiciel Arduino est le suivant :

      • Le programme principal, via des appels à HardwareSerial::write écrit les caractère de la façon suivante :
        • Si le tampon est vide et que le registre d’émission de l’UART est vide, le caractère est mis dans le registre d’émission de l’UART.
        • Si le tampon n’est pas vide ou le registre d’émission de l’UART est plein et que le tampon n’est pas plein, le caractère est mis dans le tampon
        • Si le tampon est plein, HardwareSerial::write fait une attente active de libération de place dans le tampon. C’est ce dernier cas qui nous intéresse.
      • L’ISR appelée quand le registre d’émission de l’UART devient vide se charge de prélever le caractère de tête du tampon pour le mettre dans le registre s’émission de l’UART, ce qui rend le tampon non plein.

      Le problème survient donc quand, appelé d’une ISR, HardwareSerial::write découvre que le tampon est plein et se met en attente active. Comme l’ISR appelée quand le registre d’émission de l’UART devient vide ne peut pas s’exécuter car les interruptions sont masquée, le tampon ne devient pas non plein et l’attente active se poursuit éternellement. Ce qu’on appelle communément un deadlock.

      Répondre

  • Les interruptions (1) 4 décembre 2020 15:02, par Guy71

    Bonjour et bravo pour vos articles !
    sur Leonardo, les pins 2 et 3 sont affectées à I2C (SDA et SCL) dois-je comprendre qu’on ne peut pas avoir à la fois de l’I2C et des interruptions INT0 et INT1 ?

    merci !

    Voir en ligne : interruptions

    Répondre

  • Les interruptions (1) 14 janvier 2021 18:54, par Fantasio

    Adepte de l’ATtiny85 je souhaiterai savoir si la bibliothèque PinChangeInt est compatible avec cette puce.

    Merci par avance et bravo pour vos articles qui je lis et relis régulièrement.

    Répondre

  • Les interruptions (1) 15 janvier 2021 09:50, par Dominique

    Mais pourquoi ne pas le tester avec l’exemple fourni ?

    Répondre

  • Les interruptions (1) 15 janvier 2021 12:20, par Fantasio

    L’exemple de Jean-Luc ne peut s’utiliser sur l’Attiny85 du fait de la communication avec le moniteur série.

    Répondre

  • Les interruptions (1) 15 janvier 2021 17:01, par Fantasio

    Par contre la bibliothèque PinChangeInterrupt convient pour l’Attiny85. Ci-dessous un exemple de code l’utilisant :

    #include "PinChangeInterrupt.h"
    
    const byte pinBttnA = 0;
    const byte pinBttnB = 1;
    const byte pinLEDA = 3;
    const byte pinLEDB = 4;
    byte stateLedA = 0;
    byte stateLedB = 0;
    
    void interruptA(void) {
      stateLedA = HIGH;
      stateLedB = LOW;
    }
    
    void interruptB(void) {
      stateLedA = LOW;
      stateLedB = HIGH;
    }
    
    void setup() {
      pinMode(pinBttnA, INPUT_PULLUP);
      pinMode(pinBttnB, INPUT_PULLUP);
      pinMode(pinLEDA, OUTPUT);
      pinMode(pinLEDB, OUTPUT);
      attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(pinBttnA), interruptA, FALLING);
      attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(pinBttnB), interruptB, FALLING);
    }
    
    void loop() {
      digitalWrite(pinLEDA, stateLedA);
      digitalWrite(pinLEDB, stateLedB);
    } 

    Répondre

  • Les interruptions (1) 16 janvier 2021 10:22, par Dominique

    Bravo, vous avez trouvé vous-même la réponse !!

    Répondre

  • Les interruptions (1) 19 janvier 2022 12:37, par Tom

    Bonjour !

    J’ai un petit projet de conception de tachymètre pour mon stage que j’ai décider de réaliser avec un arduino et un capteur à effet hall.

    Pour pouvoir déterminer la vitesse de rotation d’une broche je vais y positionner dessus un aimant, et la capteur à effet hall que j’ai choisi de type npn, va permettre d’envoyer un signal à la carte arduino.
    Ensuite afin de déterminer la vitesse de rotation, je souhaite utiliser la fonction attachInterrupt afin de récupérer la période entre deux fronts descendants, et ensuite par un calcul déterminer la vitesse en tr/min avant de le renvoyer sur un afficheur 4*7 segments.

    Pouvez-vous m’aider dans la rédaction de mon code car je pense que ce dernier ne fonctionne pas correctement, après avoir effectuer plusieurs test, rien à faire je ne trouve pas la solution.

    Voici le code :

     #include "SevSeg.h"
    
    SevSeg myDisplay;
    
    const byte PIN_SIGNAL = 2;
    volatile unsigned long periode=0;
    
    void setup() {
      
      byte numDigits = 4;
      byte digitPins[] = {10, 11, 12, 13};
      byte segmentPins[] = {9, 6, 4, 9, 3, 5, 8, 7};
      bool resistorsOnSegments = false; 
      byte hardwareConfig = COMMON_CATHODE; 
      bool updateWithDelays = false; 
      bool leadingZeros = true; 
      bool disableDecPoint = true; 
      
      myDisplay.begin(hardwareConfig, numDigits, digitPins, segmentPins,resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);
      myDisplay.setBrightness(90);
    
        pinMode(PIN_SIGNAL, INPUT);
        attachInterrupt(digitalPinToInterrupt(PIN_SIGNAL), tick, FALLING);
        Serial.begin(115200);
    }
    void tick() {
      static unsigned long previousMicros = 0;
      unsigned long currentMicros = micros();
      periode = currentMicros - previousMicros;
       previousMicros = currentMicros;
    }
    
    
    void loop() {  
      int Rotation = 1000000 * 60 / (periode);
      char tempString[10]; 
      sprintf(tempString, "%4d", Rotation); 
      myDisplay.setNumber(tempString, 0);
      delay(10);
     }

    désolé je ne sais l’afficher comme sur l’interface arduino

    Répondre

    • Les interruptions (1) 19 janvier 2022 21:50, par Jean-Luc

      Ah. La période des projets du lycée. ☺️

      Vous devriez poser votre question sur le forum Arduino. Ici on ne fait que du train miniature.

      Cordialement.

      Répondre

      • Les interruptions (1) 20 janvier 2022 12:17, par Tom

        Haha, non pour moi il s’agit d’un projet annexe en stage de 2ème école d’ingénieur.
        D’accord merci quand même.

        Répondre

  • Les interruptions (1) 19 janvier 2022 22:01, par msport

    Bonjour,
    1. votre projet ne concerne pas le modélisme ferroviaire même si il y a des idées à prendre sur ce site ...
    2. voir Enrichissement typographiques dans les commentaires ci-dessus.
    3. On vient juste d’éditer un article : Où et comment demander de l’aide ? et vous ne faites pas l’effort d’expliquer ce que vous constatez en testant le sketch que vous avez.
    4. Inutile de recopier un sketch ailleurs et de nous le poster en nous demandant de l’expliquer.
    5. Commencez par lire les articles d’initiation cités dans l’article ci-dessus.
    Bonne soirée.

    Voir en ligne : Où et comment demander de l’aide ?

    Répondre

  • Les interruptions (1) 9 mars 2023 23:04, par vitrac

    Bonjour
    Ca veut dire quoi le sigle ILS ?
    Merci de la réponse

    Voir en ligne : lesinterruptions

    Répondre

  • Les interruptions (1) 24 avril 2023 22:35, par Franck

    Bonjour,
    je voudrais lire 4 codeurs incrémentaux (pour 4 roues) sur un Arduino UNO.
    Es ce possible, car j’ai besoin de 8 entrée d’interruption. Sur quelles entrées ?
    Si non, puis je le faire avec un Arduino plus gros style MEGA.
    Merci d’avance pour votre réponse.
    Cordialement.
    Franck.

    Répondre

    • Les interruptions (1) 4 mai 2023 09:37, par Jean-Luc

      Bonjour,
       
      Relisez l’article à partir de « Les interruptions externes PCINT0, PCINT1 et PCINT2 »
       
      Cordialement

      Répondre

  • Bonjour,
    J’utilise un code qui me permet d’afficher la vitesse du vent sur le moniteur série à l’aide d’un anémomètre muni d’un ILS. J’avais réalisé ce code grâce à votre article sur les interruptions en 2017 (ça date ...). Jusqu’à maintenant, il fonctionnait parfaitement mais dernièrement, plus rien ne s’affiche sur le moniteur série. Je n’arrive pas à comprendre d’où vient le problème et s’il est lié aux fonctions associées aux interruptions. Ce programme est utilisé avec des élèves de 4ème par l’intermédiaire d’un éditeur de blocs où j’ai conçu les blocs de l’anémomètre pour éviter qu’ils saisissent le code (pas au programme de 4ème). https://technologies-numeriques.pag...

    Auriez-vous l’amabilité de m’aider à résoudre ce problème car je n’ai pas toutes les compétences pour le faire.
    Merci par avance d’autant que j’ai conscience que ça sort du cadre des trains automatisé.
    Bien cordialement

    Voici le code

    #include <PinChangeInt.h>
    
    
    #define pinILS 2
    #define pi 3.1415
    #define RayonDesBras 0.07
    
    unsigned long dureeDebut;
    int nombreTourSec = 0;
    int nombreTourMin = 0;
    float vitesseVent(0);
    float FEtalonnage(0.92); //coefficient à appliquer en fonction de la valeur mesurée avec un appareil de test
    
    volatile unsigned int comptageILS = 0; // une variable utilisée dans une interruption doit être déclarée "volatile"
    const unsigned long dureeAntiRebond = 1;
    
    
    void interruptILS() //comptage de l'ILS
    {
      static unsigned long dateDernierChangement = 0;
       unsigned long date = millis();
     
      if ((date - dateDernierChangement) > dureeAntiRebond) {
        comptageILS++;
        dateDernierChangement = date;
      }
    }
    
    
    
    void setup()
    {
      Serial.begin(9600);
      pinMode(pinILS, INPUT);
      PCintPort::attachInterrupt(pinILS, interruptILS, FALLING);
      dureeDebut = millis();
    }
    
    
    
    void loop()
    {
      if ((millis() - dureeDebut) > 5000) { // durée de 5 secondes
        nombreTourSec = comptageILS/5; //comptage du nombre de tours par seconde
        nombreTourMin = nombreTourSec * 60;
        vitesseVent = pi * RayonDesBras * nombreTourMin * FEtalonnage * 3.6 / 30; // formule pour le calcul de la vitesse du vent
        comptageILS = 0; // réinitialisation du comptage
    
    
        Serial.print("Vitesse du vent = "); // affichage des valeurs
        Serial.print(vitesseVent);
        Serial.print(" km/h\r\n");
        dureeDebut = millis();
      }
    }

    Voir en ligne : Procédure utilisée par les élèves

    Répondre

  • Les interruptions (1) 15 novembre 2024 08:52, par Jean-Luc

    Bonjour

    la première chose qui me saute aux yeux est que dureeDebut n’est pas initialisé.

    Répondre

    • Les interruptions (1) 15 novembre 2024 17:34, par Franck

      Bonjour,

      J’ai initialisé comme suit mais rien ne change : pas de réponse du moniteur série. unsigned long dureeDebut = 0;

      Répondre

      • Les interruptions (1) 15 novembre 2024 18:48, par Jean-Luc

        Question bête : Le moniteur série a bien une vitesse de 9600 bauds comme spécifié dans le programme ?

        Répondre

        • Oui la vitesse est le même que dans le code. Ce que je ne comprends pas c’est que j’enseigne dans 2 établissements scolaires différents et que les codes fonctionnent parfaitement sur les ordinateurs connectés en réseau de ces collèges mais j’ai testé sur 3 ordinateurs individuels (dont celui de mon domicile) et là ça fonctionne pas. Ce que j’ai peur c’est si il y a une mise à jour quelconque sur les serveurs des collèges et que je ne puisse plus utilisé les codes avec mes élèves. En fait, je ne sais pas si ça vient du code en lui-même ou d’autres choses. J’ai testé le moniteur série avec d’autres codes que celui de l’anémomètre et ça fonctionne. Merci beaucoup pour votre aide.

          Répondre

          • C’est curieux. Si vous enlevez le test if ((millis() - dureeDebut) > 5000) { // durée de 5 secondes de manière à afficher systématiquement au lieu de toutes les 5 secondes, vous avez un affichage ?

            Répondre

            • Toujours pareil même sans le test : aucun affichage. J’ai aussi testé avec un écran OLED I2C SSD1306 de 0,96 pouce sans faire intervenir le moniteur série dans le code afin de voir si la valeur était envoyée : rien ne s’affiche (et j’ai testé l’écran avec un autre code avant). Je ferai ce test avec l’OLED lundi dans mon collège. Selon vous, est-ce que cela peut venir de la bibliothèque ?

              Répondre

  • Les interruptions (1) 16 novembre 2024 10:59, par Franck

    J’ai supprimé PCintPort::attachInterrupt(pinILS, interruptILS, FALLING); et maintenant le moniteur série affiche "Vitesse vent = 0 km/h" comme attendu.

    Répondre

    • Les interruptions (1) 16 novembre 2024 11:37, par Jean-Luc

      Ok.

      Une possibilité : L’entrée d’interruption reçoit des impulsions de fréquence élevée, ce qui fait que l’Arduino passe son temps dans la routine d’interruption. Avez vous un oscilloscope pour vérifier ça ?

      Seconde possibilité : Ça vient de la bibliothèque. Pourriez vous essayer avec une des deux entrées d’interruption INT0 ou INT1 qui ne nécessitent pas la bibliothèque.

      Répondre

  • Les interruptions (1) 17 novembre 2024 18:00, par Franck

    Génial ! Le moniteur affiche les valeurs attendues. Comme je n’ai pas d’oscilloscope, je suis passé directement à la 2ème possibilité. Comme vous me l’avez suggéré, j’ai changé la fonction initiale par attachInterrupt(digitalPinToInterrupt(3), interruptionILS, FALLING); et j’ai aussi supprimé la bibliothèque.

    Voici le code dans son intégralité avec toutes les modifications. Ce code est un peu différent de celui posté au départ car j’avais testé tous mes codes concernant cette mesure (dont aucun ne fonctionnait …) et je n’avais pas posté celui qui était utilisé dans le bloc pour la mesure du vent de l’éditeur Blockly@rduino (donc celui-ci).

    Si vous voyez d’autres choses qui peuvent être améliorées (syntaxe) n’hésitez pas car vu que je suis loin d’avoir votre expertise, cela me permet de progresser.

    En tout cas, MERCI INFINIMENT pour votre aide. Comme mon code ne concernait pas le modélisme ferroviaire je craignais aucune réponse. Je suis très soulagé que cela fonctionne car ce code n’est pas pour une utilisation personnelle mais pour être exploité par l’intermédiaire de blocs avec des élèves de collège. Encore Merci !

    unsigned long datedernierPassage = 0;
    long derniereSeconde = 0;
    float nombreTourSec (0);
    float nombreTourMin (0);
    float vitesseVentkmh(0);
    volatile unsigned int comptageILS = 0;
    const unsigned long dureeAntiRebond = 1; // anti-rebonds en milliseconde
    
    
    void interruptionILS() //comptage de l'ILS
    {
      static unsigned long dateDernierChangement = 0;
      unsigned long date = millis();
      if ((date - dateDernierChangement) > dureeAntiRebond) {
        comptageILS++;
        dateDernierChangement = date;
      }
    }
    
    
    float mesureventkmh()// mesure de la vitesse du vent instantanée (km/h)
    {
      float deltaTime = millis() - datedernierPassage;
      deltaTime = deltaTime/1000.0; //Converti en secondes
      nombreTourSec = (float)comptageILS / deltaTime;
      nombreTourMin = nombreTourSec * 60;
      vitesseVentkmh = 3.1416 * 7 * nombreTourMin * 1 * 0.036 / 30; // formule pour le calcul de la vitesse du vent
      comptageILS = 0; // Réinitialise et commence à incrémenter pour le nouveau comptage
      datedernierPassage = millis();
      return (vitesseVentkmh);
    }
    
    
    float incrementation()
    {
      if(millis() - derniereSeconde >= 1000)
      {
        derniereSeconde += 1000;
        mesureventkmh();
      }
      delay(100);
      return(vitesseVentkmh);
    }
    
    
    void setup() {
      Serial.begin(115200);
      pinMode(3, INPUT_PULLUP);
      derniereSeconde = millis();
      attachInterrupt(digitalPinToInterrupt(3), interruptionILS, FALLING);
    }
    
    
    void loop() {
      Serial.print("Vitesse du vent = ");
      Serial.print(incrementation());
      Serial.println(" km/h");
    }

    Répondre

Réagissez à « Les interruptions (1) »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Programmation »

Les derniers articles

Les articles les plus lus