Alps EC11 Problem mit Drehrichtung

Ich habe noch ein paar Probleme mit der Genauigkeit meines Alps EC11 Encoders. Der Encoder hat PinA und PinB sowie einen geminesamen Kontakt. Des weiteren einen 2 poligen Taster. Den Taster kann ich mit einem 0,1µ und externen PullDown erfolgreich entprellen. Habe es bei den Encodereingängen auch soweit versucht, ebenfalls mit 10k PullDown. Leider verhält sich der Encoder identisch, als wenn er ohne die Schaltung betrieben wird.

Habe derzeit erst einen kleinen Testcode soweit zusammengestellt. Interrupt wollte ich nicht zwingend nutzen. Zurvor habe ich mit 3 Tastern diese Sachen realisiert.

Das Problem nun, ich muss 2 Schaltstellen weiterschalten, ehe ich eine Rückmeldung bekomme, ob sich der Wert erhöhen oder verringern soll. Wenn ich etwas schneller per Hand drehe, springt der Wert immer leicht zurück, bzw. erhöht sich nicht gleich mit der Anzahl der Umdrehung.

Der Encoder hat 18Stufen.

/* LC Display */
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
const byte NUMBER_OF_ROWS = 2;
const byte NUMBER_OF_COLUMS = 16;
LiquidCrystal_I2C lcd(0x27,NUMBER_OF_COLUMS,NUMBER_OF_ROWS);

byte menu, value;
String strMenu[4] = {"Modus", "Einstellung", "Dauer", "Helligkeit"};


/* Streaming (F-Makro) */
#include <Streaming.h>


/* Encoder */
const int encoderPinA = 2;
const int encoderPinB = 3;
const int encoderPinS = 4;

//boolean encoderALast = false; // Vorigen Pin-Zustand merken

enum rotation 
{
	aus, auf, ab
} variable;


void setup()
{
	/* LC Display */
	lcd.init();
	lcd.backlight();


	/* Encoder */
	pinMode(encoderPinA, INPUT);
	// digitalWrite(encoderPinA, HIGH);

	pinMode(encoderPinB, INPUT);
	// digitalWrite(encoderPinB, HIGH);

	pinMode(encoderPinS, INPUT);
	//digitalWrite(encoderPinS, HIGH);


	/* Serielle Schnittstelle */
	Serial.begin (9600);
}


void loop()
{
	/* Auswertung Rotation */
	boolean encoderA = digitalRead(encoderPinA);
	static byte lastVariable;
	static boolean encoderALast;

	//if((encoderALast == true) && (encoderA == false)) 
	if(encoderALast == false && encoderA == true) 
	{
		if(digitalRead(encoderPinB) == true) variable = ab;
		else variable = auf;
	}

	if(lastVariable != variable) 
	{
		// Serial << F("Drehrichtung : ");
		if(variable == ab) 
		{
			// Serial << F("AB");
			if(value > 0) value -= 5;
		}
		else if(variable == auf)
		{
			// Serial << F("AUF");
			if(value < 255) value += 5;
		}
		// Serial << endl;
		variable = aus;
		lastVariable = variable;

	}
	encoderALast = encoderA;


	/* Auswertung Switch */
	boolean encoderS = digitalRead(encoderPinS);
	static boolean lastEncoderS;

	if(encoderS == true && encoderS != lastEncoderS) {
		// Serial << F("Modus : ");
		if(menu < 3) {
			menu++;
			// Serial << menu;;
		}
		else {
			menu = 0;
			// Serial << 0;
		}
		// Serial << endl;
	}
	lastEncoderS = encoderS;


	lcd.setCursor(0,0);
	lcd << strMenu[menu];
	for(int i = 0; i < 16 - strMenu[menu].length(); i++) lcd << F(" ");

	lcd.setCursor(0,1);
	if(value < 10) lcd << F(" ");
	if(value < 100) lcd << F(" ");
	lcd << value;
}

sschultewolter:
Ich habe noch ein paar Probleme mit der Genauigkeit meines Alps EC11 Encoders. Der Encoder hat PinA und PinB sowie einen geminesamen Kontakt. Des weiteren einen 2 poligen Taster. Den Taster kann ich mit einem 0,1µ und externen PullDown erfolgreich entprellen. Habe es bei den Encodereingängen auch soweit versucht, ebenfalls mit 10k PullDown. Leider verhält sich der Encoder identisch, als wenn er ohne die Schaltung betrieben wird.

Habe derzeit erst einen kleinen Testcode soweit zusammengestellt. Interrupt wollte ich nicht zwingend nutzen.

Ich habe hier mal einen Testcode für Drehgeber geschrieben, der ohne Interrupts arbeitet und die Eingänge in der loop abfragt.

Schaltung: Keine äußere Beschaltung notwendig, im Sketch werden die internen PullUps für die A- und B- Eingänge aktiviert.

Programmlogik: Während der Drehgeber dreht, wird in der Auswertung nur zusammengezählt, was an Rechts- und Linksimpulsen auftritt, und sobald eine Pause von mindestens TIMEOUT Millisekunden auftritt (Default=20ms) wird eine Auswertung der Drehgeberimpulse auf Serial ausgegeben.

Das Prellen, wenn es auftritt, wird softwaretechnisch dadurch herausgerechnet, dass am Ende immer die Differenz der gezählten Rechts- und Linksimpulse gebildet wird. Falls also beim Rechtsdrehen tatsächlich (wegen Prellens) gezählt werden sollte RRRRLRRRR wird bei der Ausgabe der Differenz automatisch die richtige Schritteanzahl ausgegeben.

