State machine in Assembler

Gast #688371
Lesenswert?

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?
Gast #688375
Lesenswert?

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.
#688384
Lesenswert?

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.
Gast #688405
Lesenswert?

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?
#688417
Lesenswert?

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
Gast #688505
Lesenswert?

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?
#688687
Lesenswert?

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.

...
(Firma: TravelRec.) Persönliche Seite #688700
Lesenswert?

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.
Angehängte Dateien:
#688704
Lesenswert?

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...

...
#688730
Lesenswert?

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
Gast #688839
Lesenswert?

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.
#688881
Lesenswert?

@ 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
Gast #688919
Lesenswert?

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 ...
#689384
Lesenswert?

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... ;-)

...
Gast #689422
Lesenswert?

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.
Angehängte Dateien:
Gast #689437
Lesenswert?

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?
#689453
Lesenswert?

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.

...
Gast #689892
Lesenswert?

>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
Angehängte Dateien:
Gast #690210
Lesenswert?

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?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren