Verbesserte Anzeige des Arduino-Drehgebers

Drehgeber sind großartige Eingabegeräte für Elektronikprojekte - hoffentlich wird dieses Instructable Sie inspirieren und Ihnen helfen, eines in Ihrem nächsten Projekt zu verwenden.

Warum Drehgebercode schreiben?

Ich wollte einen kostengünstigen Drehgeber als Eingabemechanismus für eines meiner anstehenden Projekte verwenden und war zunächst verwirrt über die verfügbaren Codeoptionen, um Messwerte vom Drehgeber abzulesen und festzustellen, wie viele "Arretierungen" oder Zyklen der Geber vorbei und geklickt hat in welche Richtung. Ich denke, meine Hauptskizze muss den größten Teil des Arduino-Speichers verwenden, daher vermeide ich die verschiedenen verfügbaren Encoder-Bibliotheken, die schwierig zu funktionieren schienen, als ich einige davon ausprobierte. Sie scheinen auch weit mehr des Codebudgets zu verbrauchen als die skizzenbasierten Code-Ansätze, die von nun an diskutiert werden.

Wenn Sie nur das Denken hinter meinem Ansatz umgehen und direkt in das Instructable einsteigen möchten, können Sie mit Schritt 1 fortfahren!

Andere Ansätze

Einige der wichtigsten skizzenbasierten Ansätze (dh sie verwenden keine Bibliothek) werden im Blog-Beitrag von rt erörtert, in dem sie Drehgebercode schreiben, mit dem die billigsten Geber als Arduino-Eingaben verwendet werden können. Sie haben auch ein gutes Beispiel für das Logiksignal, das der Encoder erzeugt. rt stellte fest, dass ein Timer-Interrupt-System für sie am besten funktioniert, aber ich befürchte, dass die Abruffrequenz die Geschwindigkeit der Bildschirmaktualisierung in der Hauptschleife meiner Projektskizze beeinträchtigen würde. Angesichts der Tatsache, dass sich der Drehgeber für einen winzigen Teil der Zeit bewegt, in der der Bildschirm aktualisiert werden soll, scheint dies für meine Anwendung nicht geeignet zu sein.

Ich habe mich dafür entschieden, Steve Spences Code hier zu verwenden, was für sich genommen in Ordnung war, aber anscheinend sehr langsamer wurde, als ich den Rest meines Skizzencodes einbaute (was das Schreiben von Display-Updates auf einen kleinen TFT-Bildschirm beinhaltet). Anfangs habe ich mich gefragt, ob es daran liegen könnte, dass die Hauptschleife eine Debounce-Anweisung enthält.

Ich habe dann Olegs Drehgeber-Artikel über eine Interrupt-Service-Routine-Version seines vorherigen Beitrags gelesen. Ich dachte auch, es wäre eine gute Idee, die direkte Port-Manipulation zu verwenden, um beide Pins gleichzeitig und sobald der Interrupt ausgelöst wird, zu lesen. Sein Code kann an jedem Eingangspin verwendet werden, wenn der Portmanipulationscode neu geschrieben wird. Im Gegensatz dazu habe ich beschlossen, nur die Hardware-Interrupts an den digitalen Pins 2 und 3 zu verwenden, sodass wir Interrupts so einstellen können, dass sie nur bei einer ansteigenden Flanke der Pin-Spannung ausgelöst werden und nicht bei einer Änderung der Pin-Spannung, einschließlich fallender Flanken. Dies reduziert die Häufigkeit, mit der der ISR aufgerufen wird, und lenkt von der Hauptschleife ab.

Olegs Code verwendet eine Nachschlagetabelle, um die kompilierte Codegröße auf eine wirklich kleine Größe zu reduzieren, aber ich konnte keine zuverlässigen Ergebnisse erzielen, die sowohl eine sehr langsame als auch eine relativ schnelle Rotation erfassen würden. Denken Sie daran, dass das Entprellen von Hardware (siehe Schritt 2) bei der Zuverlässigkeit der Messwerte sehr hilfreich sein kann. Ich war jedoch auf der Suche nach einer Softwarelösung, um den Hardware-Build zu vereinfachen und für andere Hardwareanwendungen so portabel wie möglich zu sein.

Damit ist die Einführung meiner Herausforderung und meiner Überlegungen abgeschlossen. In Schritt 2 werfen wir einen Blick auf die Encoderhardware, die Terminologie und einige praktische Überlegungen, wenn Sie einen Drehgeber in Ihr Projekt integrieren möchten.

Schritt 1: Ein bisschen über Drehgeber

