Amélioration de la lecture du codeur rotatif Arduino

Les encodeurs rotatifs sont d'excellents périphériques d'entrée pour les projets électroniques - espérons que cet Instructable vous inspirera et vous aidera à en utiliser un dans votre prochain projet.

Pourquoi écrire du code d'encodeur rotatif?

Je voulais utiliser un encodeur rotatif à faible coût comme mécanisme d'entrée pour l'un de mes projets à venir et j'ai d'abord été déconcerté par les options de code disponibles pour prendre des lectures à partir de l'encodeur rotatif et déterminer le nombre de "détentes" ou de cycles sur lesquels l'encodeur avait cliqué et dans quelle direction. Je pense que mon croquis principal devra utiliser la plupart de la mémoire de mon Arduino, donc j'évite les différentes bibliothèques d'encodeurs disponibles, qui semblaient difficiles à faire fonctionner lorsque j'en ai essayé quelques-unes. Ils semblent également utiliser beaucoup plus le budget du code que les approches de code basées sur des croquis discutées à partir d'ici.

Si vous voulez simplement contourner la pensée derrière mon approche et entrer directement dans l'Instructable, n'hésitez pas à passer à l'étape 1!

Autres approches

Plusieurs des principales approches basées sur des croquis (c'est-à-dire qu'elles n'utilisent pas de bibliothèque) sont discutées dans le blog de rt où elles écrivent du code d'encodeur rotatif qui rend les encodeurs les moins chers utilisables comme entrées Arduino. Ils ont également un bon exemple de leur signal logique produit par l'encodeur. rt a constaté qu'un système d'interruption de minuterie fonctionnait mieux pour eux, mais je crains que la fréquence d'interrogation ne nuise à la vitesse de mise à jour de l'écran dans la boucle principale de mon croquis de projet. Étant donné que l'encodeur rotatif se déplacera pendant une infime proportion du temps que je souhaite que l'écran soit mis à jour, cela semble une mauvaise correspondance pour mon application.

J'ai choisi de commencer à utiliser le code de Steve Spence ici, ce qui était bien en soi mais semblait vraiment ralentir lorsque j'ai incorporé le reste de mon code d'esquisse (ce qui implique d'écrire des mises à jour d'affichage sur un petit écran TFT). Au départ, je me demandais si cela pouvait être parce que la boucle principale contient une instruction anti-rebond.

J'ai ensuite lu l'article sur l'encodeur rotatif d'Oleg sur une version de routine de service d'interruption de son post précédent, j'ai également pensé que ce serait une bonne idée d'utiliser la manipulation directe de port pour lire les deux broches simultanément et dès que l'interruption se déclenche. Son code peut être utilisé sur n'importe quelle broche d'entrée si le code de manipulation de port est réécrit. En revanche, j'ai décidé d'utiliser uniquement les interruptions matérielles sur les broches numériques 2 et 3, afin que nous puissions définir des interruptions pour qu'elles ne se déclenchent que sur un front montant de la tension des broches, plutôt que sur le changement de tension des broches, qui inclut les fronts descendants. Cela réduit le nombre d'appels de l'ISR, distrayant de la boucle principale.

Le code d'Oleg utilise une table de recherche pour réduire la taille du code compilé à une très petite taille, mais je n'ai pas pu obtenir de résultats fiables qui entraîneraient une rotation très lente ainsi qu'une rotation raisonnablement rapide. Gardez à l'esprit que le rebouncing matériel (voir l'étape 2) peut beaucoup aider à la fiabilité des lectures, mais je recherchais une solution logicielle pour simplifier la construction matérielle et être aussi portable que possible pour d'autres applications matérielles.

Ceci conclut l'introduction de mon défi et de mes considérations. À l'étape 2, nous examinerons le matériel de l'encodeur, la terminologie et certaines considérations pratiques lorsque vous souhaitez intégrer un encodeur rotatif dans votre projet.

Étape 1: Un peu sur les encodeurs rotatifs

Pourquoi les encodeurs rotatifs sont-ils si cool?

  1. Contrairement à une résistance / potentiomètre variable, ils ont une course infinie dans toutes les directions et parce qu'ils produisent un "code gris" numérique, vous pouvez adapter leurs lectures à la plage que vous souhaitez.
  2. La double direction les rend utiles pour augmenter ou diminuer une valeur dans une variable ou pour naviguer dans les menus.
  3. Enfin, beaucoup de ces encodeurs rotatifs sont livrés avec un bouton poussoir central, qui peut être utilisé pour sélectionner des éléments de menu, réinitialiser un compteur ou faire tout ce que vous pouvez penser qui pourrait convenir à un bouton poussoir momentané.

termes

  1. PPR: impulsions par rotation - généralement 12, 20 ou 24. Vous pouvez également voir les spécifications pour une rotation maximale en tr / min, etc. Ceci est probablement déterminé par la propension du codeur à "rebondir" les contacts - voir ci-dessous.
  2. Détente: le petit clic de l'action au moment où elle jaillit vers un point de repos naturel entre les impulsions. Il peut y avoir un cran par impulsion / cycle (pas égal à une rotation de l'arbre) ou deux.
  3. Rebond: les contacts mécaniques à l'intérieur de l'encodeur rebondissent suffisamment pour sauter et revenir sur un contact lors de la rotation, ce qui peut entraîner trop de lectures attribuées à cette phase du voyage entre les détentes.
  4. Anti-rebond: Cela peut être fait soit sur le matériel, peut-être avec un condensateur en céramique de faible valeur entre chaque broche et la masse, soit sur le logiciel, peut-être avec un retard. Dans les deux cas, l'objectif est de créer un système qui ignore les contacts rebondissants.

Conseils

  1. Recherchez une section filetée près de la base de l'arbre et un écrou correspondant si vous souhaitez monter votre encodeur dans un panneau ou un boîtier.
  2. De nombreux boutons sont disponibles pour les encodeurs rotatifs, les plus facilement disponibles étant des arbres de 6 mm de diamètre.
  3. Faites attention si votre arbre de codeur utilise une face plate ou des cannelures pour obtenir un ajustement correct avec le bouton.
  4. Le corps de l'encodeur rotatif peut également être livré avec une broche / talon surélevé, destiné à s'accoupler avec un petit retrait / trou dans votre panneau (probablement caché par votre bouton) et empêcher votre encodeur de tourner lorsque vous tournez le bouton. Il se peut que vous souhaitiez supprimer cela si vous pouvez créer suffisamment de friction pour empêcher la rotation du corps de l'encodeur à l'aide du boulon de montage pour visser l'encodeur dans le panneau ou le boîtier.
  5. Assurez-vous de savoir où se trouve l'état de détente de votre encodeur et adaptez votre code en conséquence. Mon exemple utilise un encodeur dont les broches sont à la fois déconnectées de la masse et tirées haut par leurs résistances de rappel d'entrée respectives. Cela entraîne ma sélection d'une interruption RISING. Si les deux broches étaient connectées à la masse lorsqu'elles étaient en position d'arrêt, elles auraient besoin d'un code qui recherchait la tension des broches CHUTE.

Étape 2: Le circuit

Le circuit est si simple. Tu auras besoin de:



• Un Arduino basé sur ATMEGA328P, tel que Uno, Pro Mini ou Nano.
• Un encodeur rotatif mécanique (par opposition à optique) en quadrature - c'est le type le plus courant, alors ne vous inquiétez pas trop s'il n'est pas spécifié. Les listes eBay et Aliexpress mentionnent souvent Arduino dans la description et c'est un bon indicateur que l'on convient.
• Fils de raccordement / cavaliers.
• En option: une maquette de prototypage.


Tout d'abord, recherchez une collection de trois broches sur un côté de l'encodeur. Ce sont les trois pour mesurer la rotation avec notre code. S'il y a deux broches ensemble d'un autre côté, il s'agit probablement du bouton-poussoir central. Nous les ignorerons pour l'instant.

Sur les trois broches ensemble, la broche de masse de l'encodeur est connectée à la broche de terre Arduino. L'une ou l'autre des deux autres broches est connectée à la broche numérique 2 et le reste est connectée à la broche numérique 3. Si votre sens de rotation n'est pas celui que vous souhaitez, permutez simplement les deux broches non mises à la terre.

Les broches 2 et 3 sont importantes car sur les Arduinos basés sur ATMEGA328P, ce sont les seules broches qui ont la capacité de détecter les interruptions de changement de broches RISING et FALLING. Les cartes MEGA 2560, etc. ont d'autres broches d'interruption matérielles qui peuvent le faire.

Remarque: Dans le schéma, la broche de terre est l'une des broches d'extrémité. En réalité, la broche de terre est souvent la broche centrale mais ce n'est pas toujours le cas, alors lisez la fiche technique ou testez votre encodeur pour savoir quelle broche est mise à la terre.

Autre remarque: ArneTR a fait un bon commentaire sur le fait de ne pas avoir de connexion câblée séparément à la tension positive logique (par exemple 5 V ou 3, 3 V) pour le circuit du codeur rotatif illustré. L'Arduino ne peut pas lire l'encodeur rotatif sans à la fois un signal de masse (auquel nous avons connecté un fil) et la tension logique (parfois annotée Vcc ou Vdd), alors comment l'Arduino peut-il lire la logique de cet encodeur sans positif fil de tension? La réponse est que la puce ATMEGA328P de l'Arduino a un mode spécial que vous pouvez définir sur les broches numériques (que nous utilisons), où une broche est automatiquement tirée "haute" à la tension logique par une résistance interne. Regardez dans le code "pinMode (pinX, INPUT_PULLUP)" pour nous voir dire à l'Arduino que nous voulons profiter de ce mode. Une fois réglé, il suffit de fournir à l'encodeur un fil de terre car les fils de détection des broches numériques fournissent déjà la tension logique.

UNE CHOSE DE PLUS ... Githyuk a découvert qu'un encodeur de marque particulier ne fonctionnait pas avec cette façon de faire (c'est-à-dire le code ci-dessous). Veuillez consulter la section des commentaires pour plus de détails, mais en général, essayer un autre encodeur serait une bonne étape de débogage lorsque vous avez épuisé les étapes plus faciles / plus rapides / moins chères.

Étape 3: Le code

Si vous n'êtes pas familier avec la programmation d'Arduinos, veuillez vous familiariser avec cette ressource d'Arduino.

Ce code est gratuit pour votre utilisation (comme sans frais et à modifier à votre guise), veuillez attribuer où vous le souhaitez.


/ ******* Croquis de l'encodeur rotatif basé sur les interruptions *******
par Simon Merrett, basé sur la perspicacité d'Oleg Mazurov, Nick Gammon, rt, Steve Spence
* /

statique int pinA = 2; // Notre première broche d'interruption matérielle est la broche numérique 2
statique int pinB = 3; // Notre deuxième broche d'interruption matérielle est la broche numérique 3
octet volatile aFlag = 0; // nous fait savoir quand nous nous attendons à ce qu'un front montant sur pinA signale que le codeur est arrivé à un cran
octet volatile bFlag = 0; // nous fait savoir quand nous nous attendons à ce qu'un front montant sur la broche B signale que le codeur est arrivé à une détente (direction opposée à celle lorsque aFlag est défini)
octet volatile encoderPos = 0; // cette variable stocke notre valeur actuelle de la position de l'encodeur. Changez en int ou uin16_t au lieu d'octet si vous voulez enregistrer une plage plus grande que 0-255
octet volatil oldEncPos = 0; // stocke la dernière valeur de position de l'encodeur afin que nous puissions la comparer à la lecture actuelle et voir si elle a changé (afin que nous sachions quand imprimer sur le moniteur série)
lecture d'octets volatils = 0; // quelque part pour stocker les valeurs directes que nous lisons à partir de nos broches d'interruption avant de vérifier si nous avons déplacé un cran entier

void setup() {
pinMode (pinA, INPUT_PULLUP); // définir la broche A comme entrée, tirée HAUT à la tension logique (5 V ou 3, 3 V pour la plupart des cas)
pinMode (pinB, INPUT_PULLUP); // définir la broche B comme entrée, tirée HAUT à la tension logique (5 V ou 3, 3 V pour la plupart des cas)
attachInterrupt (0, PinA, RISING); // définir une interruption sur PinA, rechercher un signal de front montant et exécuter la routine de service d'interruption "PinA" (ci-dessous)
attachInterrupt (1, PinB, RISING); // définir une interruption sur PinB, rechercher un signal de front montant et exécuter la routine de service d'interruption "PinB" (ci-dessous)
Serial.begin (115200); // démarre la liaison du moniteur série
}

void PinA () {
cli (); // arrête les interruptions avant de lire les valeurs des broches
lecture = PIND & 0xC; // lire les huit valeurs de broche puis supprimer toutes les valeurs sauf celles de pinA et pinB
if (lecture == B00001100 && aFlag) {// vérifier que nous avons les deux broches au niveau de la détente (HIGH) et que nous attendons une détente sur le bord montant de cette broche
encoderPos -; // décrémente le nombre de positions de l'encodeur
bFlag = 0; // réinitialiser les drapeaux pour le prochain tour
aFlag = 0; // réinitialiser les drapeaux pour le prochain tour
}
sinon si (lecture == B00000100) bFlag = 1; // signale que nous nous attendons à ce que pinB signale la transition vers la détente de la rotation libre
sei (); // redémarre les interruptions
}

void PinB () {
cli (); // arrête les interruptions avant de lire les valeurs des broches
lecture = PIND & 0xC; // lire les huit valeurs de broche puis supprimer toutes les valeurs sauf celles de pinA et pinB
if (lecture == B00001100 && bFlag) {// vérifier que nous avons les deux broches au niveau de la détente (HIGH) et que nous attendons une détente sur le bord montant de cette broche
encoderPos ++; // incrémente le nombre de positions de l'encodeur
bFlag = 0; // réinitialiser les drapeaux pour le prochain tour
aFlag = 0; // réinitialiser les drapeaux pour le prochain tour
}
sinon si (lecture == B00001000) aFlag = 1; // signale que nous nous attendons à ce que pinA signale la transition vers la détente de la rotation libre
sei (); // redémarre les interruptions
}

boucle vide () {
if (oldEncPos! = encoderPos) {
Serial.println (encoderPos);
oldEncPos = encoderPos;
}
}

C'est ça!

Pièces jointes

  • RotaryEncoder.ino Télécharger

Étape 4: Conclusion

J'espère que vous trouverez ce code utile pour votre prochain projet qui utilise un encodeur rotatif ou qu'il vous a inspiré à considérer un encodeur rotatif comme entrée pour votre prochain projet.

Résumé des objectifs

J'ai essayé d'écrire du code qui réalise un bon équilibre de:

  • Portabilité (le code de manipulation de port est le compromis lors du passage à d'autres puces)
  • Vitesse (la manipulation de port aide vraiment)
  • Faible taille de code compilé (manipulation de port et aide de bitmath)
  • Enregistre de manière fiable une rotation manuelle lente et rapide
  • Réduction des appels de routine du service d'interruption nugatoire (en utilisant l'interruption RISING et en désactivant temporairement les interruptions)

Mises en garde et idées d'amélioration

Ce code n'est en aucun cas parfait et vous voudrez peut-être le changer pour utiliser d'autres broches. J'ai testé ce code avec l'esquisse qui causait le plus de retard et les lectures les moins fiables avec les autres approches discutées - je ne l'ai certainement pas comparé avec des minuteries pour voir dont le code produit moins de routines de service d'interruption nugatoire, prend le moins de temps à exécuter ou filtre le pourcentage le plus élevé de rebonds de contact. Peut-être que quelqu'un aimerait faire un test de référence par rapport aux autres approches.

Articles Connexes