Ausgangssituation
An den Motoren sind Lichtschranken und Schlitzscheiben zur Erfassung der Drehung angebracht. Jeder Motor hat dabei zwei Gabellichtschranken (A und B) die ein um 90° versetztes Signal liefern. Ein Mikrocontroller soll nun diese Signale auswerten.
Schematischer Ablauf der Auswertung
Für die Auswertung eines Encoders soll ein Interrupt bei jeder steigenden und fallenden Flanke der Signale A und B am Mikrocontroller ausgelöst werden. Innerhalb einer Interrupt Service Routine (ISR) werden die Signale ausgewertet und die zurückgelegten Umdrehungen des Motors ermittelt. Durch die Auswertung beider Flanken von zwei Gabellichtschranken, ergibt sich eine vierfache größere Genauigkeit als Schlitze auf der Scheibe sind. Der Nachteil ist aber auch eine höhere rechen Belastung des Mikrocontrollers.
Für einen Motor mit max 5000 UPM und einer Schlitzscheibe mit 32 Strichen ergibt sich:
5000 upm∙36 Striche ∙4 Flanken = 12kHz
Da der µC die Auswertung für zwei Motoren machen muss, müssen 24.000 ISR pro Sekunde verarbeitet werden.
Bei jedem Interrupt werden die Zustände der beiden Signale erfasst und gespeichert (kleines a und b). Dabei durchläuft die Signalabfolge genau 4 Phasen (s. Bild oben) bis die Abfolge wieder von Forn beginnt. Bei jedem Interrupt weren die zuvor gespeicherten und neuen (großes A und B) Zustände verglichen. Die Signale a, b und A, B können zusammen 16 verschiedene Werte annehmen, je nachdem, ob das Sibnal 1 oder 0 ist.
Da sich von der einen auf die andere Phase nur ein Signal ändern darf, sind in den 16 möglichen Werten einige die ungültig. Das nebenstehende KV-Diagramm zeigt alle möglichen Zustände, wobei A und B die aktuellen und a und b die Signale der vorhergehenden Messung darstellen. Die Signale mit einem Ausrufezeichen stehen für LOW und die ohne für HIGH.
Zur Veranschaulichung des KV-Diagrams, hier einmal ein Beispiel: Im zweiten dunkel grauen Feld oben links (das mit der 1) ist das Signal B HIGH und hat sich nicht geändert (kleines b war auch HIGH), wobei A von LOW auf HIGH gewechselt hat. Dies ergibt dann eine Drehung des Motors um genau einen Flanke(Schritt). Bei negativen Schritten hat sich der Motor in die entgegengesetzte Richtung gedreht und bei 0 gar nicht.
Programmstruktur
Die alten und neuen Signale werden bei jedem Interrupt in einer Variablen (bPhase) gespeichert, welche genau dann genau die 16 möglichen Werte darstellt. Mit diesem Wert wird aus einer festen Tabelle (aSteps) der entsprechende Schritt ermittelt. Die Tabelle ergibt sich aus dem KV-Diagramm.
Quellcode
#DEFINE IS_SET_MOT1_SIG_A (PINC & (1<< PINC1) #DEFINE IS_SET_MOT1_SIG_B (PINC & (1<< PINC2) #DEFINE IS_SET_MOT2_SIG_A (PINC & (1<< PINC3) #DEFINE IS_SET_MOT2_SIG_B (PINC & (1<< PINC4) #DEFINE INV -128 typedef union { struct { unsigned Signal_A :1; // neues Signal A unsigned Signal_B :1; // neues Signal B unsigned AltSignal_A :1; // altes Signal a unsigned AltSignal_B :1; // altes Signal b } x; uint8_t bRow; } tPhase; Encoder { // Member Variablen long lEncoder; // enthält die gezählten Flanken des Motors long lError; // enthält die gezählten Fehler des Motors tPhase bPhase; // enthält das vergangenen und neuen Signale // Funktionen void ISR(); // Interrupt Service Routine long GetEncoder(); // gibt die Anzahl der Flanken zurück long GetError(); // gibt die Anzahl der Fehler zurück void Reset(); // setzt die Flanken und Fehler auf Null zurück ISR(PCINT0_vect) { // lookup table static const int8_t aSteps[] = { 0, -1, 1, ENC_INV, 1, 0, ENC_INV, -1, -1, ENC_INV, 0, 1, ENC_INV, 1, -1, 0 }; int8_t iStep = 0; // Schrittweite des Motors // alte Phase um zwei Bit nach links verschieben bPhase.bRow = (bPhase.bRow << 2); // alles ausser a und b löschen bPhase.bRow &= 0x0C; // 0000 1100 = 0x0C // Signal A einlesen bPhase.x.Signal_A = IS_SET_MOT1_SIG_A ? true : false; // Signal B einlesen bPhase.x.Signal_B = IS_SET_MOT1_SIG_B ? true : false; // Schrittweite aus Tabelle auslesen iStep = aSteps[bPhase.bRow]; if (iStep != ENC_INV) { lEncoder += iStep; } else { lError++; } }
Reichweite
In den Variablen lEncoder_R und lEncoder_L werden die zurückgelegten Drehungen der Motoren gespeichert. Diese Werte sind auch außerhalb der ISR verfügbar und können so für eine Streckenmessung oder eine Drehzahlregelung verwendet werden. Nun ist es aber auch wichtig zu überprüfen wie weit man mit einer Streckenmessung kommt. Bei meinem Roboter speichere ich die zurückgelegten Drehungen, genauer gesagt die gezählten Flanken in einer 64bit Variablen. Diese hat einen Wertebereich von -2.174.483.648 bis +2.174.483.647. An den Motoren ist ein Getriebe mit einer Übersetzung von 1/30 direkt angebracht und zu den Reifen noch einmal zwei Zahnräder mit 45/60 Zähnen. Jeder Reifen hat einen Radumfang von ca. 31cm.
Keine Kommentare:
Kommentar veröffentlichen