Warum sind Drehgeber so cool?

  1. Im Gegensatz zu einem variablen Widerstand / Potentiometer haben sie eine unendliche Bewegung in jede Richtung und da sie einen digitalen "Grau-Code" erzeugen, können Sie ihre Messwerte auf einen beliebigen Bereich skalieren.
  2. Die doppelte Richtung macht sie nützlich, um einen Wert innerhalb einer Variablen zu erhöhen oder zu verringern oder um in Menüs zu navigieren.
  3. Schließlich sind viele dieser Drehgeber mit einem mittleren Druckknopf ausgestattet, mit dem Sie Menüelemente auswählen, einen Zähler zurücksetzen oder alles tun können, was Sie sich für einen momentanen Druckknopf vorstellen können.

Bedingungen

  1. PPR: Impulse pro Umdrehung - normalerweise 12, 20 oder 24. Möglicherweise sehen Sie auch Spezifikationen für die maximale Umdrehung in U / min usw. Dies wird wahrscheinlich durch die Neigung des Encoders zum "Abprallen" von Kontakten bestimmt - siehe unten.
  2. Arretierung: das kleine Klicken der Aktion, wenn sie zwischen den Impulsen zu einem natürlichen Ruhepunkt springt . Es kann eine Arretierung pro Impuls / Zyklus (nicht gleich einer Drehung der Welle) oder zwei geben.
  3. Sprungkraft: Mechanische Kontakte im Inneren des Encoders springen buchstäblich so weit, dass sie beim Drehen von einem Kontakt ab- und zurückspringen können, was möglicherweise zu zu vielen Messwerten führt, die dieser Phase des Fahrens zwischen den Arretierungen zugeschrieben werden.
  4. Entprellen: Dies kann entweder in Hardware erfolgen, möglicherweise mit einem Keramikkondensator mit niedrigem Wert zwischen jedem Pin und Masse, oder in Software, möglicherweise mit einer Verzögerung. In beiden Fällen besteht das Ziel darin, ein System zu schaffen, das springende Kontakte ignoriert.

Tipps

  1. Achten Sie auf einen Gewindeabschnitt in der Nähe der Wellenbasis und eine passende Mutter, wenn Sie Ihren Encoder in einer Platte oder einem Gehäuse montieren möchten.
  2. Für Drehgeber sind viele Knöpfe erhältlich, wobei die am leichtesten verfügbaren in Wellen mit 6 mm Durchmesser erhältlich sind.
  3. Achten Sie darauf, ob Ihre Encoderwelle eine flache Fläche oder Keile verwendet, um eine korrekte Passform mit dem Knopf zu erzielen.
  4. Der Körper des Drehgebers kann auch mit einem erhöhten Stift / Stummel ausgestattet sein, der mit einer kleinen Vertiefung / einem kleinen Loch in Ihrem Bedienfeld (wahrscheinlich durch Ihren Knopf verdeckt) zusammenpassen soll und verhindert, dass sich Ihr Geber dreht, wenn Sie den Knopf drehen. Möglicherweise möchten Sie dies entfernen, wenn Sie genügend Reibung erzeugen können, um eine Drehung des Geberkörpers zu verhindern. Verwenden Sie dazu die Befestigungsschraube, um den Geber in die Schalttafel oder das Gehäuse einzuschrauben.
  5. Stellen Sie sicher, dass Sie herausfinden, wo sich der Rastzustand für Ihren Encoder befindet, und passen Sie Ihren Code entsprechend an. In meinem Beispiel wird ein Encoder verwendet, dessen Pins beide von Masse getrennt sind und von ihren jeweiligen Eingangs-Pullup-Widerständen hochgezogen werden. Dies treibt meine Auswahl eines RISING-Interrupts an. Wenn beide Pins in der Arretierung mit Masse verbunden wären, würden sie einen Code benötigen, der nach der FALLING-Pin-Spannung sucht.

Schritt 2: Die Schaltung

Die Schaltung ist so einfach. Du wirst brauchen:



• Ein ATMEGA328P-basiertes Arduino wie Uno, Pro Mini oder Nano.
• Ein mechanischer (im Gegensatz zu einem optischen) Quadratur-Drehgeber - dies ist die häufigste Art. Machen Sie sich also keine Sorgen, wenn er nicht spezifiziert ist. eBay- und Aliexpress-Angebote erwähnen Arduino häufig in der Beschreibung und dies ist ein guter Indikator dafür, dass eines geeignet ist.
• Anschlusskabel / Überbrückungskabel.
• Optional: ein Prototyping-Steckbrett.


Suchen Sie zunächst nach einer Sammlung von drei Stiften auf einer Seite des Encoders. Dies sind die drei zur Messung der Rotation mit unserem Code. Wenn auf einer anderen Seite zwei Stifte zusammen sind, sind diese wahrscheinlich für den mittleren Druckknopf bestimmt. Wir werden diese vorerst ignorieren.

