KY040

Eine wichtige Gruppe von Bedienungselementen sind die sogenannten Inkrementalgeber. Mit Inkrementalgebern werden Lage- oder Winkeländerungen erfasst und als entsprechendes Signal ausgegeben. Eine mögliche Anwendung sind Drehregler. Der Vorteil von Drehreglern gegenüber normalen Druckknöpfen ist die hohe Einstellgeschwindigkeit bei größeren Änderungen der einzustellenden Größe, wie z.B. die Lautstärke oder die Scrollposition in einem Menü. Allerdings können mit Inkrementalgebern nur Änderungen erfasst werden. D.h. ein Referenzwert muss auf Softwareseite gespeichert bzw. initialisiert werden, der durch die Regelung geändert werden kann.
Der Inkrementalgeber KY040 ist ein Rotationsencoder für den Einsatz als Drehregler mit einem zusätzlichen Druckknopf. Die Benutzung des KY040 ist sehr simpel. In diesem Tutorial stelle ich den Wert eines Integers, initialisiert mit 0, durch Drehung eines KY040 ein und gebe diesen Wert bei jeder Änderung auf der Konsole aus. Ich stelle zwei Varianten vor, von denen die eine Variante die Drehrichtung auf Software- und die andere Variante auf Hardwareseite auswertet.

Inhalt


Material

  • KY040
  • Diverse Kabel
  • Steckbrett
  • Variante 2: 2x Inverter (74HC04)
  • Variante 2: 5x AND-Gatter (74HC08)
  • Variante 2: 2x XOR-Gatter (74HC86)

Aufbau

Mit 5 Anschlüssen ist der KY040 ein kleiner Baustein:

KY040
An + wird eine +5V Spannung angelegt, während - an GND angeschlossen wird. Der mittlere Anschluss SW ist für den Knopf und ist auf HIGH, solange er nicht gedrückt wird. Drehungen machen sich durch LOW-Pegel an DT und CLK bemerkbar. Wenn keine Drehung stattfindet, sind beide Pegel auf HIGH. Bei einer Linksdrehung ändern sich die Zustände in folgender Reihenfolge:
  1. DT: HIGH, CLK:HIGH
  2. DT: LOW, CLK:HIGH
  3. DT: LOW, CLK:LOW
  4. DT: HIGH, CLK:LOW
  5. DT: HIGH, CLK:HIGH

Bei einer Rechtsdrehung ist die Reihenfolge umgekehrt:
  1. DT: HIGH, CLK:HIGH
  2. DT: HIGH, CLK:LOW
  3. DT: LOW, CLK:LOW
  4. DT: LOW, CLK:HIGH
  5. DT: HIGH, CLK:HIGH

Es ergeben sich für die Auswertung mehrere Varianten, von denen ich zwei Vorstellen möchte.

Gesamtaufbau Variante 1

In der Variante 1 werden die Anschlüsse direkt ans Raspberry Pi angeschlossen und über die Software ausgewertet.

Gesamtaufbau Variante 1
In dieser Variante muss auf Softwareseite der Spezialfall einer unvollständigen Drehung beachtet werden.

Gesamtaufbau Variante 2

Die Variante 2 wandelt eine Drehung in ein HIGH-Pegel um, das auf Softwareseite nur noch ausgelesen werden muss.

Gesamtaufbau Variante 2
Im Falle einer Linksdrehung geht der Pegel am Anschluss 21, im Falle einer Rechtsdrehung der Pegel am Anschluss 20 auf HIGH. Dadurch spart man sich den Spezialfall einer unvollständigen Drehung auf Softwareseite. Außerdem muss auf Softwareseite nicht mehr auf Flanken an beiden Anschlüssen geachtet werden.

Programm

Die Software ist bei beiden Varianten sehr kurz. Sie unterscheiden sich darin, dass in Variante 1 die Reihenfolge der LOW-Pegel ausgewertet werden müssen, während in Variante 2 die Reihenfolge auf Hardwareseite ausgewertet wird und nur das Ergebnis der Auswertung auf Softwareseite ausgelesen wird.

Programm für Variante 1

Variante 1 wertet die LOW-Pegel an den Eingängen 20 und 21 aus. Es muss auf die Reihenfolge der LOW-Pegel geachtet werden. Die Drehrichtung ergibt sich aus der Reihenfolge gemäß den Tabellen aus dem Unterkapitel "Aufbau".
#include <wiringPi.h>
#include <iostream>

#define DT 28
#define CLK 29
#define SW 27

#define STATE_NONE 0
#define STATE_LEFT_1 1
#define STATE_LEFT_2 2
#define STATE_LEFT_3 3
#define STATE_RIGHT_1 4
#define STATE_RIGHT_2 5
#define STATE_RIGHT_3 6