Außerdem habe ich auch mal eine Erkennung für "zu schneller Überdrehen" eingebaut, d.h. falls einzelne Impulse beim Abfragen verpasst werden, werden Fehler in der Variablen "impulseError" mitgezählt.

Durch diese Programmlogik, dass beim schnellen Drehen nur schnell gezählt aber überhaupt nichts ausgegeben wird, während nur in Drehpausen eine Ausgabe auf Serial erfolgt, kann der Serial-Sendepuffer in meinem Test-Sketch nicht überlaufen.

Ich weiß nicht, ob das bei Deinem Sketch das Problem ist, aber wenn Dir beim Pollen der Eingänge nämlich der serielle Ausgangspuffer überläuft, werden die Eingänge nur noch sehr langsam und unregelmäßig abgefragt, und es gehen dann in jedem Fall Drehimpulse verloren. Falls Dir der serielle Sendepuffer überlaufen kann, wärst Du mit einer Auswertung in einer ausreichend oft laufenden Timer-Interruptroutine wahrscheinlich besser bedient.

Schaue es Dir mal an, ob Dein Drehgeber damit genauer arbeitet:

#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;
}

P.S.: Und wenn mein Sketch beim Testen Werte für "impulseError" anzeigt, dann hängt das natürlich auch mit Deinem LCD und den LCD-Ausgaben aus dem PROGMEM-Flashspeicher zusammen: Textkonstanten aus dem PROGMEM zu ziehen kostet Zeit, und ein LCD-Display ist beim Schreiben auch immer für Verzögerungen von einigen Millisekunden gut. Wenn während einer Ausgabe auf LCD zwei Drehgeber-Impulse auftreten, geht einer davon verloren. Im Zweifelsfall: Langsam genug drehen.

So, konnte deinen Sketch schon einmal ohne Probleme kompilieren, habe den Taster vorerst rausgelassen.

Habe noch einmal eine kleine Summenberechnung mit eingebaut, um die Werte besser zu kontrollieren können. Werte werden jeden Schritt konstant + 2 auf, bzw. abgerechnet. Scheint gut zu funktionieren, noch keine wirklichen Springer.

Danke

P.S.: Falls du nicht überall sowas bei deinen Sketchen besetzt (// sollte nie vorkommen), habe ich den Sketch vermutlich schonmal bei durchfliegen hier in den Foren von dir gelesen :wink:

sschultewolter:
Scheint gut zu funktionieren, noch keine wirklichen Springer.

Falls es mit längeren LCD-Textausgaben beim gleichzeitigen Drehen springen sollte, kannst Du ggf. eine Spezial-LCD-Ausgabefunktion machen, die zeichenweise auf LCD ausgibt und nach jedem einzelnen Zeichen einmal den Drehgeber abfragt.

Mein Code ist jedenfalls der einzige mir bekannte Code für Drehgeber, bei dem über "impulseError" mitgezählt wird, ob und ggf. wie oft die Eingabe springt, also nicht ausgewertet werden kann, weil die Phasenänderung weder einer Rechts- noch einer Linksdrehung entspricht. So kannst Du testen, bis zu welcher Drehgeschwindigkeit noch keine Fehler auftreten.

sschultewolter:
P.S.: Falls du nicht überall sowas bei deinen Sketchen besetzt (// sollte nie vorkommen), habe ich den Sketch vermutlich schonmal bei durchfliegen hier in den Foren von dir gelesen :wink:

Ja, gut aufgepaßt: Den Code habe ich heute zum zweiten mal ins Forum gepostet. :grin:

Ne, heute meinte ich das nicht, ist schon ein paar Wochen her :wink:

  if (!impulsAuswertung()) return;

Kurze Frage, was bewirkt diese Zeile? Bin mir da nicht 100% sicher, da keine geschweiften Klammern folgen.

=> Wenn der Wert von der Funktion ImpulsAuswertung einen fehlerhaften/false Wert zurück liefert den ganzen Loop überspringen und von vorne beginnen?

Gruß Stefan

Ja. return beendet ja eigentlich eine Methode. Wenn du das in loop() machst, fängt er wieder von vorne an, da du da nicht raus kommst.

sschultewolter:

  if (!impulsAuswertung()) return;

Kurze Frage, was bewirkt diese Zeile? Bin mir da nicht 100% sicher, da keine geschweiften Klammern folgen.

Geschweifte Klammern gruppieren mehrere Zeilen. Man benuzt sie nach Bedingungen, in der Definition von Funktionen bei FOR und anderem.
Wenn man nur 1 Zeile bedingt braucht, braucht man keine geschwungene Klammern. Im oben zitierten IF ist eine einzige Zeile / Funktion die ausgeführt werden soll und zwar das return(); .

Grüße Uwe

Ja, hattte ich mir schon so gedacht. War mir nur nicht so sicher, ob da nicht evtl nur einfach die Klammern füür den unteren Bereich fehlten.

Kurzes Feedback. Läuft noch nicht ganz so, wie gewünscht. ich möchte einen Wert immer i++ wenn machen, wenn die Pos 1 weiterwandert. Habe es bereits schon versucht umzuscheiben, läuft aber noch nicht wie gewünscht. Näheres morgen.

So, folgendes Problem mit dem Sketch,
ich habe den defenierten Timeout zur Feinjustierung abgeändert auf Werte zwischen 0 und 1000. Leider gibt es hier immer noch Probleme.

Ich bekomme beim hin und herschalten immer mal wieder einen Error. Des weiteren gibt es immer wieder denn fall, dass Anstatt 1x R=2 nur 2x R=1 zurückgeliefert wird.

Ich bekomme es derzeit nicht hin, den Drehencoder so einzustellen, dass er mir bei einer Drehung zum nächsten Schaltpunkt fehlerfrei einen Wert zurückliefert.