Von den drei Pins zusammen ist der Encoder-Erdungsstift mit dem Arduino-Erdungsstift verbunden. Einer der beiden anderen Pins ist mit dem digitalen Pin 2 verbunden, und der verbleibende ist mit dem digitalen Pin 3 verbunden. Wenn Ihre Drehrichtung nicht Ihren Wünschen entspricht, tauschen Sie einfach die beiden nicht geerdeten Pins aus.

Die Pins 2 und 3 sind wichtig, da sie bei den ATMEGA328P-basierten Arduinos die einzigen Pins sind, die RISING- und FALLING-Pinwechsel-Interrupts erkennen können. Die MEGA 2560-Karten usw. verfügen über andere Hardware-Interrupt-Pins, die dies tun können.

Hinweis: In der Abbildung ist der Erdungsstift einer der Endstifte. In der Realität ist der Erdungsstift häufig der Mittelstift. Dies ist jedoch nicht immer der Fall. Lesen Sie daher das Datenblatt oder testen Sie Ihren Encoder, um herauszufinden, welcher Stift geerdet ist.

Noch ein Hinweis: ArneTR hat einen guten Kommentar dazu abgegeben, dass für die gezeigte Drehgeberschaltung keine separat verdrahtete Verbindung zur logisch positiven Spannung (z. B. 5 V oder 3, 3 V) besteht. Der Arduino kann den Drehgeber nicht ohne ein Erdungssignal (an das wir einen Draht angeschlossen haben) und die Logikspannung (manchmal als Vcc oder Vdd bezeichnet) lesen. Wie kann der Arduino also die Logik von diesem Geber ohne Positiv lesen? Spannungskabel? Die Antwort ist, dass der ATMEGA328P-Chip im Arduino einen speziellen Modus hat, den Sie an den von uns verwendeten digitalen Pins einstellen können, bei denen ein Pin von einem internen Widerstand automatisch "hoch" auf die logische Spannung gezogen wird. Suchen Sie im Code nach "pinMode (pinX, INPUT_PULLUP)", um zu sehen, wie wir dem Arduino mitteilen, dass wir diesen Modus nutzen möchten. Einmal eingestellt, müssen wir den Encoder nur mit einem Erdungskabel versehen, da die Sensordrähte von den digitalen Pins bereits die Logikspannung liefern.

Noch etwas ... Githyuk stellte fest, dass ein bestimmter Marken-Encoder mit dieser Vorgehensweise nicht funktioniert hat (dh mit dem folgenden Code). Weitere Informationen finden Sie im Kommentarbereich. Im Allgemeinen ist es jedoch ein guter Debugging-Schritt, einen anderen Encoder zu verwenden, wenn Sie die einfacheren / schnelleren / billigeren Schritte ausgeschöpft haben.

Schritt 3: Der Code

Wenn Sie mit der Programmierung von Arduinos nicht vertraut sind, informieren Sie sich bitte über diese Ressource von Arduino.

Dieser Code ist für Ihre Verwendung kostenlos (wie kostenlos und kann nach Belieben geändert werden). Bitte geben Sie an, wo Sie sollten.


/ ******* Interrupt-basierte Drehgeberskizze *******
von Simon Merrett, basierend auf Erkenntnissen von Oleg Mazurov, Nick Gammon, RT, Steve Spence
* /

statisch int pinA = 2; // Unser erster Hardware-Interrupt-Pin ist der digitale Pin 2
statisch int pinB = 3; // Unser zweiter Hardware-Interrupt-Pin ist der digitale Pin 3
flüchtiges Byte aFlag = 0; // Lassen Sie uns wissen, wenn wir erwarten, dass eine ansteigende Flanke an PinA signalisiert, dass der Encoder in einer Arretierung angekommen ist
flüchtiges Byte bFlag = 0; // Lassen Sie uns wissen, wenn wir erwarten, dass eine ansteigende Flanke an PinB signalisiert, dass der Encoder in einer Arretierung angekommen ist (entgegengesetzte Richtung, wenn aFlag gesetzt ist).
flüchtiger Byte-EncoderPos = 0; // Diese Variable speichert unseren aktuellen Wert der Encoderposition. Wechseln Sie zu int oder uin16_t anstelle von Byte, wenn Sie einen größeren Bereich als 0-255 aufzeichnen möchten
flüchtiges Byte oldEncPos = 0; // speichert den letzten Encoderpositionswert, damit wir ihn mit dem aktuellen Messwert vergleichen und sehen können, ob er sich geändert hat (damit wir wissen, wann auf dem seriellen Monitor gedruckt werden muss)
flüchtiges Byte Lesen = 0; // irgendwo, um die direkten Werte zu speichern, die wir von unseren Interrupt-Pins lesen, bevor wir prüfen, ob wir eine ganze Arretierung bewegt haben

