www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik State machine in Assembler


Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oops, "weis" natürlich mit "ß". Wie gesagt, Bildung in Deutschland ... 
:D

Autor: 2921 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie ich vermutet habe. Die Drehrichtung darf nur beim Signal A bestimmt 
werden. Danke.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht 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.

...

Autor: Knut Ballhause (Firma: TravelRec.) (travelrec) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Aber ich kann keinerlei Lösung in AVR-ASM entdecken.

Wird wohl Zeit, da mal etwas aufzuarbeiten. Ist sowieso alles zu 
C-verseucht hier ;-) ;-) ;-)

Autor: Knut Ballhause (Firma: TravelRec.) (travelrec) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ 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

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht 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...

...

Autor: Knut Ballhause (Firma: TravelRec.) (travelrec) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie auch immer: Lösungsansatz vor 3 Posts, siehe oben. Ich bin nicht so 
anpassungsfähig, wie Du ;-)

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Christoph Kessler (db1uq) (christoph_kessler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Beitrag "Drehgeber auslesen"
unter anderem eine AVR-Assemblerlösung von mir, allerdings mit 
Interrupt.

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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 ...

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ...

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: T.S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hannes

> Für C bin ich nicht zuständig.

Ich habe Dich aber mit dem Kernighan/Ritchie heute erwischt. ;)

Torsten

Autor: Tim S. (lumpi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht 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... ;-)

...

Autor: Tim S. (lumpi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Hannes für deine Worte :-)
So jetzt haben wir das Ding..haha

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Maxim (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Maxim (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Mist, falsche Datei angehängt, hier die richtige.

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht 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.

...

Autor: Maxim (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Knut Ballhause (Firma: TravelRec.) (travelrec) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, sie kann Kontaktprellen detektieren.

Autor: Maxim (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.