IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Arduino Discussion :

Interruption déclenchée à chaque lancement de l'interrupt sur Nano


Sujet :

Arduino

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    406
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut Interruption déclenchée à chaque lancement de l'interrupt sur Nano
    Bonjour à tous

    je me suis rendu compte que dès le setup lorsque j'appelle attachInterrupt(0,switchOFDetected,FALLING);, une interruption est déclenchée.
    Puis dans mon programme j'utilise detachInterrupt(0);.
    Alors, dès que je réactive l'interruption, une interruption est à nouveau détectée.
    Je ne pense pas que ce phénomène soit normal.

  2. #2
    Membre Expert
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 017
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 017
    Par défaut
    Bonjour mormic

    As-tu bien initialisé l'entrée en , INPUT_PULLUP?
    Une entrée "en l'air" peut déclencher l'interrupt à tout moments.

    Cordialement
    jpbbricole

  3. #3
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    406
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    Bonjour jpbbricole

    Oui voilà mon setup:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void setup() {
      //Serial.begin(9600);
      pinMode(LEDPIN,OUTPUT);
      pinMode(OPIN,OUTPUT);             //commande de relais d'ouverture
      pinMode(FPIN,OUTPUT);             //commande le relais de fermeture
      pinMode(OFSWPIN, INPUT_PULLUP);    //poussoir ouverture manuelle
     
     attachInterrupt(0,switchOFDetected,FALLING);
    }

  4. #4
    Modérateur

    Homme Profil pro
    Ingénieur électricien
    Inscrit en
    Septembre 2008
    Messages
    1 277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur électricien

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 277
    Par défaut
    Bonjour à tous

    C'est un problème classique et expliqué dans la docs des AVR.
    En programmation AVR, il faut effacer un flag (par sécurité) avant d'autoriser à nouveau les interruptions. Ne pas effacer le flag permet d'avoir un comportement, souhaité selon les cas, d'une suspension de l'interruption (si une est arrivée pendant la désactivation, elle s'exécute à l'autorisation).

    En programmation Arduino, je m'attendrais à ce que cela soit fait d'office, mais je n'en sais pas plus.

    PS: La suspension des interruptions c'est pratique quand on veut qu'une partie critique du code ne soit pas coupée par une interruption. Cela ne fait sens que lorsque cette partie critique est courte.

    Delias

  5. #5
    Membre Expert
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 017
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 017
    Par défaut
    Bonjour mormic

    Sur quelle pin est ton interrupt (OFSWPIN)?
    Utilises cette syntaxe pour attacher ton interrupt:
    attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) (recommended)

    Cordialement
    jpbbricole

  6. #6
    Modérateur

    Homme Profil pro
    Ingénieur électricien
    Inscrit en
    Septembre 2008
    Messages
    1 277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur électricien

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 277
    Par défaut
    Bonjour

    Avant le message de jpbbricole, je n'avais pas fait attention que Arduino utilise directement la syntaxe AVR pour la gestion des interruptions.

    Si tu veux supprimer le problème il faut ajouter cette ligne de code avant de réactiver l'interruption. Il est possible d'avoir cette ligne de code aussi avant la première activation, il y a moins de coût en temps d'exécution et en mémoire de la laisser que de faire un test.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    EIFR &= ~(_bv(x)); // supprime interruption en attente sur INT0 (patte Arduino 2) ou INT1 (patte Arduino 3)
    Avec x qui vaut 0 pour l'interruption de la patte Arduino 2 et x qui vaut 1 pour l'interruption de la patte Arduino 3.

    Si on veut faire le reset sur les deux interruptions on peut faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    EIFR = $00; // supprime interruption en attente sur INT0 (patte Arduino 2) et INT1 (patte Arduino 3)
    J'utilise la variante d'écriture en hexadecimal car je travail sur des bits (petite convention de codage personnelle)

    Delias

  7. #7
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    406
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    Merci Delias pour cette info.
    S'agit-il du bit6 (INTF0 dans mon cas) du registre GIFR que je doit remettre à zéro dès que la routine d'interruption est entrée?
    Que font les routines cli() et sei()?
    EIFR &= ~(_bv(0)); Que veut dire _bv ?

    Ok j'ai trouvé _bv c'est l'acronyme de BitValue. Mais traduit en langage Arduino, qu'est-ce que ça donne?

  8. #8
    Modérateur

    Homme Profil pro
    Ingénieur électricien
    Inscrit en
    Septembre 2008
    Messages
    1 277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur électricien

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 277
    Par défaut
    Bonjour Mormic

    Pour paramétrer et gérer une interruption, il y a 5 étapes. Par simplification, je vais indiquer les registres utilisés par INT0.

    La première c'est d'activer les aiguillages (multiplexeurs) qui détectent une interruption. C'est EICRA bits ISC00 et ISC01.
    Dès que cela est fait, lorsque la condition pour activer une interruption est présente, il y a un set (passage à 1) du Flag de l'interruption correspondante, le flag est le registre EIFR bit INTF0.
    C'est bien le INTF0 du registre EIFR (tu as écrit GIFR que je ne trouve pas dans la doc du ATMEGA328) Ce flag est remis à 0 matériellement lors du lancement de l'interruption. Pas besoin de le faire à l'entrée dans la fonction d'interruption. C'est à faire uniquement avant la réactivation d'un masque (étape suivante) pour ignorer les interruptions arrivées pendant que le masque était désactivé. Cela peut aussi être fait en fin d'interruption pour ignorer une deuxième activation de l'interruption si la condition d'activation peut se présenter plusieurs fois dans un délai inférieur à celui du traitement. On peut par exemple l'utiliser comme anti-rebond sur l'interruption de changement de niveau si la durée du traitement de l'interruption correspond à la pause nécessaire au fonctionnement de l'anti-rebond.

    Il y a ensuite deux niveaux de masque, un bit par interruption permettant de bloquer l'interruption correspondante, c'est EIMSK bit INT0. Mais cela n'empêche pas au flag de passer à 1 si la condition d'interruption est remplie.
    Le deuxième niveau est général, c'est le Flag I de l'ALU que l'on peut activer et désactiver par sei() et cli(). Ce n'est qu'un masque qui empêche le démarrage des interruptions, mais, à nouveau, cela n'empêche pas aux flags des interruptions de passer à 1.

    Le dernier point c'est le vecteur d'interruption qui est la définition de la fonction qui doit être appelée lors d'une interruption.


    _BV est juste une macro qui fait 1<<x et donc _BV(0) équivaut à 0b00000001 et _BV(1) équivaut à 0b00000010. En Arduino ce serait bit();. Dommage d'utiliser une fonction quand une macro fait l'affaire, l'optimisation de compilation est moins bonne.

    Pour être encore plus strict j'aurais dû écrire EIFR &= ~(_BV(INTF0));, mais j'ai toujours de la peine à utiliser les noms des bits (et les macros en majuscules).

    Delias

  9. #9
    Modérateur

    Homme Profil pro
    Ingénieur électricien
    Inscrit en
    Septembre 2008
    Messages
    1 277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur électricien

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 277
    Par défaut
    Citation Envoyé par mormic Voir le message
    S'agit-il du bit6 (INTF0 dans mon cas) du registre GIFR
    Ça c'est pour les ATTiny85 (et probablement d'autres petits ATTiny) qui sont rependus chez les utilisateurs d'Arduino comme puce minimaliste.
    Mais ce n'est pas valable pour les ATMega qui équipent les vrai Arduino (Uno, Nano, Mega, Leonardo, etc.)

    Delias

  10. #10
    Modérateur

    Homme Profil pro
    Ingénieur électricien
    Inscrit en
    Septembre 2008
    Messages
    1 277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur électricien

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 277
    Par défaut
    Bonjour mormic

    Il manque à minimum cela:
    Citation Envoyé par jpbbricole Voir le message
    As-tu bien initialisé l'entrée en , INPUT_PULLUP?
    Une entrée "en l'air" peut déclencher l'interrupt à tout moments.
    Dans le setup()(honneur à celui qui l'a indiqué en premier)

    Ensuite les variables utilisées dans l'interruption doivent être déclarée avec le mot clé volatile pour éviter les problèmes suite aux optimisations du compilateur.

    Delai(); ne fonctionne pas dans les interruptions (mais c'est bizarre tu devrais avoir un freeze de l'AVR pas un comptage)

    Je ne suis pas certain que le detachInterrupt() utilisé dans l'interruption ne provoque pas de comportement non souhaité voir aléatoire. J'ai été voir le code Arduino, à priori pas.

    Delias

  11. #11
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    406
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    Je vais devenir fou. Je suis passé sur un Arduino Uno pour lever le doute. j'ai recompilé un petit prog en apportant quelques modifs mais c'est toujours bizarre.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    #define swPin 2
    volatile uint8_t i=0;
    volatile bool interruptOn=false;
     
    void delai(unsigned long t){
      unsigned long deb = millis();
      while(1){
        if(millis() - deb >= t) break;
      }
    }
     
    void gestionInterrupt(){
      interruptOn=true;
    }
     
    void action(){ 
      delai(500);
      i++;
      Serial.print("Inter "); Serial.println(i);
      interruptOn=false;
      delai(1000); 
      EIFR &= ~(bit(INTF0)); // supprime interruptions en attente sur INT0 
      attachInterrupt(0,gestionInterrupt,FALLING); 
    }
     
    void setup(){
      Serial.begin(9600);
      pinMode(swPin,INPUT_PULLUP);
      attachInterrupt(0,gestionInterrupt,FALLING); 
      Serial.print("SetUp "); Serial.println(i); 
    }
     
    void loop(){
      do{
      }while(!interruptOn);
      detachInterrupt(0);
      action();
    }
    Affichage: "SetUp 0" puis attente appui sur sw "Inter 1" attente 1 seconde "Inter 2" puis attente..
    Et systématiquement lorsque j'appui sur sw 2 interrupt semblent se déclencher ???

    Je ne sais plus par quel bout solutionner ce pb.

    Le fait de supprimer EIFR &= ~(bit(INTF0)); ne change rien

  12. #12
    Modérateur

    Homme Profil pro
    Ingénieur électricien
    Inscrit en
    Septembre 2008
    Messages
    1 277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur électricien

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 277
    Par défaut
    Bonjour

    J'aimerais voir s'il n'y a pas une erreur dans la lib Arduino.
    Est-ce que tu peux essayer de remplacer les lignes 23 et 24 par les trois variantes suivantes (sans autre modification de ton code):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    EIFR &= ~(1<<INTF0);
    EIMSK |= 1<<INT0;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    EICRA = (1<<ISC01);
    EIFR &= ~(1<<INTF0);
    EIMSK |= 1<<INT0;
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    EIFR &= ~(1<<INTF0);
    EICRA = (1<<ISC01);
    EIMSK |= 1<<INT0;
    Si cela fonctionne dans le deux premiers cas mais pas dans le 3ème, c'est la lib Arduino qui ne respecte pas une recommandation de codage de la doc AVR (qui était présente pour les anciennes puces)
    Mélanger les fonctions Arduino et l'accès aux registres ce n'est pas top. C'est plus pour chercher la raison du problème.

    Attention également: Registre &= ~(_BV(numbit)); et Registre &= ~(1<<numbit); fonctionnent, mais Registre &= ~(bit(numbit)); peut ne pas fonctionner, c'est lié à ce que l'on cherche à utiliser une optimisation bien précise du compilateur qui n'est possible que si la partie de droite est sur 8 bits, constante et avec un seul 0. En utilisant une fonction, ce contrôle par le compilateur peut ne pas fonctionner (la partie de droite est détectée comme variable) et provoquer un résultat inattendu.
    C'est pareil pour l'instruction inverse Registre |= _BV(numbit); et Registre |= 1<<numbit;.

    Delias

    [Edit] Petite erreur de ma part, je réactivais l’interruption sur le flan montant, j'avais lu la doc trop vite. Corrigé ici

  13. #13
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    406
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    Bonsoir Delias
    les trois solutions ne fonctionne qu'une fois, c'est à dire que: j'appui sur sw , je passe par action() puis les appuis suivants ne sont pas détectés.

    J'ai corrigé la ligne 23 d'origine par EIFR &= ~(1<<0); Je retrouve le même fonctionnement précédent, c'est à dire pour un appui j'ai 2 détections mais parfois j'en ai qu'une. Là c'est peut-être l'anti-rebond qui n'est pas bon ?

  14. #14
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 881
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 881
    Par défaut
    j'ai l'impression que ce sujet a largement dérivé.

    Qu'est-ce que le code est censé faire ou démontrer?

  15. #15
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    406
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    Bonsoir
    Mon problème d'origine était une détection supplémentaire d'interruption non dû à priori à un rebond du switch.
    Delias m'a fait faire différentes manoeuvres pour essayer de solutionner ce problème.
    Voilà pourquoi nous sommes repartis sur un sketch simple pour le localiser.
    Il m'a orienté vers les registres et flags gérant les interruptions AVR.
    Donc il n'y a pas dérive, et peut-être que la solution a été trouvé. J'attends la réponse de Delias.

  16. #16
    Modérateur

    Homme Profil pro
    Ingénieur électricien
    Inscrit en
    Septembre 2008
    Messages
    1 277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur électricien

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 277
    Par défaut
    Bonsoir à tous

    C'est exactement cela pour la réponse à Jay M

    Par contre le code, cela devrait être EIFR (le flag) pas EIMSK (le masque) qui résout le problème, car l'essai d'avant montre un résultat attendu (une interruption arrivée pendant la coupure de l'interruption est mise en mémoire dans le flag pour être exécutée au lancement.)
    D'autant plus que le code de attachInterrupt écrit dans EIMSK.

    Si tu utilises le _delay_ms(); je pense même que tout le code est transférable dans la fonction d'interruption. A modifier par petits pas et vérifier à chaque fois que cela n'a rien cassé.

    Bonne suite

    Delias

  17. #17
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    406
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    Bonjour Delias

    Je suis tout à fait d'accord avec toi : c'est le flag du registre EIFR qui doit être remis à 0.
    Je viens de faire l'essai en remplaçant EIMSK |= 1<<INT0; par EIFR &= ~(1<<INTF0); et effet bizarre, je retrouve mon problème de doublement de la détection !

  18. #18
    Modérateur

    Homme Profil pro
    Ingénieur électricien
    Inscrit en
    Septembre 2008
    Messages
    1 277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur électricien

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 277
    Par défaut
    Bonjour

    Je me suis fais ba...é par la description du registre
    When an edge or logic change on the INT0 pin triggers an interrupt request, INTF0 becomes set (one). If the I-bit in SREG
    and the INT0 bit in EIMSK are set (one), the MCU will jump to the corresponding interrupt vector. The flag is cleared when
    the interrupt routine is executed. Alternatively, the flag can be cleared by writing a logical one to it.
    Si le Flag est à un, il déclenche l'interruption, mais il faut écrire un 1 pour le faire passer à 0.
    Remplacer EIFR &= ~(1<<INTF0); par EIFR |= 1<<INTF0;.
    Avec cette astuce, on est dans un des cas où la fonction bit() peut poser problème. Car c'est un des registres où la lecture n'est pas égale à l'écriture.

    Si EIMSK |= 1<<INT0; fonctionne c'est que attachInterrupt lie la fonction passée en paramètre à l'interruption et detachInterrupt retire ce lien. Donc quand on fait cela, l'interruption est lancée, mais il n'y a plus de fonction à exécuter. Je suis étonné que cela ne plante pas simplement le micro (que cela provoque un Reset ou un fonctionnement aléatoire). Cela je ne l'ai vu que hier soir après ma réponse et j'ai compris l'implication que maintenant.

    Delias

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Utilisation du mode Wait For Interrupt sur STM8L151G6
    Par wiloui dans le forum Embarqué
    Réponses: 2
    Dernier message: 27/06/2014, 20h02
  2. [16f84] Aide pour une interruption sur port B
    Par spedy dans le forum Autres architectures
    Réponses: 3
    Dernier message: 12/09/2011, 18h08
  3. Problème d'interruption sur PIC16F877
    Par fidchell456 dans le forum C
    Réponses: 3
    Dernier message: 22/06/2010, 10h08
  4. Interruption sur RB0 [langage C]
    Par jorg1n dans le forum C
    Réponses: 11
    Dernier message: 28/02/2008, 16h57
  5. Interruption sur port série
    Par psl dans le forum VB 6 et antérieur
    Réponses: 1
    Dernier message: 15/06/2007, 20h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo