Comment utiliser correctement les variables volatiles dans Arduino?

Je faisais un petit projet avec un Arduino Uno. Cela impliquait des interruptions car jutilise des encodeurs pour mesurer à quel point le système de roue différentielle avance. Mon robot avance uniquement. Je nutilise donc quun seul canal de chaque encodeur. Voici mes deux routines dinterruption:

ISR (INT0_vect){ encoderRPos = encoderRPos + 1; } ISR (INT1_vect){ encoderLPos = encoderLPos + 1; } 

Les variables encoderRPos et encoderLPos sont de type volatile int. Je comprends que les variables qui subissent des changements dans toute routine dinterruption doivent être de type volatile. Cest pour avertir les autres parties du code qui utilisent ces variables que cela peut changer à tout moment.

Mais ce qui sest passé dans mon code était un peu étrange et je ne pouvais pas lexpliquer. Voici comment je calcule la distance déplacée par la roue gauche:

 #define distancePerCount 0.056196868 float SR = distancePerCount * (encoderRPos - encoderRPosPrev); float SL = distancePerCount * (encoderLPos - encoderLPosPrev); encoderRPosPrev = encoderRPos; encoderLPosPrev = encoderLPos; 

Mais lorsque jimprime ce qui suit sur mon moniteur série, je remarque une anomalie:

entrez la description de limage ici

Si vous regardez le troisième colonne, (SL) sa valeur est trop élevée juste pour un certain temps. Cela bouleverse tous mes calculs.

Le seul indice que je peux obtenir, si je prends la valeur de SL que jai ( 3682), qui est toujours une constante, et recalculer (encodeLPos - encoderLPosPrev), jobtiendrai 65519,66, ce qui est proche de la valeur maximale de unsigned int . Ce qui signifie que (encoderLPos - encoderLPosPrev) provoque un débordement alors que les deux valeurs dont la différence est prise se situent aux alentours de 5000 seulement!

Et jai réussi à le résoudre. Cétait en chance. Voici comment jai modifié le code:

 static int encoderRPosPrev = 0; static int encoderLPosPrev = 0; int diffL = (encoderLPos - encoderLPosPrev); int diffR = (encoderRPos - encoderRPosPrev); float SR = distancePerCount * diffR; float SL = distancePerCount * diffL; encoderRPosPrev = encoderRPos; encoderLPosPrev = encoderLPos; 

Je ne peux « pas comprendre ce qui sest passé. Y a-t-il quelque chose à propos des variables volatiles que jaurais dû connaître?

Mise à jour: Voici le code complet si vous voulez jeter un œil. Et cela fonctionne très bien après avoir changé ce qui a été suggéré dans la réponse acceptée.

Commentaires

  • Votre question dit ce que la troisième colonne de le résultat est … quelles sont les autres colonnes? Veuillez éditer la question et ajouter des en-têtes de colonne
  • @ jwpat7 Je les ai supprimées intentionnellement car cela ne fera que dérouter le lecteur. Mais la question a déjà été bien répondue par Majenko.
  • Il est difficile de donner des réponses détaillées à partir de vos extraits. could you explain why it is not happening randomly but at a specific time every time I run the code? Also why does it give the particular value? – Je pourrais probablement le faire si je voyais tout le code. En attendant, lisez ceci: gammon.com.au/interrupts
  • @NickGammon Voilà: paste.ubuntu.com / 14085127
  • 3683 / .056196868 = 65537 il semble donc avoir été incrémenté au mauvais moment, oui? Vous accédez à une variable qui pourrait être modifiée lors dune interruption plusieurs fois dans ce code, donc obtenir une copie locale, alors que les interruptions sont désactivées, serait beaucoup plus sûr.

Réponse

Vous devez en savoir plus sur les sections critiques .

Quoi Il se produit probablement que les variables sont modifiées par les routines dinterruption à mi-chemin des calculs. Votre «correctif» réduit le temps passé à faire le calcul avec les variables volatiles, ce qui réduit le risque de collision.

Ce que vous devez faire est de copier les variables volatiles dans des variables locales avec les interruptions désactivées pour cela courte période.

cli(); int l = encoderLpos; int r = encoderRpos; sei(); 

Parce que lArduino est un processeur 8 bits, il faut plusieurs instructions dassemblage pour effectuer des opérations mathématiques sur des valeurs 16 bits. La virgule flottante est encore pire en utilisant de nombreuses instructions pour un simple ajout. La division et la multiplication en utilisent beaucoup plus. Une interruption a de nombreuses occasions de se déclencher pendant cette liste dinstructions. En effectuant une affectation comme celle-là, puis en utilisant les nouvelles variables locales dans vos calculs, les instructions nécessaires pour traiter les variables volatiles sont réduites au minimum absolu. En désactivant les interruptions pendant laffectation, vous garantissez que les variables ne pourront jamais être modifiées pendant que vous les utilisez. Cet extrait de code est appelé une section critique .

Commentaires

  • Cela pourrait bien être le cas. Vous vous demandez simplement pourquoi cela ne se produit pas au hasard, mais à un moment précis chaque fois que jexécute le code? Aussi pourquoi cela donne-t-il la valeur particulière?
  • Voici une excellente référence au cli / sei. nongnu.org/avr-libc/user-manual/… . Avec la barrière mémoire, la déclaration volatile nest pas vraiment nécessaire dans le code ci-dessus. Voici quelques lectures amusantes sur ce sujet. noyau .org / doc / Documentation / volatile-consider-nocif.txt
  • @MikaelPatel Nice, mais pas si pertinent pour les MCU.Volatile est nécessaire dans cette situation pour empêcher le compilateur doptimiser les instances où il pense que ‘ nest pas utilisé (la valeur ne change jamais). Le cli / sei est là pour faire de lopération atomique WRT le seul autre thread (interruptions) qui sexécute.
  • Avez-vous essayé de compiler le code avec et sans volatile? Mais avec la section critique (cli / sei). Ce que jessaie de discuter est le concept de la barrière de la mémoire et comment cela fournit un accès volatil (et un ordre correct) du compilateur avec lobligation de déclarer des variables comme volatiles. La plupart des programmeurs apprennent que toute variable accessible dans un ISR doit être déclarée volatile, mais il y a tellement plus dans cette histoire.
  • Je ne ‘ pense pas que le compilateur a beaucoup de concepts sur ce que font cli () et sei () et comment cela affecterait des choses comme loptimisation de variables qui ne devraient pas ‘ être optimisées. Tout ce que sei () et cli () font est de manipuler le drapeau activé dinterruption globale dans son registre. Ils ne font rien pour le flux de code.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *