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 :

Problème "while" et "do while"


Sujet :

Arduino

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Futur Membre du Club
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2019
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 71
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Septembre 2019
    Messages : 4
    Par défaut Problème "while" et "do while"
    Bonjour,
    je programme depuis 1976 dans divers langages et sur divers microprocesseurs ou micro contrôleurs.
    j'ai commencé avec les cartes ARDUINO il 5 ou 6 ans et je n'ai jamais pu exploiter les instructions "while" ou "do while" que se soit sur UNO, MEGA ou DUE,
    via 5 versions d'IDE (la dernière 2.0.1) et 3 PC différents avec différents OS, VISTA, ..........., WINDOWS10.
    j'ai bien sûr consulté les forums et bien que me conformant aux exemples, ça ne marche pas, ecxepté pour le DUE ou je rajoute une instruction d'un cycle (inTerrups()) avant la boucle, ce qui n'est pas très satisfaisant, à croire que l'addresse de retour de boucle est décalée d'un cycle.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    inTerrupts();
    while(condition)
      {
         // do something
     
      }
    un exemple type sur UNO:
    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
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    void setup()
    {
    #include <arduino.h>
    //  timeout = 15000;
    //  b_timeout = false;
      interrupts();
    }
     
    void loop()
      {
      timeout = 15000;
      b_timeout = false;
     
      while(!b_timeout)
       {
          cmpt++;  
       }
    //  if(b_timeout)
    //    {
        noInterrupts();
    //    }
      }
     
    // interrupt routine basée sur un timer cadensé à 1ms
     
    // ********************************
    // timer_interrupts
    // ********************************
    ISR(TIMER1_COMPA_vect)
      {
      // 1ms
     
      // 1s
      if(tim_1s > 1)
        {
        tim_1s--;
        }
      else
        {
        b_1s = true;  
        digitalWrite (LED_BUILTIN, !digitalRead(LED_BUILTIN)); 
        tim_1s = 500;
        }
     
      // 15s
      if(timeout > 1)
        {
        timeout--;  
        }
      else
        {
        b_timeout = true;  
        }
      }
    On peut vérifier que le flag (boolean) b_timeout ainsi que la variable (unsigned int) timeout sont bien mis à jour en supprimant la boucle "while" et en validant le "if".
    La LED clignotte 15 fois puis s'eteind avec le "if", clignotte indéfiniment avec le "while", boucle infinie.

    Quelqun peut-il m'éclairer?
    Grand merci d'avance

  2. #2
    Expert confirmé

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

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 907
    Par défaut
    le while fonctionne très bien

    Comme vous utilisez des interruptions, les variables doivent être déclarées volatile et les lecture (si plus d'un octet) ou modification dans la loop (hors ISR si les interruptions restent bloquées) doivent se faire en section critique

    postez un code qui compile avec les déclarations des variables (et c'est mieux pour lire si le code est indenté et que vous utilisez les balises de code)

  3. #3
    Membre Expert

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 632
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 632
    Par défaut
    Bonjour,

    Hormis ce que Jay a écrit et qui doit être pris en compte, il y a plein de choses à corriger :
    • tel quel le programme ne compile pas. Il ne semble pas complet, les variables globales ne sont mêmes pas déclarées.
    • pourquoi #include <arduino> et pourquoi à cet endroit ?
    • à quoi sert cmpt ?
    • à quoi sert b_1s ?
    • où est le paramétrage du timer ?
    • loop désactive les interruptions donc c'est une seule fois. Alors autant mettre le traitement dans le setup() et laisser loop() vide.
    • le test sur timeout pourrait être mis dans le else du test précédent en divisant les 15000 par 500 soit 30 car 15 s = 30 1/2 s


    Salutations

  4. #4
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 252
    Par défaut
    Bonjour,
    Comme le dit Jay M le problème vient probablement des variables globales qui doivent être déclarées en volatile si elles interviennent dans une interruption. Ce mot clé force la mise à jour de la variable dans son emplacement mémoire, sinon elle se met à jour dans le registre de travail et disparaît au moment où on dépile.

  5. #5
    Futur Membre du Club
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2019
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 71
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Septembre 2019
    Messages : 4
    Par défaut
    Citation Envoyé par Vincent PETIT Voir le message
    Bonjour,
    Comme le dit Jay M le problème vient probablement des variables globales qui doivent être déclarées en volatile si elles interviennent dans une interruption. Ce mot clé force la mise à jour de la variable dans son emplacement mémoire, sinon elle se met à jour dans le registre de travail et disparaît au moment où on dépile.
    Bonjour et merci à tous, le problème est réglé par l'ajout de "volatile" à mes déclaration de variables, néanmoins une chose m'échappe:
    Pourquoi ces variables sont elles correctement interprétées dans le programme principal (loop) par l'instruction "if" et non pas par l'instruction "while" ?
    Particularité de l'IDE ?
    Encore merci à vous.
    Cordialement

  6. #6
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 252
    Par défaut
    Citation Envoyé par pvisage Voir le message
    Bonjour et merci à tous,
    C'est Jay M qui a trouvé la solution


    Citation Envoyé par pvisage Voir le message
    Pourquoi ces variables sont elles correctement interprétées dans le programme principal (loop) par l'instruction "if" et non pas par l'instruction "while" ?
    Je ne vois pas de if dans ton loop() Du moins ils sont en commentaires.

  7. #7
    Expert confirmé

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

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 907
    Par défaut
    Citation Envoyé par pvisage Voir le message
    Pourquoi ces variables sont elles correctement interprétées dans le programme principal (loop) par l'instruction "if" et non pas par l'instruction "while" ?
    Particularité de l'IDE ?
    Non l'IDE n'a absolument rien à voir avec tout cela, ce n'est grosso modo qu'un éditeur de texte.

    Le comportement que vous voyez dépend des optimisations que le compilateur fait et vous pouvez avoir des comportements différents en ne modifiant qu'une instruction.

    par exemple pour un if il faut aller lire la valeur une fois en mémoire et donc elle sera donc juste, mais pour un while, une fois la variable lue —*si elle n'est pas déclarée volatile —*il va continuer à utiliser le registre puisque c'est plus rapide.

    avec volatile vous dites au compilateur "la variable peut changer pendant que que tu effectues cette boucle, donc ne fait pas confiance au registre, va lire et écrire toujours en mémoire".

    Comme dit aussi précédemment, si l'accès à la variable n'est pas atomique, il faut soit faire le test ou la boucle en section critique (interruptions suspendues) mais c'est dommageable de suspendre les interruptions trop longtemps, donc le mieux c'est de faire juste une copie de la variable en section critique, rétablir les interruptions et travailler avec la copie

  8. #8
    Expert confirmé

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

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 907
    Par défaut
    essayez cela sur votre ESP32

    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
     
    hw_timer_t *leTimer = NULL;
     
    unsigned long long valeur = 0; 
     
    void IRAM_ATTR declenchement() {
      valeur = ~valeur; // alterne 0xFFFFFFFFFFFFFFFF et 0x0
    }
     
     
    void setup() {
      Serial.begin(115200);
      leTimer = timerBegin(0, 80, true);                    // timer 0, prescaler, increment
      timerAttachInterrupt(leTimer, &declenchement, true);  // on attache l'interruption
      timerAlarmWrite(leTimer, 100, true);                  // déclenche toutes les 100µs avec réarmement
      timerAlarmEnable(leTimer);                            // activation
    }
     
    void loop() {
      if ((valeur != 0xFFFFFFFFFFFFFFFF) && (valeur != 0x0)) {
        Serial.println("C'est louche !");
      }
    }
    puis testez cela
    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
     
    hw_timer_t *leTimer = NULL;
     
    volatile unsigned long long valeur = 0;
     
    void IRAM_ATTR declenchement() {
      valeur = ~valeur; // alterne 0xFFFFFFFFFFFFFFFF et 0x0
    }
     
     
    void setup() {
      Serial.begin(115200);
      leTimer = timerBegin(0, 80, true);                    // timer 0, prescaler, increment
      timerAttachInterrupt(leTimer, &declenchement, true);  // on attache l'interruption
      timerAlarmWrite(leTimer, 100, true);                  // déclenche toutes les 100µs avec réarmement
      timerAlarmEnable(leTimer);                            // activation
    }
     
    void loop() {
      noInterrupts();
      unsigned long long copie = valeur;
      interrupts();
      if ((copie != 0xFFFFFFFFFFFFFFFF) && (copie != 0x0)) {
        Serial.println("C'est louche !");
      }
    }

    Ce code déclare une variable de 64 bits qui va alterner entre 0x0 et 0xFFFFFFFFFFFFFFFF toutes les 100µs

    l'ESP32 est une architecture 32 bits et il ne sait pas comparer deux valeurs de 64 bits de manière atomique et le && n'est pas atomique non plus. il ne sait pas non plus stocker 64 bits dans un registre.

    Comme je bascule la valeur toutes les 100µs, il y a de forte chance pour que l'interruption se produise de temps en temps pendant une des deux comparaisons et conduise au fait que le if ne voit ni 0 ni 0xFFFFFFFFFFFFFFFF pour diverses raisons (par exemple c'était 0 quand il comparait avec 0xFFFFFFFFFFFFFFFF et c'était passé à 0xFFFFFFFFFFFFFFFF quand il compare à 0, ou alors comme la comparaison se fait en deux temps (partie basse sur 32 bits et partie haute ensuite de manière non atomique) il se peut que le premier test voit les 4 octets bas à 0 et les 4 octets haut à 0xFFFFFFFF, ....)

    Dans le second code on fait les choses correctement, valeur est volatile et sa lecture (pour faire une une copie) se fait en section critique ce qui rend le test de facto atomique

    je n'ai pas essayé mais je suis pratiquement sûr que le premier code vous affichera de temps en temps "C'est louche !" alors que le second code ne le fera jamais.

    En jouant sur la fréquence on peut sans doute minimiser (par exemple essayer 1ms au lieu de 1100µs) ou augmenter les cas où "C'est louche !" apparait mais la loi de Murphy vous garantit que si un problème peut arriver, il arrivera (au pire moment bien sûr)


    je vous laisse tester.

  9. #9
    Membre prolifique Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    Février 2011
    Messages
    6 883
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : Février 2011
    Messages : 6 883
    Par défaut
    Salut Jay M.

    J'ai testé le premier programme. En 1/4 d'heure, le message est apparu que deux fois seulement. Si j'appuie sur le bouton RESET, j'ai plein de messages.
    J'ai testé le deuxième programme et j'ai plein de messages.

    J'ai fusionné les deux programmes afin d'en faire un seul, que voici :
    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
    volatile	unsigned long long	 _Val;
    		unsigned long long	*_Cop;
     
    		hw_timer_t		*_Timer = NULL;
     
    void IRAM_ATTR onTrigger()
    {
    	 _Val = ~_Val;		// alterne 0xFFFFFFFFFFFFFFFF et 0x0
    }
     
    void setup()
    {
    	_Cop = (unsigned long long *)&_Val;
    	_Val = 0;
     
    	Serial.begin(115200);
     
    	_Timer = timerBegin(0, 80, true);
    	timerAttachInterrupt(_Timer, &onTrigger, true);
    	timerAlarmWrite(_Timer, 100, true);
    	timerAlarmEnable(_Timer);
     
    	delay(500);
    	Serial.println("\e[1;33mDébut\e[0m");
    }
     
    void loop()
    {
    	if (( _Val != 0xFFFFFFFFFFFFFFFF) && ( _Val != 0x0))	Serial.println("\e[1;32mVAL : C'est louche !\e[0m");
    	if ((*_Cop != 0xFFFFFFFFFFFFFFFF) && (*_Cop != 0x0))	Serial.println("\e[1;31mCOP : C'est louche !\e[0m");
    }
    Il faut attendre longtemps pour voir apparaitre les messages. Je constate que j'ai une fréquence plus importante pour le test sur _Val (x4) que sur _Cop (x1).

    Je pense que cela ne démontre pas l'utilité du volatile mais d'un problème de synchronisation des tests dans loop() vis-à-vis du déclencheur pnTrigger().
    Pour résoudre ce problème, il suffit d'installer le Mutex aussi bien dans loop() que dans onTrigger().

    Cordialement.
    Artemus24.
    @+

  10. #10
    Expert confirmé

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

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 907
    Par défaut
    Je pourrais aussi tester demain, je suis surpris que le second affiche des erreurs

    Le mutex n’empêche pas l’optimisation du compilateur qui pourrait conserver en cache une valeur
    Ici avec la valeur sur 64 bits il ne le peut pas, donc peut être ce n’est pas un assez bon exemple.

    Édit: je viens de me souvenir que noInterrupts ne fonctionne pas bien sur esp32 / freeRTOS…donc mauvais exemple…mais sur un AVR (en changeant le timer) on devrait pouvoir faire quelque chose avec ça

  11. #11
    Expert confirmé

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

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 907
    Par défaut
    ah OK - j'avais mal compris!

Discussions similaires

  1. Problème quote array PHP / SQL lettres et chiffres
    Par Irokoi dans le forum Requêtes
    Réponses: 1
    Dernier message: 18/03/2014, 09h29
  2. [SQL Serveur 2000] - Problème QUOTED-IDENTIFIER
    Par Silvia12 dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 07/06/2007, 14h17
  3. Problème quotes
    Par Anduriel dans le forum Langage
    Réponses: 6
    Dernier message: 16/11/2005, 19h04
  4. [Tableaux] problème avec while
    Par zimotep dans le forum Langage
    Réponses: 3
    Dernier message: 11/09/2005, 10h30

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