void init() {
    pinMode(DT,  INPUT);
    pinMode(CLK, INPUT);
    pinMode(SW,  INPUT);
}

int main() {
    if(wiringPiSetup() == -1) {
        return 0;
    }

    init();

    int value = 0;
    int state = STATE_NONE;

    std::cout << value << std::endl;

    while(1) {
        if(digitalRead(DT) && digitalRead(CLK))  {
            state = STATE_NONE;
        }

        if(!digitalRead(DT) && digitalRead(CLK)) {
            if(state == STATE_NONE) {
                state = STATE_LEFT_1;
            }
            else if(state == STATE_RIGHT_2) {
                state = STATE_RIGHT_3;
                std::cout << ++value << std::endl;
            }
        }

        if(digitalRead(DT) && !digitalRead(CLK)) {
            if(state == STATE_NONE) {
                state = STATE_RIGHT_1;
            }
            else if(state == STATE_LEFT_2) {
                state = STATE_LEFT_3;
                std::cout << --value << std::endl;
            }
        }

        if(!digitalRead(DT) && !digitalRead(CLK)) {
            if(state == STATE_LEFT_1) {
                state = STATE_LEFT_2;
            }
            else if(state == STATE_RIGHT_1) {
                state = STATE_RIGHT_2;
            }
        }

        if(!digitalRead(SW)) {
            value = 0;
            std::cout << "Reset to zero." << std::endl;
        }

        if(value >= 50 || value <= -50) {
            break;
        }
    }

    return 0;
}
Die Signaländerungen führen zu Zustandsänderungen der Software. Man muss mit unvollständigen Drehungen umgehen können. Sollte der Anwender nach einer halben Drehung den Encoder wieder zurückdrehen, so darf Software nicht im Zustand der halben Drehung stecken bleiben, sondern muss wieder in den Ausgangszustand zurückkehren.

Programm für Variante 2

In Variante 2 muss nur auf ein HIGH-Pegel an den Eingängen reagiert werden. Die Drehrichtung ergibt sich aus dem Logik-Netzwerk, welche die LOW-Pegel des KY040 auswertet.
#include <wiringPi.h>
#include <iostream>

#define LEFT 28
#define RIGHT 29
#define SW 27

void init() {
    pinMode(LEFT,  INPUT);
    pinMode(RIGHT, INPUT);
    pinMode(SW,    INPUT);
}

int main() {
    if(wiringPiSetup() == -1) {
        return 0;
    }

    init();

    int value = 0;

    std::cout << value << std::endl;

    while(1) {
        if(digitalRead(LEFT)) {
            while(digitalRead(LEFT)) {
            }
            delay(10); // Verzögerung um Prellen vorzubeugen
            value--;
            std::cout << value << std::endl;
        }

        if(digitalRead(RIGHT)) {
            while(digitalRead(RIGHT)) {
            }
            delay(10); // Verzögerung um Prellen vorzubeugen
            value++;
            std::cout << value << std::endl;
        }

        if(!digitalRead(SW)) {
            value = 0;
            std::cout << "Reset to zero." << std::endl;
        }

        if(value >= 50 || value <= -50) {
            break;
        }
    }

    return 0;
}
Die größte Schwierigkeit in dieser Variante besteht darin, dass der KY040 nicht ganz prellfrei ist. Es muss eine kleine Verzögerung eingebaut werden, damit eine Drehung nicht mehrmals ausgewertet wird.

Leider ist bei meinem Exemplar des KY040 der Druckknopf kaputt, sodass ich diese Funktion nicht testen konnte. Ich denke aber, dass ein einfaches Auslesen des Eingangs in den meisten Fällen ausreicht. Man muss ggf. hier ebenfalls eine kleine Verzögerung einbauen, da auch dieser Schalter nicht prellfrei ist.

Ich hoffe dieses Tutorial hat euch bei der Verwendung des KY040 weitergeholfen oder euch seinen Nutzen näher gebracht und euch zum Kauf angeregt. Für weitere Informationen zum KY040 siehe:

2 Kommentare:

  1. Super Anleitung. Allerdings wäre auch nützlich zu wissen wohin mit dem programm und dateinamen und wie man es zum laufen bringt.

    AntwortenLöschen
    Antworten
    1. Hallo Dany Humm el, du kannst das Programm an einem beliebigen Ort in deinem Dateisystem mit einem beliebigen Dateinamen kompilieren und dann ausführen. Für grundlegende Fragen zur Vorbereitung des Systems und zum Kompiliervorgang siehe den Blogeintrag 'WiringPi'.

      Löschen

Hinweis: Nur ein Mitglied dieses Blogs kann Kommentare posten.