void setup () {
pinMode (pinA, INPUT_PULLUP); // PinA als Eingang setzen, HIGH auf die logische Spannung ziehen (5V oder 3, 3V in den meisten Fällen)
pinMode (pinB, INPUT_PULLUP); // PinB als Eingang setzen, HIGH auf die logische Spannung ziehen (5V oder 3, 3V in den meisten Fällen)
attachInterrupt (0, PinA, RISING); // setze einen Interrupt auf PinA, suche nach einem steigenden Flankensignal und führe die Interrupt Service Routine "PinA" aus (unten)
attachInterrupt (1, PinB, RISING); // setze einen Interrupt auf PinB, suche nach einem steigenden Flankensignal und führe die Interrupt Service Routine "PinB" aus (unten)
Serial.begin (115200); // Starten Sie die serielle Monitorverbindung
}}

void PinA () {
cli (); // Stoppt Interrupts, bevor wir Pin-Werte lesen
Lesen = PIND & 0xC; // Alle acht Pin-Werte lesen und dann alle Werte außer PinA und PinB entfernen
if (Reading == B00001100 && aFlag) {// Überprüfen Sie, ob beide Pins in der Arretierung (HIGH) sind und ob wir eine Arretierung an der ansteigenden Flanke dieses Pins erwarten
encoderPos -; // Dekrementiere die Positionszahl des Encoders
bFlag = 0; // Flaggen für die nächste Runde zurücksetzen
aFlag = 0; // Flaggen für die nächste Runde zurücksetzen
}}
sonst wenn (Lesen == B00000100) bFlag = 1; // signalisieren, dass wir erwarten, dass PinB den Übergang zur Rastung aus der freien Rotation signalisiert
sei (); // Interrupts neu starten
}}

void PinB () {
cli (); // Stoppt Interrupts, bevor wir Pin-Werte lesen
Lesen = PIND & 0xC; // Alle acht Pin-Werte lesen und dann alle Werte außer PinA und PinB entfernen
if (Lesen == B00001100 && bFlag) {// Überprüfen Sie, ob beide Stifte in der Arretierung (HIGH) sind und ob wir eine Arretierung an der ansteigenden Flanke dieses Pins erwarten
encoderPos ++; // Inkrementiere die Positionszahl des Encoders
bFlag = 0; // Flaggen für die nächste Runde zurücksetzen
aFlag = 0; // Flaggen für die nächste Runde zurücksetzen
}}
sonst wenn (Lesen == B00001000) aFlag = 1; // signalisieren, dass wir erwarten, dass PinA den Übergang zur Rastung aus der freien Rotation signalisiert
sei (); // Interrupts neu starten
}}

void loop () {
if (oldEncPos! = encoderPos) {
Serial.println (encoderPos);
oldEncPos = encoderPos;
}}
}}

Das ist es!

Anhänge

  • rotaryEncoder.ino Herunterladen

Schritt 4: Fazit

Ich hoffe, Sie finden diesen Code nützlich für Ihr nächstes Projekt, das einen Drehgeber verwendet, oder dass er Sie dazu inspiriert hat, einen Drehgeber als Eingabe für Ihr nächstes Projekt zu betrachten.

Zusammenfassung der Ziele

Ich habe versucht, einen Code zu schreiben, der eine gute Balance von Folgendem erreicht:

  • Portabilität (Port-Manipulationscode ist der Kompromiss beim Wechsel zu anderen Chips)
  • Geschwindigkeit (Portmanipulation hilft wirklich)
  • Geringe kompilierte Codegröße (Portmanipulation und Bitmath helfen)
  • Zeichnet zuverlässig langsame und schnelle manuelle Drehung auf
  • Reduzierte nugatorische Interrupt-Serviceroutinenaufrufe (mit RISING-Interrupt und vorübergehender Deaktivierung von Interrupts)

Vorsichtsmaßnahmen und Verbesserungsvorschläge

Dieser Code ist keineswegs perfekt und Sie möchten ihn möglicherweise ändern, um andere Pins zu verwenden. Ich habe diesen Code mit der Skizze getestet, die mit den anderen diskutierten Ansätzen die meisten Verzögerungen und die am wenigsten zuverlässigen Messwerte verursachte. Ich habe ihn sicherlich nicht mit Timern verglichen, um festzustellen, wessen Code weniger nugatorische Interrupt-Serviceroutinen erzeugt, die geringste Zeit für die Ausführung benötigt oder filtert den höchsten Prozentsatz an Kontaktsprüngen heraus. Vielleicht möchte jemand einen Benchmark-Test gegen die anderen Ansätze durchführen.

Ähnlicher Artikel