[gelöst] Encoder Impulse, über langsame Schnittstelle ohne einen zu verlieren

Hi,
ich bin gerade mal wieder an meinem Cuadcopter am tüfteln. Um die PIDS einzustellen, gibt es da verschiedene Möglichkeiten, am bequemsten ist natürlich vom Sender aus. Das geht über einen Dreistufenschalter, nach vorne erhöht er den Wert, nach hinten vermindert er den Wert. Besser wäre ein 3-Stufen Taster.
Am besten wäre ein Rotary Encoder :grin: :smiley:

Was ich bisher habe und auch läuft:
3-Wege-Schalter durch 2x Optokoppler ersetzt.
Rotary Encoder, dekodiert (per %4 habe ich pro Klick einenImpuls) und gibt bei Linksdrehung einen Impuls von 60ms und 60ms Pause auf Pin11, bei rechts das gleiche auf Pin12. Pin13 blitzt paralell auf
Auf Pin 11 und 12 sind die OK, welche mir den 3-Wegetaster simulieren.

Das ganze funktioniert, wenn man langsam dreht, weil ich ja auf das übertragen warten muss mit delay :frowning: ), sprich pro Klick 120ms, schneller geht es hardwaremässig nicht.

Was ich nun bräuchte:

Denkanstoss/ansatz dafür:
Ich will die Rotary Klicks puffern, und sie dann übertragen. Die Übertragung sollte idealerweise nichtblockierend mit dem ersten Klick schon beginnen. Falls ich während der Übertragung in die andere Richtung drehe, sollte die Übertragung sofort abgebrochen werden und die neue anfangen.

Irgendwie mit hochzählen und dann beim übertragen zurückzählen oder so. Ich denk bestimmt schon 5 Minuten :wink: drüber nach, aber der entscheidende Klick fehlt.

Hier der Code, den ich habe:

//#define ENCODER_DO_NOT_USE_INTERRUPTS
#include <Encoder.h>

Encoder Encoder1(2, 3);

int warten = 60; // impuls/pause beim übertragen

void setup() {
  Serial.begin(115200);
  Serial.println("Basic NoInterrupts Test:");
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(11, OUTPUT);
}

long position  = -999;

void loop() {

  long neuePosition = Encoder1.read();
  if (neuePosition != position) {

    if (position % 4 == 0 && position > neuePosition) {
      digitalWrite(11, HIGH);
      digitalWrite(13, HIGH);
      delay(warten);
      digitalWrite(11, LOW);
      digitalWrite(13, LOW);
      delay(warten);
    }

    if (position % 4 == 0 && position < neuePosition) {
      digitalWrite(12, HIGH);
      digitalWrite(13, HIGH);
      delay(warten);
      digitalWrite(12, LOW);
      digitalWrite(13, LOW);
      delay(warten);
    }

    position = neuePosition;

    Serial.println(position / 4);
  }


}

Hallo,

ich verstehe ehrlich gesagt nicht warum du warten mußt. Worauf denn?
Während der delay Pause wird nichts übertragen.
Wofür ist das delay im Code?
Nur das du es blinken siehst oder hat das weitere unbekannte Bedeutungen?

Und mit dem %4 haste einen kleinen Denkfehler drin. Du bekommst aller 4 Klicks eine Änderung +/- in deinem Code "berechnet". Der Encoder liefert weiterhin pro Klick einen Impuls an deine Encoder Lib. Das nur als Nebeninfo. Diese Klicks bekommste nur alle mit solange deine loop schnell genug ist. Oder man "pollt" per Interrupt auf die Encoderpulse. Komme aber bitte nicht auf die Idee mit Interrupt und delay zu arbeiten.

Egal was du machst, mit delay kannste erfolglos puffern wie du möchtest, sobald delay zuschlägt ist der Code für den Encoder blind. Du müßtest die 60ms Blinkfunktion oder was das ist auslagern und mit millis realisieren.

Das delay habe ich im Moment drin, weil ich ja Impulse generieren muss, und die müssen nun mal 120 ms sein (60ms high und danach mindestens 60 ms low). Pro high Pegel/Flanke ändert sich der Wert am Zielsystem um eins.

Und in dieser Wartezeit wird eben der Encoder nicht mehr abgefragt.

Inwieweit habe ich mit %4 einen Denkfehler? Wenn ich den Encoder ganz langsam drehe, bekomme ich 4 Änderungen zwischen den Klicks (oder soll ich besser sagen zwischen den Rastungen?)

Deswegen %4, damit ich pro Rastung einen Impuls habe und nicht 4.

