Forum: Mikrocontroller und Digitale Elektronik Drehimpulsgeber


von Dominik (Gast)


Angehängte Dateien:

Lesenswert?

Hallo

ich versuche einen Drehimpulsgeber von Conrad(705594 - 62) an meinem 
xmega128A1 zu betreiben.

Angeschlossen sind PinA und PinB des Impulsgebers an PORTC Pin0 und 
Bin1. Ich verwende hierfür die integrierten 
PullUps(PORT_OPC_WIREDANDPULL_gc).



Folgender Code wird in einem Interrupt jede Millisekunde ausgeführt:


cli();
newB = (PORTC.IN & 0x02);
newA = (PORTC.IN & 0x01);
if(newB != oldB || newA != oldA)
{
  xorA = newA != oldA;
  xorB = newB != oldB;
  if(oldA == oldB)
  {
    if(xorA == 0 && xorB == 1) cnt++;
    if(xorA == 1 && xorB == 0) cnt--;
  } else {
    if(xorA == 0 && xorB == 1) cnt--;
    if(xorA == 1 && xorB == 0) cnt++;
  }
  oldA = newA;
  oldB = newB;
}
sei();

Leider funktioniert dieser Code nur halb.
Bei einer Drehung im Uhrzeigersinn wird wie gewollt incrementiert aber 
anschließend sofort wieder decrementiert. Nach dem 3. Drehimpuls wird 
dann 2 mal decrementiert.

Bei einer Drehung gegen den Uhrzeigersinn ist das gleiche Verhalten zu 
beobachten nur wird nach dem dritten Drehimpuls doppelt incrementiert.

Ich starre den Code nun schon eine geschlagene Stunde an aber weiß 
nicht, wo das Problem liegt.
Bei der logischen Gestaltung habe ich mich an den Algorithmus aus dem 
Datenblatt des Drehimpulsgebers gehalten welcher hier auch angefügt ist.

Seht ihr vielleicht wo das Problem liegt?


Danke schon mal
Gruß Domi

von Karl H. (kbuchegg)


Lesenswert?

Dominik schrieb:
<
 Ich starre den Code nun schon eine geschlagene Stunde an aber weiß
> nicht, wo das Problem liegt.

Das du den Code anstarrst ist zwar schön, wie du aber gesehen hast, 
hilft dir das nicht viel.

Nimm Papier und Belistift, 'erfinde' die Eingangswerte, für den Ablauf, 
der zu Problemen führt und geh den Code durch, wobei du alle Operationen 
genau so machst, wie sie dein Computer auch macht.

So findet man raus, wo man den Denkfehler gemacht hat.

von Dominik (Gast)


Lesenswert?

Das mit dem "Anstarren" war eine Anspielung auf die vielen 
Lösungsversuche die ich schon hinter mir habe. Unter anderem auch das 
durchspielen eines Beispiels.

Aber danke für den Hilfeversuch.

von Karl H. (kbuchegg)


Lesenswert?

und den cli() bzw. sei() entfernst du stillschweigend gleich mal aus der 
ISR (aber das ist im Moment nicht das Problem)

von Karl H. (kbuchegg)


Lesenswert?

Dominik schrieb:
> Das mit dem "Anstarren" war eine Anspielung auf die vielen
> Lösungsversuche die ich schon hinter mir habe. Unter anderem auch das
> durchspielen eines Beispiels.

Na dann zeig doch mal.

Der ENcode steht auf

  A    B    oldA   oldB    xorA    xorB

  0    0     0       0

jetzt wechselt der Encoder zu

  1    0

wie gehts weiter?

Dann wechselt er zu

  1    1

und weiter gehts zu

  0    1

und

  0    0


für diese Reihenfolge (ich hoffe ich hab mich da jetzt nicht vertan) 
spielst du die entsprechenden Durchläufe durch die ISR durch (wobei du 
natürlich dann auch immer den jeweils vorhergehenden Wert von oldA und 
oldB benutzt).

