Drehencoder ungenau

Hallo zusammen,

ich bin gerade dabei, einbisschen mit 2 Drehencodern rumzuspielen.
Es handelt sich um folgende: STEC11B09: Drehimpulsegeber, 20 Impulse - 20 Rastungen, horizontal bei reichelt elektronik

Ich nutze folgendes Beispiel: Arduino Playground - RotaryEncoders

Es werden die Internen Pull-Ups benutzt, ich bin also vom Encoder direkt in meinen Arduino geganen.

Leider reagieren beide Encoder sehr ungenau. Jeder 2-3 Impuls wird verschluckt.

Ich habe am Taster des Encoder 5V anliegen. Den Taster will ich später noch verwenden, es gibt also keinen Quellcode der ihn abfragt. Aber trozdem kommt es mir so vor, als würde der Encoder besser reagieren, wenn ich den Taster gedrückt halte.

Was mache ich falsch? Ich las vom paralalschalten von einem kleinen C`?

Wer kann mir einen Tipp geben?

Vielen Dank!

Leider reagieren beide Encoder sehr ungenau. Jeder 2-3 Impuls wird verschluckt.

Bei welcher Drehgeschwindigkeit? Auch bei langsamer Drehung von Hand?

Was mache ich falsch? Ich las vom paralalschalten von einem kleinen C`?

Das würde gegen mechanisches Prellen helfen, aber bei Dir sind ja scheinbar zuwenige Pulse anliegend. Oder habe ich Dich da falsch verstanden?

Wozu willst Du die Encoder einsetzen?

Ich hatte das gleiche Problem (oder eins mit denselben Symptomen) auch und da lag es tatsächlich am Prellen - erklären kann ich mir das nur dadurch, dass eine vermeintliche Gegendrehbewegung durch das Prellen erkannt wird - passiert vor allem bei den Encodern die rasten. Ein RC-Glied (Kondensator von Signal auf Masse, Widerstand direkt in die Signalleitung) hilft da schon gut, z. B. mit C = 10 nF und R = 47k. Der Pullup sollte dann aber vor das RC Glied, die internen kannst du dann nicht mehr benutzen. Wenn du sehr schöne Signale willst, packst du noch einen Schmitttrigger (z. B. 40106) rein.

Gruß,
Marv

pylon:

Leider reagieren beide Encoder sehr ungenau. Jeder 2-3 Impuls wird verschluckt.

Bei welcher Drehgeschwindigkeit? Auch bei langsamer Drehung von Hand?

Relativ langsam von Hand. Auch bei schnellerer Drehung ist es nicht besser.

pylon:

