Hallo an alle, ich möchte in ASM meinen Encoder auswerten. Ich wollte das so machen, wie es Peter Dannegger hier im Forum beschrieben hat. Beitrag "Drehgeber auslesen" Ich habe zwar schon mehrfach seine Beschreibung duchgelesen und versucht aus seinem C-Code etwas herauszulesen. Aber an einer Stelle komme ich einfach nicht weiter. Das habe ich bis jetzt gemacht: Testboard mit ATMEGA 8, 2 Leds und 4-zeiliges LCD angeschlossen. Durch die Leds kann ich den Zustand der Encoder-Pins erkennen. Im LCD lasse ich mir den Dezimalwert (umgewandelter Gray-Code) der Pins anzeigen. Richtige Reihenfolge ist nun 0-1-2-3-0 bzw 0-3-2-1-0. Ich verwende einen STEC11B03 Encoder von Reichelt mit Rastung. Rastung ist bei 0 und 2. Mein Code im Timer-Interrupt (Codeausschnitt siehe Anhang). Es wäre schön, wenn mir jemand sagen könnte, wie der Code nun weiter gehen muss. Axel
Hi Axel Ich gehe davon aus, Du möchtest einen handbedienten rastenden mechanischen Drehgeber auswerten. So wie Du angefangen hast, wird das nur Pfusch. Schau Dir mal Falks Artikel im Wiki an: http://www.mikrocontroller.net/articles/Drehgeber Um das umzusetzen, brauchst Du erstmal einen Timer, der Dir etwa alle 1ms einen Interrupt auslöst. Ob nun die Drehgeberauswertung in der ISR erfolgt, oder durch einen Merker (Semaphore, Flag) synchronisiert wird, ist erstmal Nebensache, das entscheidet sich, wenn der Rest der Aufgaben des Programms bekannt ist. Wenn Du dann (ohne Warteschleifen!) in der Lage bist, einen Job alle 1ms aufzurufen, dann kannst Du Dich um die eigentliche Drehgeberauswertung kümmern. Dazu - liest man den Zustand ein, - maskiert alle anderen Bits aus, - ORt den um 2 Bits verschobenen Zustand vom letzten mal dazu und erhält eine 4-Bit-Zahl. - Man setzt den Z-Pointer (für LPM, also mit doppeltem Wert der Adresse) auf eine Tabelle mit 16 Einträgen, in der die Zählwerte (-1, 0, +1) der Zustandswechsel stehen, - addiert den bereits ermittelten Zustandsmix als Offset dazu, - pickt sich mit LPM den Zählwert (das Inkrement) aus der Tabelle - und addiert diesen zum aktuellen Zählerstand. Der aktuelle Zählerstand wird also um 1 vermindert (-1), um 1 erhöht (+1)oder bleibt gleich (0). Ein Beispiel findest Du hier: Beitrag "Kleiner Funktionsgenerator mit Tiny2313" Der relevante Codeblock ist dieser:
1 | drehgeber: ;wertet Drehgeberbewegungen aus |
2 | in wl,dgpin ;Drehgeber-Port einlesen |
3 | andi wl,dgneumsk ;nur die benutzten Bits werten |
4 | lsl dgalt ;altes Drehgeber-Bitmuster |
5 | lsl dgalt ;nach oben schieben |
6 | or dgalt,wl ;neue Drehgeberbits einblenden |
7 | andi dgalt,dgaltmsk ;Index begrenzen (uralt löschen) |
8 | ldi zl,low(dgtab*2) ;Tabelle mit |
9 | ldi zh,high(dgtab*2) ;Dregheber-Increment-Werten |
10 | add zl,dgalt ;Index addieren |
11 | adc zh,null ;evtl. Übertrag berücksichtigen |
12 | lpm wl,z ;Inkrement-Wert holen (0, +1 oder -1) |
13 | ;in wl steht das Drehgeber-Inkrement, das die Drehrichtung angibt |
14 | tst wl ;wurde der Drehgeber gedreht? |
15 | brne drehgeber00 ;ja... |
16 | rjmp drehgeber_end ;nein, weiter... |
17 | drehgeber00: ;am Drehgeber wurde (wirklich) gedreht |
18 | mov dgi,wl ;Drehgeber-Increment übernehmen |
19 | drehgeber0: ;am Drehgeber wurde (virtuell) gedreht |
20 | add dgz,dgi ;Drehgeber-Increment aufaddieren |
21 | ;... (hier war die Begrenzung des Zählumfangs und noch etwas mehr) |
22 | drehgeber_end: |
23 | ret ;fertig, zurück... |
24 | |
25 | dgtab: ;Tabelle mit Drehgeber-Werten (alt-alt-neu-neu als Index) |
26 | ;aa nn, aa nn |
27 | .db 0, 0 ;00 00, 00 01 |
28 | .db 1, 0 ;00 10, 00 11 |
29 | .db 0, 0 ;01 00, 01 01 |
30 | .db 0,-1 ;01 10, 01 11 |
31 | .db -1, 0 ;10 00, 10 01 |
32 | .db 0, 0 ;10 10, 10 11 |
33 | .db 0, 1 ;11 00, 11 01 |
34 | .db 0, 0 ;11 10, 11 11 |
Die Routine wird als Unterprogramm im festen Zeitraster von etwa 1ms aufgerufen, dieses Tempo reicht für handbetätigte rastende Drehgeber. Da es verschiedene Ausführungen von Drehgebern gibt, muss die Tabelle zum Drehgeber passen. Die Beispieltabelle passt zu den preiswerten Drehgebern von Pollin (die mit dem dicken Knopf). Die Deklaration der verwendeten Konstanten und Registernamen findest Du im verlinkten Originalcode. ~
> - addiert den bereits ermittelten Zustandsmix als Offset dazu,
Hier ist natürlich Index gemeint und nicht Offset, sorry
~
Hallo sinusgeek, danke erstmal für deine Antwort. > So wie Du angefangen hast, wird das nur Pfusch. Also wenn du das bezüglich des LCDs und der Leds meinst, das habe ich nur zur Überprüfung meiner Werte angeschlossen. Wenns funktioniert entferne ich das LCD und die LEDs wieder. Ich hab schon eine sehr hohe Meinung von Peters Codes und Beiträgen. Deshalb hab ich in diese Richung angefangen. Habe mir auch einige andere Beiträge über Encoder hier aus dem Forum ausgedruckt und angesehen. Leider hab ich absolut null Plan von C, kann das also nicht für mich in ASM umwandeln. Werde mir aber erstmal deinen Code und den Beitrag von Falk zu Gemüte führen. Ich wollte aber auch verstehen wie's funktioniert und nicht einfach kopieren und einfügen. Axel
Axel Hüser wrote: > Das habe ich bis jetzt gemacht: > Testboard mit ATMEGA 8, 2 Leds und 4-zeiliges LCD angeschlossen. Durch > die Leds kann ich den Zustand der Encoder-Pins erkennen. Im LCD lasse > ich mir den Dezimalwert (umgewandelter Gray-Code) der Pins anzeigen. > Richtige Reihenfolge ist nun 0-1-2-3-0 bzw 0-3-2-1-0. Da bist du doch schon sehr weit. Du kannst schon die Drehrichtung feststellen (aufsteigende oder absteigende Zahlenreihe) und bekommst beim Drehen sich ändernde Werte. Beides kannst du auswerten, um eine Variable und damit einen Ausgang am AVR o.ä. zu verändern. Was willst du denn mit dem Drehgeber beeinflussen?
Hi Noch was zu Gemüte führen: TIMER0_COMPA: push r24 ; Timer0 CompareA in r24,SREG ; Aufruf 1ms Takt push r24 push r25 push ZL push ZH in r24,PINB ; Tasten in r25,PINE ; Drehgeber com r24 com r25 ; beide invertieren swap r24 ; Werte nach unteres Nibble lsr r24 ; nach Bit 2:0 andi r24,0b00111000 ; Rest ausblenden swap r25 ; werte nach oberes Nibbble lsr r25 ; nach Bit 5:3 andi r25,0b00000111 ; Rest Ausblenden or r24,r25 ; zusammensetzen push r24 ; Merken in r25,GPIOR0 ; alter Wert push r25 ; Merken out GPIOR0,r24 ; neuen Wert speichern eor r25,r24 ; Änderungen? and r25,r24 ; nur wenn Taste gedrückt out GPIOR1,r25 ; Änderungen Speichern lds ZL,ticksL lds ZH,ticksH adiw ZH:ZL,1 sts ticksH,ZH sts ticksL,ZL ; Encoder bearbeiten pop r25 ; alter Wert pop r24 ; neuer Wert andi r24,0b00000011 ; nur Encoderbits andi r25,0b00000011 lsl r25 lsl r25 or r24,r25 ; als Adresse zusammensetzen clr r25 ldi ZL, Low(steps<<1) ldi ZH,High(steps<<1) add ZL,r24 adc ZH,r25 ; Tabellenadresse lpm r25,Z ; Drehgeberwert in r24,GPIOR2 ; alte Impulszahl add r25,r24 ; + neue out GPIOR2,r25 ; speichern ser r24 sts ms_flag,r24 ; Flag setzen lds r24,cd_flag ; wenn cd_flag>0 kein tst r24 ; Count_down brne TIMER0_COMPA10 lds r25,count_downH ; Count Down lds r24,count_downL sbiw r25:r24,1 sts count_downH,r25 sts count_downL,r24 or r24,r25 brne TIMER0_COMPA10 lds ZL,cd_funcL ; wird bei abgelaufenen lds ZH,cd_funcH ; count down ausgeführt icall ldi r24,1 sts cd_flag,r24 TIMER0_COMPA10: pop ZH pop ZL pop r25 pop r24 out SREG,r24 pop r24 reti steps: .db 0 ,0 ,0 ,0 .db -1,0 ,0 ,1 .db 1 ,0 ,0,-1 .db 0 ,0 ,0 ,0 Ist etwas länger, weil noch einige andere Funktionen bearbeitet werden. Der Countdown stellt zum Beispiel einen veränderten Wert zurück, wenn innerhalb einer bestimmten Zeit der Taster des Encoders nicht gedrückt wird. Ist für einen ATMega1281. Als Speicher für alte Zustände dienen die GPIO-Register MfG Spess
Hi Stefan,
> Was willst du denn mit dem Drehgeber beeinflussen?
Ich will damit ein Menü im LCD steuern (= Zähler rauf oder
runterzählen). D.h. mit Rechts- und Linksdrehen durchs Menü scrollen und
mit dem Encoder-Taster den Menüpunkt aktivieren.
Axel
> > So wie Du angefangen hast, wird das nur Pfusch.
Dies bezog sich einzig und allein darauf, dass ich auf den ersten Blick
weder Interrupt-Vektoren noch eine Timer-ISR gesehen habe. Dies ist aber
nach der in Falks Wiki-Artikel beschriebenen Methode erforderlich. Auch
das darin enthaltene C-Programm von Peter Dannegger arbeitet mit
Timer-Interrupt. C kann ich nicht, Peters Programm wird aber auch nicht
viel anders arbeiten als die oben von mir vorgestellte ASM-Routine, die
übrigens auch nur eine ADM-Umsetzung dessen ist, was Falk hier in
irgendeinem Thread mal in Worten (als Algorithmus) formuliert hatte.
~
Hallo sinusgeek,
> ...dass ich auf den ersten Blick weder Interrupt-Vektoren noch eine Timer-ISR
gesehen habe.
Das ist in meinem Programm schon soweit alles pefekt vorhanden. Ich
wollte mich hier im Forum nur auf die Drehgeber-Auswertung beschränken
und nicht seitenweise Programm-Code in den Anhang stellen, der für mein
Problem nicht direkt relevant ist.
Also konkret scheitert es im Moment daran, den alten Zustand mit dem
neuen Zustand zu vergleichen, bzw wie/wann ich eine Zustandsänderung
abspeichere.
Axel
> Das ist in meinem Programm schon soweit alles pefekt vorhanden. Dann entschuldige bitte meine voreilige Äußerung. Beide Drehgeberspuren (in Hardware) möglichst auf benachbarte Bits legen. Vorteilhaft sind die Bits 0 und 1, oder auch 2 und 3 (dann muss man eben andersherum schieben). Andere Aufteilung geht auch, erfordert aber mehr Aufwand (Swap, lsl/lsr) > Also konkret scheitert es im Moment daran, den alten Zustand mit dem > neuen Zustand zu vergleichen, Der Vergleich erfolgt dadurch, dass die zwei neuen Bits und die zwei alten Bits zu einem Nibble zusammengebastelt werden, das dann als Index auf das Array mit Inkrement-Werten genutzt wird... Angenommen, die Spuren liegen an Bit 0 und 1. Dann müssen diese mit den (gemerkten) Bits der letzten Runde ergänzt werden, die dann in Position 2 und 3 müssen. Somit hast Du eine 4-Bit-Zahl, die aus zwei neuen und zwei alten Bits besteht. Dies ist der Index auf das Array (Tabelle) im Flash. Für jede Bitkombination, die keinen oder beide Pegelwechsel enthält, wird in der Tabelle eine 0 eingetragen, denn diese sind Stillstand (keine Flanke) oder verboten (beide Flanken). Für jede einzelne Flanke, die durch Rechtsdrehung entsteht wird eine 1 eingetragen. Für jede Flanke, die durch Linksdrehung entsteht, wird eine -1 (255) eingetragen. Da die Drehgeber unterschiedlich viele Flanken pro Rastung haben können, müssen bei einigen Typen einige Zählwerte zusätzlich auf 0 gesetzt werden. > bzw wie/wann ich eine Zustandsänderung > abspeichere. Du brauchst eine Speicherzelle, in der Du den alten Zustand sicherst. Spess nahm dazu ein GPIOR (hat der Mega8 nicht), ich nahm ein Register, da noch welche frei waren. Das Register ermöglicht auch das einfache Verschieben der Bits (LSL/LSR). Wenn ich den letzten Status in diesem Register gespeichert habe, kann ich den neuen Status in ein Tempregister einlesen, die irrelevanten Bits durch AND(I) ausblenden, den gemerkten alten Zustand (im anderen Register) um zwei Bits nach links verschieben, die neuen Bits aus dem Tempregister mit OR dazu kopieren (Bit 0 und 1 ist neuer Zustand, Bit 2 und 3 ist alter Zustand, Bit 4 und 5 ist uralt und wird gelöscht, Bit 6 und 7 wurde letztes mal schon gelöscht, als sie noch auf 4 und 5 waren) und mit AND(I) die unteren 4 Bits stehen lassen, also die oberen 4 Bits löschen. Die dabei entstehende Zahl ist 4 Bit breit (Werte 0..15) und enthält eine von 16 möglichen Konstellationen der Zustände alt-neu (sieha Absatz oben). Und nun wird einfach aus der LUT der Wert geholt, der dem Index entspricht. Der Index wird dabei nicht verändert und bleibt bis zur nächsten Runde gespeichert, in der er wieder um 2 Bits nach links geshiftet wird, wodurch Platz für die 2 neuen Bits geschaffen wird. Ich hoffe, dass diese (anders formulierte) Erklärung zum Verständnis beiträgt. ~
Hallo sinusgeek, vielen Dank für die super ausfühliche Erklärung. Ich denke damit komme ich jetzt erstmal weiter. Ich melde mich auf jedenfall nochmal, ob es geklappt hat. Hab ja noch ein paar Tage Urlaub ;-) Axel
Hallo, ich habe nun erstmal die Encoder-Auswertung nach dem Prinzip von Peter Dannegger hinbekommen. In der Timer-ISR habe ich folgenden Code: encoder: in neu, TAS_Pin ; Tastenport einlesen. andi neu, (1<<Enc1)|(1<<Enc2) ; Nur Encoder-Pins berücksichtigen. ldi temp1, 0b00000001 ; Wenn Bit1 gesetzt ist, Bit0 sbrc neu, 0b00000001 ; togglen. (=Gray-Code in Binär- eor neu, temp1 ; Code ändern). cp neu, last ; Wenn neu und last gleich sind, breq end_encoder ; keine Änderung -> zum Ende, ; ansonsten hier weiter... sub last, neu ; Die Differenz zwischen 2 aufein- ; ander folgenden Codes ermitteln. andi last, 0b00000010 ; Bit1 ausmaskieren und subi last, 1 ; 1 abziehen. Korrekte Ergebnisse: mov temp1, last ; 1 für Rechtsdrehen oder ; -1 (255) für Linksdrehen. andi last, 0b00000001 ; Vom Ergebnis Bit0 ausmaskieren. Wenn tst last ; Differenz 0 oder 2 ist (Bit0=0), breq end_encoder ; keine Änderung oder ungültig ; -> zum Ende springen. mov encdir, temp1 ; Drehrichtung speichern. mov last, neu ; Neuer Zustand ist nun letzter ; Zustand end_encoder: Das Ergebnis dieser Routine ist die Drehrichtung 1 oder -1 (255). Ich speichere das Ergenis im Register encdir ab, und werte es im Hauptprogramm aus (ähnlich wie Tastenentprellung). Das hat den Vorteil, dass der Code im Timer-Interrupt sehr kurz bleibt und ich im Hauptprogramm die Möglichkeit habe, verschiedene Zählsprünge einstellen zu können (z.B.: 1,2,3,... oder 10, 20, 30,... oder a, b, c, ...). Funktioniert toll. Ich kann das im LCD überprüfen, in dem ich eine Variable rauf/runter zählen lasse. Axel
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.