Ecntschulige: aber wir müssen hier auf der anderen Seite des Bildschirms 
genau das gleiche tun, um dem Fehler auf die Spur zu kommen. Da es aber 
dein Projekt ist, solltest du das in erster Linie tun und nicht wir.

von Peter D. (peda)


Lesenswert?


von michael_ohl (Gast)


Lesenswert?

Das Problem könnte auch im Drehgeber selbst liegen. Die Dinger prellen 
wie Hund und die Überlappung der Kontakte ist nicht so wie im 
Datenblatt. Wenn Du die Möglichkeit hast guck Dir das Ding mal mit einem 
Ossy an dann siehst Du vielleicht das es so nicht gehen kann.


mfG
Michael

von Peter D. (peda)


Lesenswert?

michael_ohl schrieb:
> Das Problem könnte auch im Drehgeber selbst liegen. Die Dinger prellen
> wie Hund und die Überlappung der Kontakte ist nicht so wie im
> Datenblatt.

Mit entprellender Software ist das überhaupt kein Problem.


Peter

von Dominik (Gast)


Lesenswert?

Gemäß dem Fall dass sich jeder um seine Fehler und Anliegen kümmert auch 
wenn dies Stunden dauert hätte das den Verlust der Existenzberechtigung 
dieses Forums zur Folge.
Gepostet habe ich mein Problem hier da ich mir sicher bin, dass jemand 
der schon mal einen Drehgeber in Betrieb genommen hat, den Fehler sofort 
sehen würde. Und ich denke viele Beiträge dieses Forums beruhen auf 
dieser Intention aber ich möchte keine Grundsatzdiskussion beginnen 
sondern mein Problem lösen.

Anstatt nochmal ein Beispiel händisch durch meinen Corde zu exorzieren 
habe ich mal ein Beispiel durch das angefügte Schema geführt. Mit dem 
Ergebnis, dass da was nicht stimmen kann da dort die Unterscheidung 
zwischen Übergang(AB = 01/10) zu einer Auswertung führt und die 
anschließende Endposition(wechsel zu AB = 11/00) nochmals.

Ich scheine den Code noch nicht verstanden zu haben.
Gehe ich richtig in der Annahme das bei jeder Einrastung des Drehgebers 
AB = 00 oder 11 ist und nur bei einer Drehung sich entweder A oder B 
zuerst ändert.
Also
1. Rastpunkt: AB = 00
Übergang CW: AB = 10
2.Rastpunkt: AB = 11
übergang CCW: AB = 01
1. Rastpunkt: AB = 00


Gruß Domi

von Peter D. (peda)


Lesenswert?

Der Knackpunkt ist, daß es bei jedem Phasenwechsel zu Prellungen kommen 
kann. Du mußt daher Deinen Code daraufhin abklopfen, ob er jederzeit 
diese Prellungen kompensieren kann.

Mein Code geht daher den Ansatz, daß er alle 4 Phasenwechsel zählt. Und 
da sich gemäß Graycodedefinition immer nur ein Pin ändert, zählt er 
jeden zuviel gezählten Preller bei der nächsten Abtastung exakt wieder 
zurück. Nur für die Auswertung wird dann das Ergebnis je nach Rastung /2 
bzw. /4 geteilt, wobei die für die Entprellung wichtigen unteren Bits 
erhalten bleiben.

Es mag andere effizientere Lösungen geben, aber solange eine Lösung 
nicht funktioniert, gehört sie nicht zu diesen. Du mußt also nur 
feststellen, ob Deine Lösung auch zielführend oder eine Sackgasse ist.

Ich denke, für ein Forum reicht es, eine funktionierende Lösung 
aufzuzeigen. Damit jemand kostenlos Arbeit für Dich aufwendet, muß es 
dafür auch einen Anreiz geben. Also ein Problem, für das es noch keine 
oder nur eine sehr aufwendige Lösung gibt.


Peter


P.S.:
Haben die Xmega nicht sogar Encoderhardware?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Dein Code sieht richtig aus. Stimmen die Zeitdigramme im Datenblatt,
sollte der Zähler (cnt) bei jeder Raste genau zweimal inkrementiert bzw.
dekrementiert werden.

Deine Beobachtungen deuten aber daraufhin, dass die Diagramme im Daten-
blatt nicht stimmen, und dass die Rastpunkte nicht zwischen, sondern
auf den Flanken eines der beiden Signale A und B liegen. Dann kann der
Zähler bei einer Drehung auf die jeweils nächste Raste 1, 2 oder 3
Inkremente weiterzählen. Bei der Auswertung muss dies natürlich berück-
sichtigt werden. Das Ablaufdiagramm aus dem Datenblatt wäre dann falsch.
Das wäre nicht weiter verwunderlich, denn auch das zweite Ablaufdiagramm
(mit der Interruptsteuerung) ist Murks. Drehgeberauswertungen, die durch
einen Interrupt eines der beiden Drehgebersignale ausgelöst werden,
haben noch nie zuverlässig funktioniert.

Um festzustellen, ob obiges auf deinen Drehgeber zutrifft, musst du
nachschauen, wann genau die Signale A und B umschalten. Liefert bereits
eine minimale Bewegung des Drehgebers aus einer Rastposition heraus nach
rechts oder links eine Signalflanke, musst du deine Auswertung ändern.
Ist hingegen auf beiden Seiten der Rastposition etwas Luft, in der sich
die Signale nicht ändern, entspricht der Drehgeber dem Datenblatt, und
irgendwo hat sich noch ein anderer Fehler versteckt.

von Dominik (Gast)


Lesenswert?

Wie du bereits sagtes gibt es die Möglichkeit einer effizienteren 
Lösung. Aus diesem Grund lasse ich mich ungerne von anderen Quellcodes 
leiten da man durch dessen logische Vorgehensweise vorbelastet wird und 
diese oft unbewusst übernimmt.

Nach 2 weiteren Stunden des "Anstarrens" meines Codes bin ich nun 
endlich auf eine alternative Lösung gekommen welche immun gegen prellen 
ist. Ob diese nun effizient ist oder nicht darf jeder für sich 
beurteilen.

Vorab noch: der eigentliche Fehler lag in der Datentypengestaltung 
meiner Variablen newA newB oldA usw. Durch die Verwendung des in 
stdbool.h enthaltenen bool Datentyps funktioniert folgender Code nun 
einwandfrei bei mir:


newB = (PORTC.IN & 0x02);
newA = (PORTC.IN & 0x01);
if(newB != oldB || newA != oldA)
{
  if(oldA == oldB)
  {
    xorA = newA != oldA;
    xorB = newB != oldB;
    if(xorA == 0 && xorB == 1) cnt--;
    if(xorA == 1 && xorB == 0) cnt++;
  }
}
oldA = newA;
oldB = newB;


Wie bereits angedeutet wird die Drehrichtung aus dem Verlassen der 
Gleichheit von A und B erkannt. In einer Rastposition ist A und B immer 
gleich. Bewegt sich der Drehgeber ändert sich entweder A oder B zuerst. 
War vor dieser Änderung A und B gleich so ergibt sich die Drehrichtung 
aus dem Zuerst geänderten Wert.

Ich poste diese Lösung unter Vorbehalt. Ich habe sie nicht auf extreme 
Rahmenbedingungen getestet.

Gruß Domi

von Dominik (Gast)


Lesenswert?

Stichwort Rahmenbedingung:
Ich verwende keine Hardware-Entprellung und verwende die internen 
PullUps. Somit ist der Drehgeber direkt an den XMega angeschlossen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Dominik schrieb:
> Vorab noch: der eigentliche Fehler lag in der Datentypengestaltung
> meiner Variablen newA newB oldA usw.

Stimmt, das hatte ich übersehen, als ich schrieb, dass der Code richtig
aussähe. Das Probem liegt in dieser Zeile:
1
  if(oldA == oldB)

Sind die alten Signalpegel beide high, dann ist oldA=1 und oldB=2, so
dass sie fälschlicherweise als ungleich ausgewertet werden. Mit der
Verwendung von Bool-Variablen ist der Wertebereich auf {0, 1} einge-
schränkt, so dass dieses Problem nicht auftreten kann.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.