Go Down

Topic: Magnetische Encoder auswerten (Read 4 times) previous topic - next topic

DrFlopp

Hallo Dani,
ich habe heir einmal die Encoderauswertung:

void doEncoderA(){

  // look for a low-to-high on channel A
  if (digitalRead(encoder0PinA) == HIGH) {
    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinB) == LOW) { 
      encoder0Pos = encoder0Pos + 1;         // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }

  else   // must be a high-to-low edge on channel A                                       
  {
    // check channel B to see which way encoder is turning 
    if (digitalRead(encoder0PinB) == HIGH) {   
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
}

void doEncoderB()
{
  // look for a low-to-high on channel B
  if (digitalRead(encoder0PinB) == HIGH)
  {   
    // check channel A to see which way encoder is turning
    if (digitalRead(encoder0PinA) == HIGH)
    { 
      encoder0Pos = encoder0Pos + 1;         // CW
    }
    else
    {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }

  // Look for a high-to-low on channel B

  else {
    // check channel B to see which way encoder is turning 
    if (digitalRead(encoder0PinA) == LOW) {   
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
  //  encoder0PosLast = encoder0Pos;
  encoder0PosLast = encoder0Pos;     
}

Wenn Du eine bessere Auswertung kennst würde ich mich sehr freuen.

DrFlopp

Udo Klein

#16
Jun 18, 2011, 05:02 pm Last Edit: Jun 18, 2011, 05:21 pm by Udo Klein Reason: 1
Bitte Code mit Code Tags einstellen, dann wird er so lesbar wie mein Beispiel unten.
Dein Code sieht irgendwie merkwürdig aus. Ich würde sowas eher in folgendem Stil implementieren:

Code: [Select]

int8_t decode(uint8_t pinA, uint8_t pinB)  {
   return (digitalRead(PinA) == digitalRead(PinB)?  -1:  1);
}

Position0 += decode(encoder0PinA, encoder0PinB);



Check out my experiments http://blog.blinkenlight.net

finu

....schau dir mal den Befehl increment und decrement an. Das wird schneller ausgeführt wie z.B. a=a+1

Versuch mal A auf einen Interrupteingang zu legen. (steigende Flanke auswerten)
http://arduino.cc/en/Reference/AttachInterrupt
B bleibt an irgendeinem Digitalpin

Wenn an Kanal A die Flanke steigt wird die Interruptroutine gestartet.

Dort prüfst du:
wenn Kanal B = LOW, dann ++encoder0Pos (increment)
ansonsten
--encoder0Pos (decrement)

die Variable encoder0Pos wird somit bei jeder ansteigenden Flanke erhöht oder verringert, je nach Drehrichtung.

Grüße, finu

Udo Klein

#18
Jun 18, 2011, 05:19 pm Last Edit: Jun 18, 2011, 05:22 pm by Udo Klein Reason: 1
Bist Du Dir sicher, daß der Optimizer des GCC nicht schlau genug ist sowas wegzuoptimieren? Der Optimizer ist normalerweise so gut, daß er manchmal schnelleren Code erzeugt als der erste Anlauf in Assembler. Wenn der Funktionscalloverhead wirklich ein Problem ist kann man auch noch "inline" dranschreiben. Allerdings hat DrFlopp keine echten Performanceprobleme, sonst würde er nicht "digitalRead" nehmen. Das ist die Stelle die man zuerst optimieren müsste.
Check out my experiments http://blog.blinkenlight.net

finu

:smiley-roll-blue: Aua, mein Post hat einen Fehler. Bei der Drehrichtungsänderung geht ein Puls verloren. Zum Ausprobieren ob deine Pegel (bzw. die vom Encoder  XD) ok sind reicht es aber aus.

@Udo
Wie schlau der Optimizer ist? Keine Ahnung, habe gerade erst mit dem Arduino angefangen.

Udo Klein

@finu: das war eine rhetorische Frage / ein Wink mit dem Zaunpfahl. Der Optimizer des GCC ist besser als Du Dir vorstellen kannst. Insbesondere ist Dein Vorschlag völlig nutzlos. Selbstverständlich wird sowas vom GCC optimiert. Davon abgesehen zäumst Du das Pferd von hinten auf. Bei Performanceoptimierungen schaut man zuerst nach strukturellen Problemen, dann nach Algorithmusoptimierungen bzw. besseren Algorithmen und erst ganz am Schluß nach Codeoptimierungen. Jede andere Vorgehensweise ist ziemliche Zeitverschwendung.
Check out my experiments http://blog.blinkenlight.net

finu

Quote
Wink mit dem Zaunpfahl


Der Zaunpfahl hat sein Ziel getroffen  =(

Quote
Insbesondere ist Dein Vorschlag völlig nutzlos


Grrrrrr, stimmt. Habe es ausprobiert.
++a versus a=a+1 bringt keinen Zeitvorteil.

http://www.mikrocontroller.net/articles/Drehgeber
...hier findet man einen interessanten Artikel zu Drehgebern. Dort wird auch das Thema Auswertung behandelt.

Grüße, finu

volvodani

=> Ganz Ganz Wichtig Interrupt Funktionen so kurz wie möglich halten <=

Ich schmeiße mal meinen Code hier rein:
Code: [Select]

void setup (){
  attachInterrupt(0,CHAInt,CHANGE);        // Interrupt 0 führt CHAInt aus mit wechslende Flanken
  pinMode(2,INPUT);                               // Pin2 als Input
  pinMode(3,INPUT);                               // Pin3 als Input
  digitalWrite(2,HIGH);                            // Interne Pull Ups benutzt
  digitalWrite(3,HIGH);                            // Interne PullUps benutzt
}

void CHAInt (){
  int Aread=0;
  int Bread=0;
  Aread=digitalRead(A);
  Bread=digitalRead(B);
  if (Aread==HIGH && Bread==LOW||Aread==LOW && Bread==HIGH){
    Wert++;
  }
  if (Aread==LOW && Bread==LOW ||Aread==HIGH&& Bread==HIGH){
    Wert--;
  }
}
So ist das Leben:
Manchmal bis du das Denkmal, manchmal die Taube!

Udo Klein

#23
Jun 19, 2011, 02:17 pm Last Edit: Jun 19, 2011, 02:22 pm by Udo Klein Reason: 1
Warum nicht so:

Code: [Select]

void setup (){
 attachInterrupt(0,CHAInt,CHANGE);        // Interrupt 0 führt CHAInt aus bei wechslende Flanken
  // pins pullup aktivieren
 pinMode(PinA, INPUT);                          
  digitalWrite(PinA, HIGH);   
 pinMode(PinB, INPUT);                          
 digitalWrite(PinB, High);                          
}

void CHAInt (){
 Wert+= (digitalRead(PinA) == digitalRead(PinB)? -1: 1);
}


Problem bei der ganzen Sache ist, daß es in diesem Code eine üble Race Condition gibt. Und zwar: es gibt keine Garantie, daß der Zustand der Pins zum Auslesezeitpunkt so ist wie zum Auslösezeitpunkt des Interrupts. Außerdem werden beide Pins zu verschiedenen Zeiten ausgelesen. Um das zu kompensieren wird man sich den Portzustand "atomar" holen und puffern müssen. D.h. man muß direkt auf die Ports. Dadurch wird die ISR aber auch performanter.

Sobald ich etwas Zeit habe überlege ich mal wie man das korrekt implementiert. So sieht es nur richtig aus wird aber bei starkem Rauschen garantiert nicht sauber funktionieren.
Check out my experiments http://blog.blinkenlight.net

volvodani

Komisch Udo wenn ich dich so höre, könnte man den Eindruck gewinnen das alle das hier falsch machen bis auf Einen. ;)
Probiere es mal aus und es funktioniert so ohne Probleme.
So ist das Leben:
Manchmal bis du das Denkmal, manchmal die Taube!

Udo Klein

#25
Jun 19, 2011, 02:49 pm Last Edit: Jun 19, 2011, 02:55 pm by Udo Klein Reason: 1
@Volvodani: Klar funktioniert das wenn Du es einfach so probierst. DrFlopp hatte aber was von starkem Rauchen geschrieben und dann funktioniert das nicht mehr. Hast Du das mit verrauschten Signalleitungen probiert oder glaubst Du nur, daß es dann auch funktioniert?

Vieleicht liegt es ja auch daran, daß Ihr es tatsächlich falsch macht? ;)

Nur mal so: was macht der Code bei folgendem Timing:
Code: [Select]

Pin A: 0 1 0 1 0 0 0 0 0
Pin B: 0 0 1 1 0 0 0 0 0

Zeit:  0 1 2 3 4 5 6 7 8
         

Und was bei folgendem Timing

Code: [Select]

Pin A: 0 1 0 1 0 0 0 0 0
Pin B: 0 0 1 1 1 0 0 0 0

Zeit:  0 1 2 3 4 5 6 7 8
   

In beiden Fällen:

0 = Ausgangssituation
1 = Interrupt triggert und ISR startet
2 = Interrupt triggert wird aber gequeued weil ISR noch nicht fertig
3 = Interrupt triggert wird aber nicht gequeued weil ISR schon läuft und ein IR bereits in der Queue ist
4 = Pin A wird gelesen
5 = Pin B wird gelesen
6 = ISR fertig, aufgelaufener Interrupt startet ISR erneut
7 = Pin A wird gelesen
8 = Pin B wird gelesen

Was wäre das erwartete Ergebniss gewesen?

Ich glaube ohne geeignete externe Beschaltung kann man sowas bei verrauschten Signalen nicht zuverlässig hinbekommen. Wenn man hingegen kein Rauschen hat, dann funktioniert der Code auch.
Check out my experiments http://blog.blinkenlight.net

DrFlopp

@Dani, Also ich habe Deinen Code ausprobiert. Der liefert aber keine Position sondern nur irgend einen Wert wenn sich der Encoder dreht. Wenn der Encoder steht liefert der Code 0.
Da stimmt etwas noch nicht.
Gruß DrFlopp

volvodani

Da du in der Main ja nicht weiterarbeitest mit der Funktion muss du aus
Wert++ bzw Wert--
++Wert bzw --Wert machen.
Ich werte nämlich bei mir nur die Beweung aus Linksrum halt minus rechtsrum halt Plus
So ist das Leben:
Manchmal bis du das Denkmal, manchmal die Taube!

DrFlopp

Hallo Dani,
das ist echt immerwieder verblüffend für mich als Neuling mit wie einfachen dingen man unter C dinge beeinflussen kann. Hast Du vielleicht auch noch einen Tipp um die Geschwindigkeit effizient zu berechnen?

Ich habe bei meiner Anwendung noch ein weiteres kleines Problem, ich steuer ein LCD an. Die LCD-Bibliothek benutzt glaube ich festgelegte Pinns, die sich mit den Interrupts überschneiden. Kann ich eigendlich jeden Pinn für die LCD-Anzeige konfigurieren? (Auch die Analogeingänge?)

Gruß
DrFlopp

volvodani

Du kannst auch die Analog In nutzen A0-A5 =>digital 14-18
also erst als Ausgang deklarieren und dann digital "benutzten"
So ist das Leben:
Manchmal bis du das Denkmal, manchmal die Taube!

Go Up