Wenn ich nun in 0.3 Sekunden am Encoder 20 Impulse drehen will, bekomme ich nur 2 übertragen. Somit müssten noch 18 in den nächsten ca. 2 Sekunden übertragen werden. und dazu hab ich grad keine Idee, wie das umsetzen

Hallo,
probiere die einmal, die sollte keinen Impuls verlieren.
Encoder
Gruß und Spaß
Andreas

Hallo,

du könntest eine/zwei Blinkfunktion(en) mittels millis schreiben die du dann jeweils aufrufst.
Dann kannste am Encoder drehen wie du möchtest und verpaßt nichts mehr.
Puffern in dem Sinne geht sowieso nicht.

Habs jetzt hingekriegt. Muss die Klicks doch puffern, d.h. eine Variable hochzählen. Unabhängig davon zähle ich die Variable runter und gebe jedesmal einen Impuls aus. Das dauert dann halt solange es dauert.

Falls es jemanden interessiert:
Stichworte: naze32, Flip32, PID Werte, Rotary encoder anstatt 3-Wegeschalter

#define ENCODER_DO_NOT_USE_INTERRUPTS
#include <Encoder.h>
#include <Streaming.h>

#include <INTERVAL.h>
Encoder Encoder1(2, 3);

int warten = 80; // impuls/pause beim übertragen
uint16_t links, rechts;

void setup() {
  Serial.begin(115200);
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(11, OUTPUT);
}

long position  = -999;

void loop() {

  if (links) {
    INTERVAL(warten) {
      if (digitalRead(11)) {
        digitalWrite(11, LOW);
        links--;
        Serial << position / 4 << "\t" << links << "\t"  << rechts << endl;;
      }
      else digitalWrite(11, HIGH);
      if (digitalRead(11) && digitalRead(12)) {
        digitalWrite(13, HIGH);
        delay(300);
      }
    }
  }
  else digitalWrite(11, LOW);


  if (rechts) {
    INTERVAL(warten) {
      if (digitalRead(12)) {
        digitalWrite(12, LOW);
        rechts--;
        Serial << position / 4 << "\t" << links << "\t"  << rechts << endl;;
      }
      else digitalWrite(12, HIGH);
      if (digitalRead(11) && digitalRead(12)) {
        digitalWrite(13, HIGH);
        delay(300);
      }
    }
  }
  else digitalWrite(12, LOW);


  long neuePosition = Encoder1.read();
  if (neuePosition != position) {

    if (position % 4 == 0 && position > neuePosition) {
      rechts = 0;
      links++;
      digitalWrite(12, LOW);
    }

    if (position % 4 == 0 && position < neuePosition) {
      rechts++;
      digitalWrite(11, LOW);
      links = 0;
    }

    position = neuePosition;

    Serial << position / 4 << "\t" << links << "\t"  << rechts << endl;;
  }

}

Hab jetzt aber ein anderes Problem mit dem Encoder, mache dafür aber einen neuen Thread auf.

Sorry, dass ich da noch etwas kommentiere.

ElEspanol:
Das delay habe ich im Moment drin, weil ich ja Impulse generieren muss, und die müssen nun mal 120 ms sein (60ms high und danach mindestens 60 ms low).

Mit der gleichen Argumentation könnte man behaupten BlinkWithoutDelay ließe sich nicht ohne delay programmieren, weil es ja einmal pro Sekunde blinken soll, die Leuchtdiode muss also eine halbe Sekunde an und eine halbe Sekunde aus sein.

Stimmt halt nur nicht. :cry:

Ohne delays würde man auch keine Encoder Impulse verlieren...

Hallo,

jetzt weis ich endlich was du mit puffern meinst. Du meinst den Encoderzähler. Der ist in jedem Encoderauswertungscode zwangsweise enthalten. Ich dachte bis jetzt du willst die einzelnen Änderungen an beiden Pins puffern und wußte nicht recht wofür. :confused: Jetzt nur noch das mit millis umsetzen und du verlierst keine Schritte mehr. Warum machst du das als alter Hase eigentlich nicht?
Kleiner Tipp wenns hilft. Theseus erklärt millis() :slight_smile:
http://forum.arduino.cc/index.php?topic=400102.msg2752141#msg2752141

inzwischen hat sich meine Denkblockade gelöst und der Sketch läuft nun einwandfrei. Anstatt millis habe ich meine Lieblingslibrary INTERVAL genommen. Sketch siehe Post #5

@Whandall: Dass die delays da nicht bleiben können, war von Anfang an klar. Genau deswegen hatte ich ja hier gepostet. Mir fehlte zu dem Zeitpunkt nur ein Wink in die richtige Richtung. Der alte Sketch mit delay war mehr so der Proof-of-concept, ob das ganze mit der zu steuernden Hardware überhaupt funktioniert.