www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Zustandsautomat mit avr-gcc


Autor: Frank (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Wie realisiert man am besten einen Zustandsautomat mit dem GCC? Einfach 
über eine Zustandsvariablen und switch() Anweisungen oder gibts da 
bessere Wege, oder gar Compilerunterstützung?

MFG

Autor: ... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Variable+switch ist ok. Wenn du C++ verwendest und es bissl 
komplizierter magst, dann kannst du das auch über Objekte machen. (state 
design pattern)

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frank schrieb:

> Wie realisiert man am besten einen Zustandsautomat mit dem GCC? Einfach
> über eine Zustandsvariablen und switch() Anweisungen oder gibts da
> bessere Wege, oder gar Compilerunterstützung?

Es gibt Beschreibungssprachen für Zustandsautomaten (state machines) 
und Compiler aus diesen Sprachen nach C, C++,...
http://freshmeat.net/search?q=state+machine&submit...

Autor: ... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht noch ein kleiner Tipp:

Bevor du jetzt zum softwaretechnischen Rundumschlag ausholst, denk bitte 
daran:

KISS - keep it simple stupid

Das soll heißen, wenn du dein Problem einfach mit einer Zustandsvariable 
und einem switch lösen kannst, dann mach es auch so. Du solltest aber 
dann nicht die gesamte Funktionalität in der switch-Anweisung 
ausprogrammieren. Also ich halte sowas hier für sinnvoll:
switch (state)
{
case STATE_ABC: state_abc(); break;
case STATE_DEF: state_def(); break;
...
}

Ich hab schon Sourcecode gesehen, da hat einer das gesamte Programm in 
einen switch Block gepackt, der dann weit über 2000 Zeilen lang war. Da 
sieht niemand mehr durch. Er hatte dann auch keine Lust gehabt, neue 
Zustände einzuführen und hat dann die Funktion rekursiv mit 
irgendwelchen Nebenbedingungen aufgerufen.

Autor: Fralla (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Er hatte dann auch keine Lust gehabt, neue
>Zustände einzuführen und hat dann die Funktion rekursiv mit
>irgendwelchen Nebenbedingungen aufgerufen.

In diese Versuchung kam ich auch schon, erschien mir einfacher und 
übersichtlicher eben weil kein zusätzlicher state.

Also werd ich nach dem KISS arbeiten ariable+switch

Autor: Eddy Current (chrisi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
... schrieb:
>
> switch (state)
> {
> case STATE_ABC: state_abc(); break;
> case STATE_DEF: state_def(); break;
> ...
> }
> 

Naja, das ist doch für Anfänger ganz praktikabel. Aber als Steigerung 
kannst Du es gleich so machen:

// Zustandvariable ist ein Funktionszeiger
void (*state)(void);

// Zustand einstellen
state = state_abc;

// Zustand ausführen:
state();

Autor: Frank (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zeiger auf Funktion kenn ich on Callbacks, aber welchen Vorteil hat das 
hier? Man muss ja trotzdem den aktuellen State abfragen. Ob ich 
verschiednene Funktionen aufrufe, oder der state() Funktion eine 
Addresse zuweise und ausführe ändert ja nichts. Oder doch?

Autor: Skua (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Frank

Abfrage erübrigt sich bei Funktionszeigern, die Funktion ist mit dem 
Status identisch.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Skua schrieb:
> @Frank
>
> Abfrage erübrigt sich bei Funktionszeigern, die Funktion ist mit dem
> Status identisch.

Kannst du das konkretisieren, zB in C?

Ich wüsste jetzt nicht, wie ich einen Funktionszeiger-Typ definieren 
könnte für Funktionen, die Funktionen dieses Typs zurückliefern.

Konkret als Skelett mal folgender C-Code:
// <-- header start -->
typedef enum
{
    STATE0,
    STATE1,
    
    STATE_NUM
}  state_t;

typedef struct
{
   int wetter;
   int boerse;
} welt_t;

typedef state_t (*statehandler_t)(welt_t*);

extern state_t handler0 (welt_t*);
extern state_t handler1 (welt_t*);

// <-- header end -->
statehandler_t handler[] = 
{
    handler0,
    handler1
};

static welt_t welt = 
{
   .wetter = 42,
   .boese  = -1
};

void main_loop (void)
{
    state_t state = STATE0;
    
    while (1)
    {
        state = handler[state] (&welt);
    }
}

Hier muss doch wieder eine Status-Variable eingeführt werden. Schaffst 
du es ohne eine solche Variable? Wie sieht dann der Typedef für 
statehandler_t aus, ohne daß man Schweinereien wie Pointer-Casts braucht 
oder über globale Variablen (bäh) kommuniziert?

Du willst also sowas stattdessen:

extern state_t handler0 (welt*);
extern state_t handler1 (welt*);

void main_loop (void)
{
    state_t state = handler0;
    
    while (1)
    {
        state = state (&welt);
    }
}

Preisfrage:
  Wie sieht der Typedef für state_t aus?

Johann

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> ...ohne daß man Schweinereien wie Pointer-Casts braucht
> oder über globale Variablen (bäh) kommuniziert?

Dein Code enthält doch auch eine globale Variable.
Aber wie wär's damit (vielleicht noch mit geschickterer Namensgebung):
typedef void (*statehandler_t)(welt_t*);

typedef struct
{
   int wetter;
   int boerse;
   statehandler_t state;
} welt_t;

extern void handler0 (welt_t*);
extern void handler1 (welt_t*);

static welt_t welt = 
{
   .wetter = 42,
   .boese  = -1,
   .state = handler0
};

void main_loop (void)
{
    while (1)
    {
        welt.state(&welt);
    }
}

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
>> ...ohne daß man Schweinereien wie Pointer-Casts braucht
>> oder über globale Variablen (bäh) kommuniziert?
>
> Dein Code enthält doch auch eine globale Variable.
> Aber wie wär's damit (vielleicht noch mit geschickterer Namensgebung):
>
>
> typedef struct
> {
>    int wetter;
>    int boerse;
>    statehandler_t state;
> } welt_t;
> 
> void main_loop (void)
> {
>     while (1)
>     {
>         welt.state(&welt);
>     }
> }
> 

Den Status hatte ich ganz bewusst aus "welt" rausgehalten, weil er da 
konzeptionell nicht hingehört. Die Statemaschine operiert auf einem 
Objekt, sie ist nicht darin verwoben.

Mit folgendem Code kann man vielleicht sowas erreichen, aber wirklich 
verständlich ist das auch nicht gerade und irgendwie grenzwertig:
struct welt_t;

typedef struct state_t
{
    struct state_t * (*(*state) (struct welt_t*))(struct welt_t*);
} state_t;

extern state_t * (*slartibartfas (struct welt_t * welt))(struct welt_t*);

extern struct welt_t * get_welt (void);

void main_loop (void)
{
    struct welt_t * welt = get_welt();
    state_t * (*state)(struct welt_t*) = slartibartfas (welt);
    
    while (1)
        state = state (welt); // warning: assignment from incomparible pointer type
}

Leider gibt es in der gekennzeichneten Zeile noch eine Warnung. Das 
ganze ist eher ne Spielerei, um rauszufinden ob sowas geht in C. In 
Haskell oder so ist das bestimmt easy...

Der Code ist aber immerhin korrekt trotz der Warnung:
main_loop:
  push r28   ; 
  push r29   ; 
/* prologue: function */
/* frame size = 0 */
  call get_welt   ; 
  movw r28,r24   ;  welt,
  call slartibartfas   ; 
.L4:
  movw r30,r24   ;  state,
  movw r24,r28   ; , welt
  icall

Johann

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:

> typedef struct state_t
> {
>     struct state_t * (*(*state) (struct welt_t*))(struct welt_t*);
> } state_t;

Ui.
Das ist aber hart :-)

Ich habs auch noch nie geschafft. Ein typedef für einen 
Funktionspointer, der einen Funktionspointer auf denselben Typ 
zurückgibt, ist eines der grossen Mysterien in C.

PS: Ich würds gar nicht so kompliziert machen.
Einfach einen globalen Funktionspointer und jeder State hat das Recht 
einen anderen State dort einzutragen.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Beim Kopieren verloren gegangen:
  rjmp .L4   ; 

Johann

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Johann L. schrieb:
>
>> typedef struct state_t
>> {
>>     struct state_t * (*(*state) (struct welt_t*))(struct welt_t*);
>> } state_t;
>
> Ui.
> Das ist aber hart :-)
>
> Ich habs auch noch nie geschafft. Ein typedef für einen
> Funktionspointer, der einen Funktionspointer auf denselben Typ
> zurückgibt, ist eines der grossen Mysterien in C.
>
> PS: Ich würds gar nicht so kompliziert machen.
> Einfach einen globalen Funktionspointer und jeder State hat das Recht
> einen anderen State dort einzutragen.

Ist wie gesagt um die Grenzen auszuloten und zu testen, ob sowas 
überhaupt geht. In meinen Programmen würd ich sowas net hinschreiben.

Was noch stört ist die Struktur, aber wie man die wegbekommen kann, da 
hab ich echt keine Idee. Und in Standard-C (also nicht in GNU-C) gibt 
das bestimmt Fehler.

Man kann natürlich eine Funktion definieren, die als Rückgabewert (oder 
auch als Partameter) Funktionszeiger hat, aber die sind dann von einem 
anderen Typ.

Auch eine Implementierung von slartibartfas, die nichts am State ändert, 
liefert eine Warning.
state_t * (*slartibartfas (struct welt_t * welt))(struct welt_t*)
{
    (void) welt;
    return slartibartfas;
}

Im Endeffekt passieren an den Stellen, wo gewarnt wird, Pointer-Casts, 
die man ja nicht haben will (zumindest wenn man sauber programmiert).

Johann

Autor: geb (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja, wirklich beeindruckend wie man einen einfachen übersichtlichen 
switch so programmieren kann, dass man gar nichts mehr versteht. Ich 
hoffe, dass das beim philosophieren bleibt, sonst erschiene es mir etwas 
masochistisch.

l.g. gebhard

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Den Status hatte ich ganz bewusst aus "welt" rausgehalten, weil er
> da konzeptionell nicht hingehört.

Du wolltest ja keine separate globale Variable. Notfalls kann man auch 
eine Struktur nochmal außenrum machen, so daß welt ein Teil davon ist 
und der Status ein anderer.


> Die Statemaschine operiert auf einem Objekt, sie ist nicht darin
> verwoben

Das Objekt hat aber einen Zustand. Deshalb sehe ich kein Problem darin, 
diesen im Objekt zu speichern. Das hängt aber davon ab, ob die 
Statemachine das Objekt zur Laufzeit wechseln soll und dabei aber ihren 
Zustand beibehalten muß. In der Regel würde ich davon ausgehen, daß das 
nicht der Fall ist.
Mir gefällt es irgendwie nicht, daß deine Zustandsfunktionen jeweils 
einen neuen Zustand als Returnwert zurückgeben. Da finde ich das 
Speichern im Objekt schöner.

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.