Was mache ich falsch? Ich las vom paralalschalten von einem kleinen C`?

Das würde gegen mechanisches Prellen helfen, aber bei Dir sind ja scheinbar zuwenige Pulse anliegend. Oder habe ich Dich da falsch verstanden?

Wozu willst Du die Encoder einsetzen?

Ne das passt, bei mir kommen zu wenig Pulse an. Bastele einen kleinen MP3 Player. Das will ich dort als Navigation haben. Einmal Lieder vor+Zurück und den anderen für die Lautstärke.

MGOS:
Ein RC-Glied (Kondensator von Signal auf Masse, Widerstand direkt in die Signalleitung) hilft da schon gut, z. B. mit C = 10 nF und R = 47k. Der Pullup sollte dann aber vor das RC Glied, die internen kannst du dann nicht mehr benutzen. Wenn du sehr schöne Signale willst, packst du noch einen Schmitttrigger (z. B. 40106) rein.

Danke dann werd ich das mal ausprobieren. Mal schauen was ich noch an Kondensatoren rumfliegen habe.

Danke euch schonmal!

MGOS:
Ein RC-Glied (Kondensator von Signal auf Masse, Widerstand direkt in die Signalleitung) hilft da schon gut, z. B. mit C = 10 nF und R = 47k. Der Pullup sollte dann aber vor das RC Glied, die internen kannst du dann nicht mehr benutzen. Wenn du sehr schöne Signale willst, packst du noch einen Schmitttrigger (z. B. 40106) rein.

So habe heute ein paar Kondensatoren besorgen können. Ich habe aber noch eine Frage zum RC-Glied.
Mein mittlerer Pin ligt auf Gnd. Heist das ich muss den Kondensator von Signal auf 5V?

Viele Grüße

schmodda:
So habe heute ein paar Kondensatoren besorgen können. Ich habe aber noch eine Frage zum RC-Glied.
Mein mittlerer Pin ligt auf Gnd. Heist das ich muss den Kondensator von Signal auf 5V?

Beispielschaltung zum hardwaremäßigen Verbessern des Prellverhaltens mit Kondensatoren, Widerständen und externen PullUp-Widerständen mit einer "guten" Schaltung:

Die Arduino-Eingänge werden an "Signal A" und "Signal B" angeschlossen.

Bei falscher Entprellschaltung (z.B. nur mit Kondensatoren) können beim Drehen und Schalten der Ausgänge sehr hohe Ströme über die Drehgeber-Kontakte abfließen, so dass die Kontakte beim Benutzen verschleißen und der Drehgeber dann nicht lange halten und die Funktion vor der normalen Verschleißzeit aufgeben wird.

BTW: Den von Dir verlinkten Code zur Drehgeber-Auswertung finde ich sehr grenzwertig, genau wie ich alle Auswertecodes, die mit Hardware-Interrupts oder PinChange-Interrupts arbeiten sehr grenzwertig finde. Sowas kann ernsthaft eigentlich nicht in Betracht gezogen werden, es damit zu machen, wenn real existierende, prellende Drehgeber verwendet werden sollen.

In loop-Funktionen, die schnell genug und frei von delay-Aufrufen laufen, kann die Auswertung problemlos durch Abfrage der Eingänge in der loop erfolgen. Dafür mal einen Beispielcode:

// Drehgeber-Auswertung Demo by 'jurs' for German Arduino forum
#define ENCODER_A 2
#define ENCODER_B 3

#define ERRORCODE 9999
#define TIMEOUT 20

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(ENCODER_A, INPUT_PULLUP);
  pinMode(ENCODER_B, INPUT_PULLUP);
  encoderImpuls(); // Initialisiert den Anfangszustand des Drehgebers
}

int encoderImpuls()
{
  static byte state=0;
  state= state<<2;
  if (digitalRead(ENCODER_A)) bitSet(state,1);
  if (digitalRead(ENCODER_B)) bitSet(state,0);
  state= state & 0xF; 
  switch(state)
  {
    case 0b0000:
    case 0b0101:
    case 0b1010:
    case 0b1111: return 0; // keine Änderung an den Eingängen
    case 0b0001:
    case 0b0111:
    case 0b1110:
    case 0b1000: return -1; // links Impuls an den Eingängen
    case 0b0010:
    case 0b1011:
    case 0b1101:
    case 0b0100: return 1; // rechts Impuls an den Eingängen
    default: return ERRORCODE;  // Error (z.B. wegen verpaßter Impulse)
  }  
}


int impulseRechts=0, impulseLinks=0, impulseError=0;

boolean impulsAuswertung()
{
  static unsigned long letzterImpuls;
  switch (encoderImpuls())
  {
    case 0: if (millis()-letzterImpuls>TIMEOUT) return true;
            return false;  
    case ERRORCODE:
            impulseError++;
            break;  
    case 1: impulseRechts++;
            break;  
    case -1:impulseLinks++;
            break;  
    default: Serial.println("Internal Error in impulsAuswertung"); // sollte nie vorkommen
  }  
  letzterImpuls=millis();
  return false;
}


void loop() {
  if (!impulsAuswertung()) return;
  if (impulseRechts>impulseLinks)
  {
     Serial.print("R: ");Serial.println(impulseRechts-impulseLinks);
  }
  else if (impulseLinks>impulseRechts)
  {
     Serial.print("L: ");Serial.println(impulseLinks-impulseRechts);
  }
  if (impulseError>0)
  {
     Serial.print("E: ");Serial.println(impulseError);
  }
  impulseRechts=0;
  impulseLinks=0;
  impulseError=0;
}

Dieser Code verwendet die internen PullUp-Widerstände, ist also für "nackt" angeschlossene Drehgeber. Der Code zählt beim Drehen des Gebers, und bei Erkennung einer Drehpause von mindestens 20 Millisekunden wird angezeigt. Nur mal so zum Testen.

Hier wird bei den Quadruple-Encodern jeder einzelne Wechsel am Ausgang gezählt, Du bekommst also das vierfache von dem angezeigt, was man eigentlich als Impulse bezeichnet. In realen Programmen kann man die Logik natürlich entsprechend anpassen.

Diese Art der Auswertung mit Abfrage der Eingänge in der loop taugt nur etwas für loop-Funktionen, die mindestens 500 bis 1000 mal pro Sekunde durchlaufen werden, ohne Delays und andere Blockierungen oder länger laufenden Funktionsaufrufe zwischendrin.

Wenn die Voraussetzung in einem Sketch nicht gegeben ist, dann macht man eine gute Auswertung von Drehgebern NICHT mit Hardware-Interrupts und NICHT mit PinChange-Interrupts, sondern mit Timer-Interrupts. Und zwar vom Prinzip her so: Du programmierst einen noch ungenutzten Timer (davon gibt es nicht viele) so dass er Timer-Interrupts in regelmäßigen Zeitabständen aufruft, z.B. 1000 mal pro Sekunde. Und exakt diese 1000 mal pro Sekunde wertest Du den Status der Drehgeber-Ausgänge aus.

Ich hoffe, ich darf mich an diesen Beitrag mal dran hängen und bitte gleichzeitig um ein mildes Urteil, da ich von Hause aus kein Programmierer bin.

Auch ich habe Probleme beim Auslesen von billigen Rotary-Encodern. Wenn ich im Code nur den Encoder auslese, funktionieren die meisten Methoden/Code-Schnippsel, die ich im Internet gefunden habe mehr oder weniger gut. Bei loop-Funktionen mit anderem Code und delays habe ich - wie "jurs" das beschrieben hat - immer Probleme bekommen.

Der Code von "jurs" funktioniert bei mir in soweit sehr gut, dass ich bei jedem Drehimpuls eine Antwort erhalte, was bei einigen anderen Codes nicht funktionierte, hier wurden Impulse verschluckt und teilweise falsch interpretiert.

Ich habe versucht einen Zähler in den Code zu integrieren (s. Code-Fenster weiter unten) und die vierfache Anzeige des Ausgangs zu korrigieren. Leider funktioniert das bei mir nicht, da ich bei Rotation um eine Raste (rechts wie links) manchmal 2 Werte (1 und 3) und manchmal nur einen Wert (4) zurückerhalte. Der Zähler wird aber fast immer korrekt um 4 erhöht. Hier die beispielhafte Anzeige im Serial Monitor:

1.) Impuls mit zwei Werten:
R: 1
Zaehler: 1
R: 3
Zaehler: 4

2.) Impuls mit einem Wert:
R: 4
Zaehler: 8

Und hier der ergänzte Code (zugegeben keine Meisterleistung):

case 1: impulseRechts++;
            zaehler++;
            break;  
case -1:impulseLinks++;
            zaehler--;
            break;

Um die vierfache Erhöhung des Zählers zu umgehen, habe ich versucht nur bei jedem vierten Durchlauf der Funktion impulsAuswertung() den Zähler um eins zu erhöhen/verringern. Das hat jedoch leider nicht funktioniert, da ich ja unterschiedliche Antworten auf die Drehbewegung erhalte.

Vielleicht kann mir "jurs" oder jemand anderes aus diesem Forum einen Hinweis geben, wie ich die Logik anpassen kann, dass ich den Zähler je Impuls tatsächlich nur einmal berücksichtige.

Viele Grüße
Frank

Frank_:
Ich habe versucht einen Zähler in den Code zu integrieren (s. Code-Fenster weiter unten) und die vierfache Anzeige des Ausgangs zu korrigieren. Leider funktioniert das bei mir nicht, da ich bei Rotation um eine Raste (rechts wie links) manchmal 2 Werte (1 und 3) und manchmal nur einen Wert (4) zurückerhalte.

Vorsicht, bei meinem Code gilt für die Auswertung das, was ich ausdrücklich dazu geschrieben habe:
Der Code zählt beim Drehen des Gebers, und
bei Erkennung einer Drehpause von mindestens 20 Millisekunden wird angezeigt.

Den Code habe ich nämlich mal ursprünglich für jemanden gemacht, der hier im Forum das Problem der "Drehpausenerkennung" an einem sehr hochwertigen Drehgeber an einer Maschinenspindel hatte. Die Spindel seiner Maschine drehte immer links und rechts im Wechsel mit einer halben Sekunde Pause dazwischen beim Drehrichtungswechsel. Und immer in den Drehpausen sollte angezeigt werden, um wie viele Impulse die Spindel bei der letzten Drehbewegung gedreht worden ist.

Dieser Code ist auch brauchbar, wenn man es zur Bedienung auf "langsamen" Ausgabe-Displays benötigt: Also Bewegungen schnell erkennen und vollständig mitzählen, aber Ausgabe von Werten nur in den Drehpausen, damit (möglichst) keine Werte beim Drehen verlorengehen.

Wenn Du lieber "jeden Impuls" angezeigt bekommen möchtest, und auch die "Gesamtsumme" oder die "Gesamtsumme geteilt durch 4", dann muß die Funktion "impulsAuswertung()" komplett anders programmiert werden.

Soll ich Dir dafür mal ein Beispiel machen?

Allerdings wäre es bei hohen Impulsraten dann sinnvoll, eine höhere Serial-Geschwindigkeit als 9600 zu verwenden, sonst kommt eine "Auswertung in der loop-Funktion" nicht mehr mit, jeden Wert einzeln auszugeben, wenn der serielle Ausgabepuffer volläuft und dann beim Senden über Serial immer zu lange gewartet werden muss, um einen Wert zur Anzeige zu bringen (in den Serial-Puffer zu schieben) und in der Zeit die Auswertung blockiert ist.

Die Funktion "impulsAuswertung()" im oben geposteten Sketch funktioniert deshalb auch bei 9600 Baud ordentlich, weil abhängig vom Timing beim Drehen eben nicht jeder Impuls einzeln dargestellt wird. Sondern weil die Ausgabe ausdrücklich unterdrückt wird, so lange die Impulse schneller als mit 20 Millisekunden Zeitabstand reinkommen.

Und so können 4 Impulse ganz verschieden angezeigt werden, je nachdem, wieviel Zeit zwischen den Impulsen liegt.

Wenn Du das anders haben möchtest ==> andere Funktion "impulsAuswertung()" verwenden!

Und wenn die loop-Funktion insgesamt nicht schnell genug läuft und Delays im Ablauf enthalten sind, müssen Drehgeber interruptgesteuert ausgewertet werden. Und zwar dann aber NICHT so wie es die ganzen Arduino-Programmierbeispielse zeigen mit Hardware-Interrupts oder mit PinChange-Interrupts (beides ist voll der Griff ins Klo), sondern mit Timer-Interrupts.

Beschreibe bitte mal genau, was das bei Dir werden soll!
Dann kann ich mein Programmierbeispiel leicht auf eine andere Logik abändern.

Meine Vermutung geht dahin: Du möchtest eine Ausgabe nach jedem einzelnen Impuls, egal wie wenig Zeit zwischen den Impulsen vergangen ist, und es soll nicht ausgegeben werden, um wieviel nach links oder nach rechts gedreht wurde, sondern Du möchtest nicht die Gesamtzahl, sondern die Anzahl der Quadrupelimpulse geteilt durch 4. Dabei soll aber ggf. das Kontaktprellen bei der Ausgabe unterdrückt werden, so dass eine Abfolge von Gesamtzahlen wie 1-2-3-4-5-4-5-4-5-4-5-6-7 unterdrückt werden, so dass nur 1-2-3-4-5-6-7 angezeigt wird. So ungefähr?

Hallo jurs,

vielen Dank für die rasche Antwort. Ich habe gedacht, dass ich eine Nachricht (E-Mail) erhalte, wenn jemand auf meine Frage antwortet, aber das ist wahrscheinlich nur bei Threads, die ich selber erstellt habe. Aus diesem Grund antworte ich erst jetzt.

Ich möchte den Drehgeber für die Werteeingabe in meinem Code verwenden. Die eingestellten Werte sollen tatsächlich auf einem "langsamen" vierzeiligen LCD Display angezeigt werden. Mein Ziel ist es eine Schaltung zu bauen, die es ermöglicht ein Ventil zur Tropfenfotografie zu steuern. Das Ventil soll in einstellbaren Zeitintervallen Tropfen erzeugen, die dann über ein Relais/Auslösekabel (ebenfalls Bestandteil der Schaltung) fotografiert werden.

Der Drehgeber soll also zur "einfachen" Werteeingabe dienen und zwar so, dass die Werte normalerweise in Einerschritten je Impuls verändert werden. Nach Druck auf den Drehgeberknopf, soll dann auch die Veränderung in Zehner-, Hunderter und Tausenderschritten möglich sein. Zusätzlich möchte ich noch die Eingabe von mehreren Werten ermöglichen (das Display hat ja vier Zeilen :D).

Auch in späteren Projekten möchte ich diese Drehgeber zur Eingabe von Werten benutzen. ich habe mir jede Menge davon für kleines Geld beim Chinesen gekauft und musste nun feststellen, dass es doch nicht so einfach ist wie ich zunächst vermutet oder vielmehr gehofft habe. Bis jetzt habe ich für die Werteeingabe immer Potentiometer verwendet, die sind aber sehr ungenau und ich musste die Werte sampeln (auch ein Tipp aus diesem Forum) und habe teilweise nur mit viel Geduld einen festen Wert einstellen können. Dann erhielt ich den Hinweis es mit Drehgebern zu probieren.

Ich fürchte, dass ich wohl bei meinem kleinen Projekt und auch in Zukunft nicht um den Einsatz der von Dir genannten Timer-Interrupts herumkomme. Ich habe schon mal ein wenig recherchiert und fürchte, das wird wohl noch ein steiniger Weg für mich werden. Grundsätzlich habe ich schon den Anspruch an mich, die Dinge selber zu entwickeln. Bei diesen Drehgebern stoße ich jedoch an meine Grenzen.

Meine Vermutung geht dahin: Du möchtest eine Ausgabe nach jedem einzelnen Impuls, egal wie wenig Zeit zwischen den Impulsen vergangen ist, und es soll nicht ausgegeben werden, um wieviel nach links oder nach rechts gedreht wurde, sondern Du möchtest nicht die Gesamtzahl, sondern die Anzahl der Quadrupelimpulse geteilt durch 4. Dabei soll aber ggf. das Kontaktprellen bei der Ausgabe unterdrückt werden, so dass eine Abfolge von Gesamtzahlen wie 1-2-3-4-5-4-5-4-5-4-5-6-7 unterdrückt werden, so dass nur 1-2-3-4-5-6-7 angezeigt wird. So ungefähr?

Ja, genau so stelle ich mir das vor.

Dein Angebot mir ein Bespiel zu erstellen, nehme ich sehr gerne an.

Vielen Dank für Deine Hilfe
Frank

Frank_:
vielen Dank für die rasche Antwort. Ich habe gedacht, dass ich eine Nachricht (E-Mail) erhalte, wenn jemand auf meine Frage antwortet, aber das ist wahrscheinlich nur bei Threads, die ich selber erstellt habe.

Dazu mußt Du wohl beim Schreiben/Ändern eines Beitrags unter "Additional Options" das Feld "Notify me of replies." anhaken.

Frank_:
Ich möchte den Drehgeber für die Werteeingabe in meinem Code verwenden. Die eingestellten Werte sollen tatsächlich auf einem "langsamen" vierzeiligen LCD Display angezeigt werden.

OK. Das mit der Geschwindigkeit von Ausgabegeräten ist immer relativ. Bei Drehgeberauswertung in der loop wäre gleichzeitiger LCD-Betrieb mit Drehgeberauswertung noch machbar. Eine Ausgabe auf LCD ist mit ca. 1 Zeichen/Befehl pro 0,1ms möglich. Das läppert sich dann zwar für Cursorpositionierung und Ausgabe von 20 Zeichen auch auf 2,1ms, aber man könnte es ja so handeln, dass man nicht die ganzen vier Zeilen des Displays auf einmal vollschreibt, ohne den Drehgeber auszuwerten, sondern dass man den Drehgeber immer nach Ausgabe von 10 bis höchstens 20 Zeichen einmal auswertet. Dann funktioniert es mit Drehgebern und LCDs auch ohne Timer.

Frank_:
Mein Ziel ist es eine Schaltung zu bauen, die es ermöglicht ein Ventil zur Tropfenfotografie zu steuern. Das Ventil soll in einstellbaren Zeitintervallen Tropfen erzeugen, die dann über ein Relais/Auslösekabel (ebenfalls Bestandteil der Schaltung) fotografiert werden.

Tropfenfotos mit Arduino müssen wohl gerade große Mode sein. Im letzten Jahr sind schon diverse Threads dazu gelaufen und erst kürzlich habe ich hier im Forum für einen Tropfenfotografen ein hochpräzise laufendes Timing-Modul als Demo-Sketch gepostet.

Frank_:
Auch in späteren Projekten möchte ich diese Drehgeber zur Eingabe von Werten benutzen. ich habe mir jede Menge davon für kleines Geld beim Chinesen gekauft und musste nun feststellen, dass es doch nicht so einfach ist wie ich zunächst vermutet oder vielmehr gehofft habe.

Es wäre vermutlich für den Einstieg einfacher, wenn nicht ALLE Arduino-Beispielcodes zum Auswerten von Drehgebern durch die Bank weg völlig schrottig programmiert wären. Aber so kann ich dann nochmal das eine oder andere dazu posten, was meine Sketche mit Drehgebern schaffen, was andere nicht hinbekommen.

Frank_:
Ich fürchte, dass ich wohl bei meinem kleinen Projekt und auch in Zukunft nicht um den Einsatz der von Dir genannten Timer-Interrupts herumkomme. Ich habe schon mal ein wenig recherchiert und fürchte, das wird wohl noch ein steiniger Weg für mich werden.

Mit den Timer-Interrupts für die Drehgeberauswertung wäre ich mal nicht so voreilig. Eine hohe Interrupt-Last im System hat nämlich nicht nur Vorteile, sondern führt bei einem erwünschten exakten Timing zu einem Jitter um etliche Mikrosekunden. Da es bei Deiner Anwendung auf ein möglichst exaktes und reproduzierbares Timing ankommt, würde ich bei der Drehgeberauswertung eher auf sämtliche Interrupts verzichten und stattdessen in der loop auswerten.

Die Drehgeber mit Einstellung der Werte müssen ja nicht gleichzeitig mit dem Timing-Ablauf ausgewertet werden.

Frank_:
Ja, genau so stelle ich mir das vor.

Dein Angebot mir ein Bespiel zu erstellen, nehme ich sehr gerne an.

OK, ich denk mal drüber nach und poste dann eine geänderte und auf Deinen Anwendungsfall angepaßte Auswerteroutine.

jurs:
OK, ich denk mal drüber nach und poste dann eine geänderte und auf Deinen Anwendungsfall angepaßte Auswerteroutine.

So, drüber nachgedacht und eine angepaßte Auswertung für die Zählung von Einzelimpulsen codiert :

// Drehgeber-Auswertung Demo by 'jurs' for German Arduino forum
#define ENCODER_A 2
#define ENCODER_B 3

#define ERRORCODE 9999
#define TIMEOUT 20

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(ENCODER_A, INPUT_PULLUP);
  pinMode(ENCODER_B, INPUT_PULLUP);
  encoderImpuls(); // Initialisiert den Anfangszustand des Drehgebers
}

int encoderImpuls()
{
  static byte state=0;
  state= state<<2;
  if (digitalRead(ENCODER_A)) bitSet(state,1);
  if (digitalRead(ENCODER_B)) bitSet(state,0);
  state= state & 0xF; 
  switch(state)
  {
    case 0b0000:
    case 0b0101:
    case 0b1010:
    case 0b1111: return 0; // keine Änderung an den Eingängen
    case 0b0001:
    case 0b0111:
    case 0b1110:
    case 0b1000: return -1; // links Impuls an den Eingängen
    case 0b0010:
    case 0b1011:
    case 0b1101:
    case 0b0100: return 1; // rechts Impuls an den Eingängen
    default: return ERRORCODE;  // Error (z.B. wegen verpaßter Impulse)
  }  
}


long impulseTotal=0;
unsigned long errorImpulse;

long impulsAuswertung()
{
  int thisImpulse=encoderImpuls();
  if (thisImpulse==ERRORCODE) errorImpulse++;
  else impulseTotal+=thisImpulse;
  return impulseTotal/4;
}

void updateImpulsAnzeige()
{
  static unsigned long lastDisplayTime=0;
  static long impulseDisplayed;
  long impulseAusgewertet=impulsAuswertung();
  if(impulseAusgewertet!=impulseDisplayed && micros()-lastDisplayTime>2000)
  {
    impulseDisplayed=impulseAusgewertet;
    lastDisplayTime=micros();
    Serial.println(impulseAusgewertet);
  }
  if (errorImpulse>0)  // Optionale Anzeige von Auswertefehlern
  {
    Serial.println('*');
    errorImpulse=0;
  }
}


void loop() {
  updateImpulsAnzeige();
}

Das wesentliche an diesem Code ist, dass die Funktionen "impulsAuswertung()" und "updateImpulsAnzeige()" getrennt sind.

Da die Auswertung in der loop erfolgt, müßtest Du darauf achten, dass Dein Code schnell genug läuft, wenn der Drehgeber ausgewertet werden soll. Insbesondere wenn Du ein 4-zeiliges LCD komplett neu beschreiben möchtest, z.B. 4 Zeilen a 20 Zeichen, während der Drehgeber ausgewertet wird, müßtest Du den Schreibvorgang so aufteilen, dass höchstens 2 Millisekunden zwischen zwei Aufrufen von "impulsAuswertung()" vergehen. Also z.B. folgende Pseudologik:

  lcd.setCursor(0,0);
  ErsteLcdZeileSchreiben();
  impulsAuswertung()
  lcd.setCursor(0,1);
  ZweitecdZeileSchreiben();
  impulsAuswertung()
  lcd.setCursor(0,2);
  DritteLcdZeileSchreiben();
  impulsAuswertung()
  lcd.setCursor(0,3);
  VierteLcdZeileSchreiben();
  impulsAuswertung()

Eine Anzeige von Kontaktprellen wird dadurch vermieden, dass mit der Bedingung "micros()-lastDisplayTime>2000" die Anzeige des aktuelle Wertes unterdrückt wird, wenn innerhalb von 2000µs (2ms) bereits ein anderer Wert in der Anzeige aktualisiert worden ist.

Nun könnte man sich noch etwas überlegen, wie man ohne weitere Hardware, nur mit einer intelligenten Auswertelogik, auch große Werteänderungen komfortabel einstellbar machen könnte. Also z.B. wie man einen Einstellwert um 4711 erhöhen könnte, mit weniger 4711 Impulsen am Encoder.

Eine Überlegung dazu wäre, als Zusatzinformation auszuwerten, wie oft der Drehgeber in dieselbe Richtung gedreht wird, und wenn der Drehgeber immer weiter in dieselbe Richtung gedreht wird, irgendwann die Schrittweite beim Zählen zu erhöhen. Beispiel:

  • 1 bis 11 mal nach rechts drehen ==> Zähler zählt einzeln immer 1 höher
  • 12 mal nach rechts drehen ==> Zähler springt hoch auf den nächsten vollen Zehner
  • 13 mal und mehr nach rechts drehen ==> Zähler zählt ab sofort in 10er Schritten
  • 22 mal nach rechts drehen ==> Zähler springt hoch auf den nächsten vollen Hunderter
  • 23 mal und mehr nach rechts drehen ==> Zähler zählt ab sofort in 100er Schritten
  • 32 mal nach rechts drehen ==> Zähler springt hoch auf den nächsten vollen Tausender
  • 33 mal und mehr nach rechts drehen ==> Zähler zählt ab sofort in 1000er Schritten
    Nach links entsprechend. Bei jedem Drehrichtungswechsel beginnt die Logik von vorne.

Das würde bedeuten, dass viele Werte nicht alleine durch Drehen in eine Richtung eingestellt werden können, sondern dass man links und rechts drehen muss. Dafür lassen sich mit sehr wenigen Drehimpulsen auch sehr große Werte einstellen.

Beispiel, um von 0 auf den Wert 4711 einzustellen:
Wie oben ausgeführt, würde die 32. Rechtsdrehung den Wert auf 1000 springen lassen, beim 33. Rechtsimpuls springt der Zähler auf 2000, beim 34. auf 3000, beim 35. auf 4000, beim 36. auf 5000. Ab jetzt dreht man nach links.

Nach 22 maliger Linksdrehung wäre man auf 4900 herunter, nach 23 maliger auf 4800, nach 24 maliger auf 4700.

Jetzt noch 11 Impulse nach rechts drehen ==> 4711.

D.h. mit nur 36+24+11 = 71 Zählimpulsen ließe sich der Zähler von 0 auf 4711 verstellen.

Wenn man sich mit so einer rechts-links-rechts Einstellerei nach diesem Schema anfreunden kann, ließen sich so sehr hohe Werte mit sehr wenig Kurbelei am Drehgeber einstellen.

Bei Einzelzählung wären 4711 Impulse an einem Drehgeber mit 18 Impulsen pro Umdrehung ja 262 Umdrehungen, und mit der oben skizzierten Logik nur 4 Umdrehungen. Man kann sich erhebliche Drehbewegungen beim Einstellen sparen.

Vielleicht sollte man sich das überlegen, es so auszuwerten, und dafür eine Auswertelogik schreiben.

Ich habe den Code ausprobiert und muss sagen: GENIAL !!!

Das ist der erste Code, der mit meinen Billigdrehgebern ohne prellen, überspringen, falsch zählen etc. korrekt funktioniert. Bis dahin schon mal absolute spitze.

Ich werde das Ganze mal in meinen Code integrieren und dann versuchen zu verstehen, was Du da eigentlich genau programmiert hast. Bis dahin schon mal vielen Dank für die tolle Hilfe.

Ich melde mich wieder. Das ist eine Drohung :wink:

Hallo jurs,

ich habe den Code (Loopfunktion) um eine Funktion erweitert. Ein Druck auf den Taster des Drehgebers erhöht den Wert eines Zählers, um im weiteren Verlauf des Programms entweder die Eingabe von mehreren Werten (in diesem Fall 3) zu ermöglichen oder die Erhöhung der Schrittweite für die Werte zu realisieren. In der Funktion updateImpulsAnzeige() gebe ich den Wert aus (serialPrintln).

#define ENCODER_C 4
byte knopf=1;

void loop() {
  //updateImpulsAnzeige();
  int button=digitalRead(ENCODER_C);
  if (button==HIGH) {knopf++;}
  if (knopf>3) knopf=1;
  for (int i = 0; i < 150; i++) {
    delay(1);
    updateImpulsAnzeige();
  }
}

Die for-Schleife ist ein delay für eine realistische Auswertung des Knopfdrucks, bei dem der Code noch schnell genug läuft, damit der Drehregler ausreichend oft ausgelesen wird. Was denkst Du darüber? Das geht bestimmt noch eleganter, oder?

Da die Auswertung in der loop erfolgt, müßtest Du darauf achten, dass Dein Code schnell genug läuft, wenn der Drehgeber ausgewertet werden soll. Insbesondere wenn Du ein 4-zeiliges LCD komplett neu beschreiben möchtest, z.B. 4 Zeilen a 20 Zeichen, während der Drehgeber ausgewertet wird, müßtest Du den Schreibvorgang so aufteilen, dass höchstens 2 Millisekunden zwischen zwei Aufrufen von "impulsAuswertung()" vergehen.

Ich bin gespannt was passiert, wenn das LCD Display noch dazu kommt.

Deinen Vorschlag zur komfortablen Werteänderung mittels Zählen der bereits erfolgten Umdrehungen klingt ebenfalls sehr interessant. Vielleicht kann man den ja mit der Auswertung des Tastendrucks kombinieren, um sowohl mehrere Werte eingeben zu können als auch eine komfortable Werteeingabe zu erhalten.

Wenn es Dir nicht zu viel Arbeit macht, würde ich mich über eine detailliertere Erklärung Deines Sketches sehr freuen. Ich kann den Code zwar einsetzen, verstehe ihn aber noch nicht komplett. Speziell die Auswertung über die Variable state mit Binärarithmetik interessiert mich.

Viele Grüße
Frank

Frank_:
Wenn es Dir nicht zu viel Arbeit macht, würde ich mich über eine detailliertere Erklärung Deines Sketches sehr freuen.

Das macht keine große Arbeit, denn ich habe dazu hier schon etwas gepostet und verlinke jetzt mal nur:
http://forum.arduino.cc/index.php?topic=189578.msg1404643#msg1404643

Für den Schalter habe ich auch nochmal etwas programmiert, und zwar wie folgt:

Man kann mit einem einzigen Drehgeber verschiedene Werte einstellen, umschaltbar per Taster, und zwar:

  • Werte für Umdrehungen pro Minute
  • Voltwerte
  • Milliampere-Werte

Ebenfalls mit demselben Taster einstellbar gibt es eine "Feineinstellung" und eine "Grobeinstellung". Vorgabe ist die Feineinstellung.

Zum Umschalten zwischen den verschiedenen Einstellwerten dient ein KURZER Tastendruck (<1 sec).
Zum Umschalten zwischen Feineinstellung und Grobeinstellung (Faktor 100) dient ein LANGER Tastendruck (>1 sec)

// Drehgeber-Auswertung Demo by 'jurs' for German Arduino forum
#define ENCODER_A 2
#define ENCODER_B 3
#define ENCODER_C 4

#define ERRORCODE 9999

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(ENCODER_A, INPUT_PULLUP);
  pinMode(ENCODER_B, INPUT_PULLUP);
  pinMode(ENCODER_C, INPUT_PULLUP);
  encoderImpuls(); // Initialisiert den Anfangszustand des Drehgebers
}

enum {NOTPRESSED,DOWNPRESSED,SHORTPRESSED,LONGPRESSED};
int buttonPressed()
{
  static unsigned long lastRuntime, lastDowntime;
  static byte lastState=false;
  byte pressed=NOTPRESSED;
  unsigned long now=millis();
  if (now-lastRuntime<5) return pressed; // NOTPRESSED
  byte thisState=!digitalRead(ENCODER_C);
  if (lastState==false && thisState==true) 
  {
    pressed=DOWNPRESSED;
    lastDowntime=now;
  }
  else if (lastState==true && thisState==false)
  {
    if (now-lastDowntime>1000) pressed=LONGPRESSED;
    else pressed=SHORTPRESSED;
  }
  lastRuntime=now;
  lastState=thisState;
  return pressed;
}

int encoderImpuls()
{
  static byte state=0;
  state= state<<2;
  if (digitalRead(ENCODER_A)) bitSet(state,1);
  if (digitalRead(ENCODER_B)) bitSet(state,0);
  state= state & 0xF; 
  switch(state)
  {
    case 0b0000:
    case 0b0101:
    case 0b1010:
    case 0b1111: return 0; // keine Änderung an den Eingängen
    case 0b0001:
    case 0b0111:
    case 0b1110:
    case 0b1000: return -1; // links Impuls an den Eingängen
    case 0b0010:
    case 0b1011:
    case 0b1101:
    case 0b0100: return 1; // rechts Impuls an den Eingängen
    default: return ERRORCODE;  // Error (z.B. wegen verpaßter Impulse)
  }  
}


long impulseTotal=0;
unsigned long errorImpulse;

long impulsAuswertung()
{
  int thisImpulse=encoderImpuls();
  if (thisImpulse==ERRORCODE) errorImpulse++;
  else impulseTotal+=thisImpulse;
  return impulseTotal/4;
}

/*
void updateImpulsAnzeige()
{
  static unsigned long lastDisplayTime=0;
  static long impulseDisplayed;
  long impulseAusgewertet=impulsAuswertung();
  if(impulseAusgewertet!=impulseDisplayed && micros()-lastDisplayTime>2000)
  {
    impulseDisplayed=impulseAusgewertet;
    lastDisplayTime=micros();
    Serial.println(impulseAusgewertet);
  }
  if (errorImpulse>0)  // Optionale Anzeige von Auswertefehlern
  {
    Serial.println('*');
    errorImpulse=0;
  }
}
*/

long einstellWerte[]={1000,2500,5000};
byte numEinstellWerte=sizeof(einstellWerte)/sizeof(einstellWerte[0]);
byte werteIndex=0;
boolean schnellGang=false;

void runRotaryEncoderSwitch() {
  static long alteImpulsAuswertung;
  boolean displayNeedsUpdate=false;
  impulsAuswertung(); // Imulsauswertung immer regelmäßig laufen lassen
  switch (buttonPressed())
  {
    case SHORTPRESSED: 
      werteIndex=(werteIndex+1)%numEinstellWerte;
      displayNeedsUpdate=true;
      break;
    case LONGPRESSED:  schnellGang=!schnellGang;break;
  }
  long neueImpulsAuswertung=impulsAuswertung();
  long diff=neueImpulsAuswertung-alteImpulsAuswertung;
  if (diff!=0) displayNeedsUpdate=true;
  if (displayNeedsUpdate)
  {
    if (schnellGang)
    {
      diff=100*diff;
      einstellWerte[werteIndex]= ((einstellWerte[werteIndex]+diff)/100)*100;
    }
    else
      einstellWerte[werteIndex]+=diff;
    switch(werteIndex)
    {
      case 0: Serial.print(einstellWerte[werteIndex]);
              Serial.println(" U/min");
              break;
      case 1: Serial.print(einstellWerte[werteIndex]/1000.0,3);
              Serial.println(" Volt");
              break;
      case 2: Serial.print(einstellWerte[werteIndex]/10.0,1);
              Serial.println(" mA");
              break; 
    }
  }
  alteImpulsAuswertung=neueImpulsAuswertung;
  if (errorImpulse>0)  // Optionale Anzeige von Auswertefehlern
  {
    Serial.println('*');
    errorImpulse=0;
  }
}

void loop() {
  runRotaryEncoderSwitch();
//  delay(1);
}

Wenn man in der loop das delay von nur einer Millisekunde aktiviert, kann schon nicht mehr superschnell drehen, ohne dass es zu gelegentlichen Error-Auswertungen kommt. 1ms entspricht bei LCD-Ausgabe etwa 10 Zeichen. Wenn es Dir zu hakelig wird, müßte man eventuell doch auf die Auswertung der Drehgeberimpulse per Timerinterrupt wechseln.

Hallo jurs,

Das macht keine große Arbeit, denn ich habe dazu hier schon etwas gepostet und verlinke jetzt mal nur:
Anfänger will Drehgeber auslesen - #11 by jurs - Deutsch - Arduino Forum

Danke für den Link.

Man kann mit einem einzigen Drehgeber verschiedene Werte einstellen, umschaltbar per Taster, und zwar:

  • Werte für Umdrehungen pro Minute
  • Voltwerte
  • Milliampere-Werte

Ebenfalls mit demselben Taster einstellbar gibt es eine "Feineinstellung" und eine "Grobeinstellung". Vorgabe ist die Feineinstellung.

Zum Umschalten zwischen den verschiedenen Einstellwerten dient ein KURZER Tastendruck (<1 sec).
Zum Umschalten zwischen Feineinstellung und Grobeinstellung (Faktor 100) dient ein LANGER Tastendruck (>1 sec)

Das funktioniert bei mir irgendwie nicht ganz so gut. Ich kann zwar zwischen Fein- und Grobeinstellung wechseln, aber nur mit einem kurzen Tastendruck. Das Umschalten zwischen den verschiedenen Einstellwerten klappt nicht, auch nicht wenn ich die Taste deutlich länger als eine Sekunde drücke. Ich habe den Code allerdings an meinem Arduino-Uno mit angeschlossenem 20x4 LCD-Display und anderen Sachen laufen lassen. Zusätzlich habe ich für den Schalter PIN 8 (#define ENCODER_C 8 ) genutzt, da mein LCD Display schon den PIN 4 nutzt. Grundsätzlich finde ich die Idee mit der Doppelnutzung des Tasters eine feine Sache.

Ich habe auch ein wenig programmiert und mein 20x4 LCD Display eingebunden. Leider ist die Werteingabe über den Drehgeber sehr zäh geworden. Ich bilde mir ein, dass es mal schneller und mal langsamer geht, insgesamt jedoch träge.

Ich habe mal den Codeausschnitt vom Schreiben des LCD Displays angehangen. Das Ganze läuft in der loop. Ich habe zuerst immer alle 4 Zeilen des Displays geschrieben und dann die unnötigen Aktualisierungen des Displays herauskommentiert und gehofft, dass es dann etwas flüssiger läuft. Leider ohne größeren Erfolg. Vor der aktuellen LCD-Zeile, die gerade mit dem Drehgeber verändert werden kann, befindet sich ein Markierungsstern *. Mit dem Taster des Drehgebers kann man zwischen den Zeilen umschalten (1,2,3,4,1,2,3..). Ich habe auch - wie von Dir empfohlen - die Funktion updateImpulsAnzeige() regelmäßig aufgerufen, aber das Problem ist wohl das langsame Schreiben auf das LCD-Display, oder?

switch (knopf) {
    case 1: lcd.setCursor(0, 0);
            lcd.print("*Wert 1: ");
            lcd.print(wert1);
            lcd.print("ms     ");
            updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            /*   
            lcd.setCursor(0, 1);
            lcd.print(" Wert 2: ");
            //updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            lcd.setCursor(0, 2);
            lcd.print(" Wert 3: ");
            //updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            */
            lcd.setCursor(0, 3);
            lcd.print(" Wert 4: ");
            updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            break;
    //Schreiben der 2. Zeile mit veränderbaren Werten. Hierbei wird der Markierungsstern * immer in die korrekte Zeile gesetzt.
    case 2: lcd.setCursor(0, 0);
            lcd.print(" Wert 1: ");
            updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            lcd.setCursor(0, 1);
            lcd.print("*Wert 2: ");
            lcd.print(wert2);
            lcd.print("ms     ");
            updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            /*
            lcd.setCursor(0, 2);
            lcd.print(" Wert 3: ");
            //updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            lcd.setCursor(0, 3);
            lcd.print(" Wert 4: ");
            */
            //updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            break;
    //Schreiben der 3. Zeile mit veränderbaren Werten. Hierbei wird der Markierungsstern * immer in die korrekte Zeile gesetzt.
    case 3: /*
            lcd.setCursor(0, 0);
            lcd.print(" Wert 1: ");
            //updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            */
            lcd.setCursor(0, 1);
            lcd.print(" Wert 2: ");
            updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            lcd.setCursor(0, 2);
            lcd.print("*Wert 3: ");
            lcd.print(wert3);
            lcd.print("ms     ");
            updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            /*
            lcd.setCursor(0, 3);
            lcd.print(" Wert 4: ");
            updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            */
            break;
   //Schreiben der 4. Zeile mit veränderbaren Werten. Hierbei wird der Markierungsstern * immer in die korrekte Zeile gesetzt.
    case 4: /*
            lcd.setCursor(0, 0);
            lcd.print(" Wert 1: ");
            //updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            lcd.setCursor(0, 1);
            lcd.print(" Wert 2: ");
            updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            */
            lcd.setCursor(0, 2);
            lcd.print(" Wert 3: ");
            updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            lcd.setCursor(0, 3);
            lcd.print("*Wert 4: ");
            lcd.print(wert4);
            lcd.print("ms     ");
            updateImpulsAnzeige();          //Regelmäßiges Update der Impulsmessung des Drehgebers.
            break;
     //default:  
    }

Wenn es Dir zu hakelig wird, müßte man eventuell doch auf die Auswertung der Drehgeberimpulse per Timerinterrupt wechseln.

Das denke bzw. fürchte ich auch.

Ich wäre Dir sehr dankbar für einen Hinweis, wie die Auswertung der Drehgeberimpulse per Timerinterrupt funktionieren könnte. Idealerweise für den Code aus Reply #10, den habe ich nämlich für meine Bedürfnisse ergänzt.

Wenn es weiterhilft, kann ich mal meine gesamten Spaghetti-Code-Ergänzungen komplett posten. Ich weiß allerdings nicht ob das Posten längerer Codes hier so gerne gesehen wird.

Vielen Dank nochmals für Deine Mühe. Ich hoffe ich gehe Dir nicht zu sehr auf die Nerven damit. Ich bin jedenfalls immer wieder angetan vom Umgang der Leute hier im Forum. Ich kenne Plattformen, bei denen schon das geringste outen als Nichtexperte für Hohn und Spott sorgen.

Viele Grüße
Frank

Frank_:
Das funktioniert bei mir irgendwie nicht ganz so gut. Ich kann zwar zwischen Fein- und Grobeinstellung wechseln, aber nur mit einem kurzen Tastendruck. Das Umschalten zwischen den verschiedenen Einstellwerten klappt nicht, auch nicht wenn ich die Taste deutlich länger als eine Sekunde drücke. Ich habe den Code allerdings an meinem Arduino-Uno mit angeschlossenem 20x4 LCD-Display und anderen Sachen laufen lassen.

switch (knopf)

Was ist bei Dir "knopf"? Eine vorher ausgelesene Variable? Oder ein Funktionsname?

Wenn Du einfach meine Funktion "buttonPressed()" genommen und nach "knopf()" umbenannt hast, sollte an der Stelle ein Funktionsaufruf stehen und Du hättest in dem Fall die Klammern beim Funktionsaufruf in Deinem Code vergessen:

switch (knopf())

Falls "knopf" keine Variable, sondern eine Funktion ist, wertest Du mit Deinem Code nicht den Rückgabewert der Funktion aus, sondern Du wertest die Adresse der Funktion im Flash-Speicher aus, was natürlich keinen Sinn ergeben würde.

Frank_:
Ich wäre Dir sehr dankbar für einen Hinweis, wie die Auswertung der Drehgeberimpulse per Timerinterrupt funktionieren könnte.

Informiere Dich über Timer-Interruptprogrammierung mit Atmega-Controllern!

Das ist kein ganz spezielles Arduino-Thema, denn die Arduino-Software bietet für das Aufsetzen von Timer-Interrupts keine eingebauten Komfortfunktionen. Es gibt aber fertige Timer-Libraries mit Hilfsfunktionen für Arduino-Programmierer, die einem das Aufsetzen von timergesteuerten Funktionen erleichtern können. Für die Drehgeberauswertung bietet sich ein Timer-Interrupt mit einer Frequenz von 500 bis 1000 pro Sekunde an.

Und wenn Du eine regelmäßig ablaufende Timer-Interruptfunktion hast, wertest Du den Drehgeber dann in der Interrupt-Behandlungsroutine aus, unter Beachtung aller üblichen programmtechnischen Maßnahmen, wie z.B. dass Variablen, auf die sowohl von der Interruptbehandlung als auch vom normalen Programm aus zugegriffen wird, als "volatile" deklariert sein müssen.

Hallo,

eine fertige Routine zur Abfrage im Interrupt (1000 mal pro Sekunde) gibt es hier: elektronik-bastelkeller.de

Gruß,
Ralf

Was ist bei Dir "knopf"? Eine vorher ausgelesene Variable? Oder ein Funktionsname?

knopf ist eine Variable, die bei jedem Tastendruck des Drehgebers um einen hochzählt (1,2,3,4,1,2,3..), damit kann ich die Zeile bzw. den Wert (wert1, wert2, etc.) festlegen, bei dem der Drehgeber aktuell gerade den Wert ändert.

Das funktioniert bei mir irgendwie nicht ganz so gut. Ich kann zwar zwischen Fein- und Grobeinstellung wechseln, aber nur mit einem kurzen Tastendruck. Das Umschalten zwischen den verschiedenen Einstellwerten klappt nicht, auch nicht wenn ich die Taste deutlich länger als eine Sekunde drücke. Ich habe den Code allerdings an meinem Arduino-Uno mit angeschlossenem 20x4 LCD-Display und anderen Sachen laufen lassen. Zusätzlich habe ich für den Schalter PIN 8 (#define ENCODER_C 8 ) genutzt, da mein LCD Display schon den PIN 4 nutzt.

Ich habe Deinen Code aus Reply #13 ohne Änderungen und Anpassungen meinerseits getestet. Dir Geschichte mit der Variable knopf war zu diesem Zeitpunkt nicht integriert..

Und wenn Du eine regelmäßig ablaufende Timer-Interruptfunktion hast, wertest Du den Drehgeber dann in der Interrupt-Behandlungsroutine aus, ...

Ich denke, dass ich schon was zum Thema Timerinterrupts gefunden habe: Arduino Playground - HomePage

Reicht es aus die Funktion updateImpulsAnzeige() in der Interruptroutine aufzurufen oder muss der gesamte Auswertungscode in die Interruptroutine "verschoben" werden?

@Schachmann
Danke für den Hinweis. Das schaue ich mir mal genauer an, scheint ohne Library zu funktionieren.

Frank_:
Ich habe Deinen Code aus Reply #13 ohne Änderungen und Anpassungen meinerseits getestet. Dir Geschichte mit der Variable knopf war zu diesem Zeitpunkt nicht integriert..

Dann prüfe mal, ob Du den Tastschalter richtig angeschlossen hast. Der Tastschalter ist bei Drehgebern mit integriertem Tastschalter üblicherweise vom Drehgeber galvanisch getrennt, d.h. der Mittelanschluss des Drehgebers (GND) ist NICHT mit einem der beiden Tasteranschlüsse verbunden. Sondern der elektrische Aufbau ist so als hättest Du einen getrennten Tastschalter.

Stattdessen muß einer der Tasteranschlüsse an GND verbunden werden, genau so wie der Mittelanschluss des Drehgebers, und der andere mit dem Signaleingang des Arduino. Mein Code beim Taster ist für die Verwendung der internen PullUps (INPUT_PULLUP) vorgesehen, genau wie bei den Drehgeberanschlüssen A und B, so dass kein externer PullDown-Widerstand am Taster verwendet werden darf. Oder der Code muss etwas geändert werden.

Frank_:
Reicht es aus die Funktion updateImpulsAnzeige() in der Interruptroutine aufzurufen oder muss der gesamte Auswertungscode in die Interruptroutine "verschoben" werden?

Die Funktion updateImpulsAnzeige() darf NICHT in einer Interruptbehandlung aufgerufen werden, da diese Funktion "Serial.print" verwendet und daher nicht interrupt-sicher ist.

Schaue Dir Schachmanns Code an, wie Interrupts gehandelt werden! Keine Serial-Ausgaben von der Interrupt-Routine aus, sondern nur vom normalen Programmablauf auf Serial ausgeben!

Schachmann:
eine fertige Routine zur Abfrage im Interrupt (1000 mal pro Sekunde) gibt es hier: elektronik-bastelkeller.de

Wenn du das Ding jetzt noch im CTC Modus betreibst und statt dem Overflow den Compare Match Interrupt verwendest musst du das Counter Register nicht mehr anfassen, da es selbstständig zurückgesetzt wird :slight_smile:

Aber wichtiger:

TCCR1A = (0<<COM1A1) | (0<<COM1A0) | (0<<WGM11) | (0<<WGM10);

Ist Unsinn

Das (1 << reg) ist dazu da das entsprechende Bit zu setzen. z.B. 0100 für Bit 2. Mit einem Oder setzt man das Bit dann in der Maske.

Ein Bit löschen geht so wie du es hier gemacht hast:

TIMSK1 &= ~(1<<TOIE0);

Und mit dem Inversen

Wenn du sicher gehen willst, dass ein Register auf 0 ist mache einfach das:

TCCR1A = 0;

Obwohl das theoretisch schon nach dem Reset auf 0 stehen sollte.

Und hier:

TCCR1B = (0<<CS12) | (1<<CS11) | (1<<CS10);

Kannst du sicher sein dass CS12 auf 0 ist, ohne das du es extra hinschreibst. Da durch nur CS11 und CS10 auf 1 setzt.

Ob man Overflow oder CTC machst ist Geschmackssache, aber das mit den Bits sollte in einem Tutorial eher nicht sein, auch wenn es funktioniert :slight_smile:
Vielleicht willst du nur deutlich machen, dass ein Bit, das mit den gesetzten Bits zusammenwirkt nicht gesetzt ist. Das ist verständlich, aber man siehst das so nirgendwo anders.