Mich wundert's, dass ich auf einem ITG nichts über einen endlichen Automaten gehört habe. Bildung in Deutschland, na was soll's ... Benutze AVR und Assembler. Möchte einen Drehimpulsgeber auswerten. Habe natürlich bereits per Suche und in der Artikelsammlung recherchiert. Nun weis ich aber nicht, wie man eine state machine programmiert. Kann mir jemand ein kleines Beispiel geben oder zumindest erklären, wie ich das in Assembler umsetze?
Oops, "weis" natürlich mit "ß". Wie gesagt, Bildung in Deutschland ... :D
Das ist kein Witz ? Etwas trivialeres wie eine Zustandsmaschine gibt es kaum. Manbenoetigt eine einzelne Variable, die den Zustand haelt. Dann im main mach man eine Verzweigung ( CASE / Switch ) nach der Variablen und fuehrt den passenden Code aus.
Die Statemachine besteht im wesentlichen aus einer Variablen, die die Nummer des aktuellen Zustandes enthält, einem Adressvektor, in dem die Startadressen der Codestücke stehen, die zum Zustand n und den Eingabevariablen den Folgezustand und evtl. die Ausgabe bestimmen. Der Startpunkt ist ein indizierter indirekter Sprung: SM_Start: jmp addr[n] Der Code für einen Zustand endet immer mit einem Sprung an eine gemeinsame Endadresse, oder evtl. direkt wieder auf den Start.
Wenn ich also bspw. vier mögliche Zustände habe, wobei von jedem Zustand aus nur in zwei andere gewechselt werden kann, brauche ich vier Tabellen mit je drei Möglichkeiten: T1 --------- nach D bleib in A nach B T2 --------- nach A bleib in B nach C T3 --------- nach B bleib in C nach D T4 --------- nach C bleib in D nach A Habe ich das richtig verstanden?
Man kann so ein Teil tabellengetrieben konstruieren, aber ich denke, daß das eher einfache und vor allem sehr gleichförmige Ausnahmefälle sind. Meistens wird man jeden Zustand durch ein Stück Code repräsentieren, das angesprungen wird, wenn die Maschine im betreffenden Zustand ist. Das macht die Konstruktion wesentlich mächtiger. Vielleicht solltest du dir erst mal überlegen, wie sowas in einer Sprache, wie C o.ä. aussieht. Wenn du dann klarheit über die Funktionsweise hast, überlegst du dir, wie man das in ASM schreibt. Weitere Informationen: z.B. http://de.wikipedia.org/wiki/Virtueller_endlicher_Automat
Ja klar, mit switch oder if u.s.w stelle ich einfach Bedingungen auf, wann welcher Zustand eintreten soll. Aber ich habe halt schon mitbekommen, dass man durch Arrays sehr vielfältige Operationen mit irrsinnig vielen Verzweigungen überschaubar in Code umsetzt, der vor allem schnell abgearbeitet werden kann. Ok, für meine Anwendung muss es aber nicht so kompliziert sein. Hier ist ein Diagram aus dem Datenblatt des Drehgebers: http://www.cipoint.homepage.t-online.de/phase.gif Der ist von Panasonic, bei Pollin gekauft. Komisch ist, dass ein Einrastpunkt immer an der Flanke vom Signal B ist. Bei einem anderen DIG waren die Signale bei den Einrastpunkten klar definiert. Außerdem durchläuft bei diesem DIG die Drehung von einem Einrastpunkt zum nächsten nur die halbe Periode. Das verwirrt mich etwas: Wenn sich der DIG im zweiten Einrastpunkt im Bild befindet und ich ihn im UZS drehe, wird zuerst A low(im Bild ON). Dann erreicht der DIG den dritten Einrastpunkt im Bild. Aber da der Zustand nicht definiert ist, kann es ja vorkommen, dass Signal B gar nicht low (ON) wird. Dann habe ich ein Problem, oder?
Wie ich vermutet habe. Die Drehrichtung darf nur beim Signal A bestimmt werden. Danke.
Der Thread-Beginner sucht nach einer Lösung, einen Drehencoder von Pollin in AVR-Assembler abzufragen bzw. auszuwerten. Dieser Encoder hat folgende Eigenschaften: - billig (Restposten, evtl. fehlerhafte Produktion?) - mechanische Schleifkontakte - rastet zweimal pro Vollzyklus - ist enweder etwas asymmetrisch oder auf einer Spur etwas unzuverlässig Er will also einen rastenden Drehknopf als Eingabegerät zum Dialog zwischen Mensch und Maschine nutzen, nicht mehr und nicht weniger... Falk Brunner wrote: > Drehgeber Was willst Du uns damit sagen?? Da werden (zu recht!) alle Varianten angeprangert, die externe Interrupts verwenden. Da wird auch (zu recht) darauf hingewiesen, dass die Abfrage zyklisch (in gleichen Abständen, also mit fester Frequenz) erfolgen soll. Da wird auch auf einige C-Beispiele verlinkt und ein VHDL-Beispiel gezeigt. Aber ich kann keinerlei Lösung in AVR-ASM entdecken. > AVR-Tutorial: Mehrfachverzweigung Das ist zwar korrekt und sollte zum Grundwissen eines AVR-ASM-Programmierers gehören, löst aber die konkrete Frage nicht, wie man mit der Drehgeberbewegung eine Variable mitlaufen lässt. ...
> Aber ich kann keinerlei Lösung in AVR-ASM entdecken.
Wird wohl Zeit, da mal etwas aufzuarbeiten. Ist sowieso alles zu
C-verseucht hier ;-) ;-) ;-)
Hier mal ein unbereinigtes Codesegment, welches in einer Timer ISR-läuft, die jede Millisekunde ausgelöst wird. Das Flag "RotaryFlag" speichert den letzten Pinzustand des A-Terminals vom Drehencoder, "NewRot" ist das Flag, welches gesetzt wird, wenn ein neuer Drehzustand detektiert wurde, "RotaryDir" signalisiert die Drehrichtung. Unten werden noch Buttons abgefragt und entprellt, einer davon ist der des Drehencoders. Der Drehencoder selbst ist mit 33nF an jedem Terminal nach Masse hardware-entprellt.
@ Hannes Lux (hannes) >erfolgen soll. Da wird auch auf einige C-Beispiele verlinkt und ein >VHDL-Beispiel gezeigt. Aber ich kann keinerlei Lösung in AVR-ASM >entdecken. Na dann mach mal ne AVR-ASM Lösung. Muss ja schliesslich nur die C-Version umgestrickt werden. MFg Falk
Travel Rec. wrote: >> Aber ich kann keinerlei Lösung in AVR-ASM entdecken. > > Wird wohl Zeit, da mal etwas aufzuarbeiten. Für mich nicht... Ich komme mit meiner Lösung ganz gut zurecht und bin auch in der Lage, sie an sich ändernde Gegebenheiten anzupassen. > Ist sowieso alles zu > C-verseucht hier ;-) ;-) ;-) Ich habe nichts gegen C. Aber Einige tun so, als ob man AVRs auch ohne ASM-Kenntnisse in C programmieren könnte. C-Programmierer können (auf kleinen Mikrocontrollern) nur dann effizienten Code schreiben, wenn sie auch ASM verstehen. Das wird vom Anfänger oft und gern übersehen... ...
Wie auch immer: Lösungsansatz vor 3 Posts, siehe oben. Ich bin nicht so anpassungsfähig, wie Du ;-)
Falk Brunner wrote: > @ Hannes Lux (hannes) > >>erfolgen soll. Da wird auch auf einige C-Beispiele verlinkt und ein >>VHDL-Beispiel gezeigt. Aber ich kann keinerlei Lösung in AVR-ASM >>entdecken. > > Na dann mach mal ne AVR-ASM Lösung. Habe ich doch schon, ist oben auch verlinkt. Sie ist aber speziell auf den labrigen Pollin-Encoder zugeschnitten, funktioniert damit auch, passt aufgrund dieses speziellen Encoders aber vermutlich nicht in das allgemeingültige Konzept Deines Wiki-Artikels. Sie erfüllt aber folgende Kriterien: - kein externer Interrupt, der den AVR zumüllen kann - zyklische Abfrage, vom Timer-Interrupt synchronisiert - Reaktion auf steigende und fallende Flanke des "besseren" Schalters - Vermeidung der Flankenprüfung des "labrigen" Schalters - Entprellung (Übernahme erst nachdem der neue Zustand bestätigt ist) - funktioniert mit diesen Zweitewahl-Teilen > Muss ja schliesslich nur die > C-Version umgestrickt werden. Für C bin ich nicht zuständig. > > MFg > Falk
Beitrag "Drehgeber auslesen" unter anderem eine AVR-Assemblerlösung von mir, allerdings mit Interrupt.
Ich werde das Rad wohl neu erfinden. So lernt man am meisten. Aber trotzdem Danke für Lösungsvorschläge. Nachdem ich mir das Diagramm aus dem Datenblatt genauer angeschaut habe, bin ich zu folgender Feststellung gekommen: Im UZS, wenn: nach (B=1 UND A=1) folgt (B=1 UND A=0) oder nach (B=0 UND A=0) folgt (B=0 UND A=1) Gegen UZS, wenn: nach (B=1 UND A=0) folgt (B=1 UND A=1) oder nach (B=0 UND A=1) folgt (B=0 UND A=0) Nun lege ich mir also eine LUT an. Das Programm springt zuerst an die Adresse, die dem alten Zustand entspricht und von dort aus - je nach neuem Zustand - zu der Zieladresse. Dort befindet sich ein Sprungbefehl, entweder nach "CW" oder nach "CCW". Wenn alter Zustand gleich dem neuen ist, wird erst gar nicht in der Tabelle gesucht. Das sollte funktionieren ... Nun muss ich die Eingabe noch entprellen. Das möchte ich über eine UND-Verknüpfung realisieren. Es sollen die letzten drei bis vier Eingabe vergliechen werden. Aber da weiß ich noch nicht ganz genau, wie ich das Umsetze. Vielleicht so, wie hier: Beitrag "Re: Drehimpulsgeber (wiedereinmal.)" Zur Erinnerung: Ich kenne mich zwar auch in C aus, programmiere das hier aber in Assembler.
@ Maxim (Gast) >Nachdem ich mir das Diagramm aus dem Datenblatt genauer angeschaut habe, >bin ich zu folgender Feststellung gekommen: Die Codes 00 01 11 10 werden zyklisch durchlaufen, entweder nach oben oder unten (mit Über/Unterlauf). Gray-Code eben. >Nun lege ich mir also eine LUT an. Das Programm springt zuerst an die >Adresse, die dem alten Zustand entspricht und von dort aus - je nach >neuem Zustand - zu der Zieladresse. Dort befindet sich ein Sprungbefehl, >entweder nach "CW" oder nach "CCW". Wenn alter Zustand gleich dem neuen >ist, wird erst gar nicht in der Tabelle gesucht. Das sollte >funktionieren ... Naja, geht auch. Aber da du ja von C-Ahnung hast, solltest du dir vielleicht lieber die Version der D.S.E FAQ anschauen und in ASM umstetzen. Das ist nur eine kleine Datentabelle mit 16 Einträgen. MFG Falk
Oh, ich habe die "unmöglichen" Kombinationen gar nicht beachtet. Falls sie doch auftreten, muss ich sie abfangen. Also doch lieber ALLE Zustände beachten. Dann sind es genau 16 ...
Also hier schonmal der PAP für die Entprellung. Habe auf UND-Verknüpfung verzichtet. Diese Variante kommt mir etwas sicherer vor. Es wird insgesamt achtmal der Zustand ausgelesen. Dann werden die ersten vier Werte mit den letzten vier durch ein exklusives ODER verknüpft. Ist das Ergebnis 0, so waren die ersten vier Werte exakt die letzen vier. Dann wird einer der Werte übernommen und in das Status-Register RPE_ST geschrieben. Jetzt muss natürlich noch vergliechen werden, ob die neuen Werte im Bit0/1 den alten im Bit2/3 gleichen. Falls nicht, hat sich der Drehgeber bewegt. Dann muss anhand einer LUT entschieden werden, in welche Richtung der das tat. Sind die Bits 0/1 gleich 2/3, hat sich nichts getan ...
Hannes
> Für C bin ich nicht zuständig.
Ich habe Dich aber mit dem Kernighan/Ritchie heute erwischt. ;)
Torsten
T.S. wrote: > Hannes > >> Für C bin ich nicht zuständig. > > Ich habe Dich aber mit dem Kernighan/Ritchie heute erwischt. ;) > > Torsten Da benutzt doch einer einfach mein Kürzel...tztztztz
T. S. wrote: > T.S. wrote: >> Hannes >> >>> Für C bin ich nicht zuständig. >> >> Ich habe Dich aber mit dem Kernighan/Ritchie heute erwischt. ;) Olle Petze... ;-) >> >> Torsten Beste Grüße, Hannes > > > Da benutzt doch einer einfach mein Kürzel...tztztztz Umgekehrt wird ein Schuh draus, Du benutzt Torstens Kürzel, mit dem er schon seit Jahren hier im Forum (und auch Anderswo) unterwegs ist. Ehe man sich mit einem Namen registriert, schaut man erstmal, ob der nicht bereits vergeben ist... ;-) ...
Tim S. wrote: > Danke Hannes für deine Worte :-) Danke, dass Du sie nicht falsch verstanden hast... ;-) > So jetzt haben wir das Ding..haha Prima, ist für alle Beteiligten eine gute Lösung... Bit- & Bytebruch, Hannes
Zurück zur Sache. Habe ein kleines Problem mit dem Befehl IJMP. Der Debugger meldet "AVR Simulator: Invalid opcode 0xffff at address 0x00007e". Weiß jemand, was es zu bedeuten hat? Der Befehl befindet sich in der Routine INTR_RPE, fast ganz unten. Quellcode im Anhang.
Also ich habe rausgefunden, dass ich hier: LDI ZL, LOW(RPE_TABLE*2) die Adresse der Tabelle nicht mir Zwei multiplizieren darf. Dann läuft das Programm. Aber in anderen Programmen muss ich das machen. Woran liegt das?
Maxim wrote: > Also ich habe rausgefunden, dass ich hier: > LDI ZL, LOW(RPE_TABLE*2) > die Adresse der Tabelle nicht mir Zwei multiplizieren darf. Dann läuft > das Programm. > > Aber in anderen Programmen muss ich das machen. Woran liegt das? Nur der Befehl LPM arbeitet byteweise im wordorientierten Flash (siehe Befehlsbeschreibung LPM) und braucht daher das Doppelte der realen Adresse. IJMP, ICALL greift wordorientiert auf den wordadressierten Flasch zu und braucht die Adresse korrekt. ...
>Nur der Befehl LPM arbeitet byteweise im wordorientierten Flash (siehe >Befehlsbeschreibung LPM) und braucht daher das Doppelte der realen >Adresse. IJMP, ICALL greift wordorientiert auf den wordadressierten >Flasch zu und braucht die Adresse korrekt. Das wußte ich nicht, aber jetzt erklärt sich der Rest von selbst. Mein Programm ist fertig. Im Debugger funktioniert es auch. Aber in der Realität will es noch nicht. Zur Kontrolle schaltet eine Drehung im UZS Pin PB0 an und eine gegen den UZS schaltet ihn wieder aus. In der Simulation klappt es. Das sind die zwei Routinen: DEB_RPE: Entprellung der Signale http://www.cipoint.homepage.t-online.de/DEB_RPE.jpg INTR_RPE: Interpretierung der Drehrichtung http://www.cipoint.homepage.t-online.de/INTR_RPE.jpg
Nachdem ich die Abtastrate verringert habe, indem die Routine nun nicht in der Hauptschleife, sondern durch einen Timer aufgerufen wird, funktioniert mein Programm. Interessant wäre zu wissen, warum es in der Hauptschleife nicht ging? Im Grunde war da nur die Abtastrate um einiges höher. Kann eine zu hohe Abtastrate kontraproduktiv sein?
Das Programm funktioniert perfekt ohne Prescaler. Es gab keinen einzigen Aussetzer. Dürfte aber etwas an der Ressourcen knabbern. Dann werde ich wohl einen kleinen ATtiny dafür anheuern.
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.