Forum: Mikrocontroller und Digitale Elektronik Zustandsautomat "verschluckt Zustände"/läuft nicht


von Hans Maier (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

habe vor kurzem mit AVRs angefangen und würde nun gerne eine
Fernsteuerung für ein (Gargagen-)Tor bauen. Die Hardware habe ich
soweit fertig und die läuft meiner Meinung nach auch. Die vorhandene
Handsteuerung habe ich "entdrahtet", die Leitungen zum Motor dann auf
Schütze/Relais gelegt und die ursprünglichen Taster der Handsteuerung
auf Eingänge des uC (AVR ATMega8). Außerdem habe ich noch eine alte
1-Kanal Fernbedienung angeschlossen. Was ich nun wollte, war ein
Zustandsautomat in den uC zu laden. Wenn jemand eine Taste (auf, stop,
zu) drückt hat das Vorrang vor allem anderen. Wenn sich der Zustand
(schaltet bei jedem druck zwischen high und low um) des
Fernbedienungsempfänger ändert, soll das Tor auf -> stop -> zu -> stop
-> auf... gehen. Das Tor hat elektromechanische Endschalter und stoppt
von alleine, es genügt also einfach ein Schütz/Relais für eine gewisse
Zeit anziehen zu lassen.
Irgendwie hängt sich das ganze jedoch oft komplett auf: teilweise
werden Zustände verschluckt bzw. gar nicht durchlaufen. Schlimmer noch:
es passiert sogar, dass der uC in einem Zustand "hängt" und noch nicht
einmal auf einen Tastendruck reagiert!
könnte sich evtl. mal jemand den angehängten Code ansehen (habe mich
bemüht überall Kommentare einzufügen) und mir Tipps geben wo es hängt?
Ach ja, programmiere in C und habe mir erst einmal die 45-Tage
Testversion von Imagecraft installiert.

Schönes Wochenende und weiterhin viele Tore :=)

Hans

von Unbekannter (Gast)


Lesenswert?

Oh jee... Dein Code ist voll mit Race-Condtitions.

Als erstes must Du alle Variablen, die von einem Interrupt und dem
Hauptprogramm verändert werden, als "volatile" deklarieren. Also:

  volatile int count = 0;
  volatile unsigned char zustand = 1;
  volatile unsigned char alterfunkzustand = 1;

Dann hast Du im Hauptprogramm noch oft Konstruktionen wie:

  if ( zustand == xyz )
  {
    // mach was
  }
  else if (zustand == abc)
  {
    // mach was anderes
  }

Überlegt Dir mal, was passiert wenn der Timer-Interrupt auftritt und
genau zwischen die beiden if-Anweisung fällt. Dann kann (je nach Code)
Deine State-Maschine nämlich kräftig aus dem Tritt kommen.

Du musst cli() und sei() benutzen um Interrupts an der entsprechende
Stelle zu sperren um saubere State-Maschine-Übergänge zu erhalten.

Ich habe Deinen Code nur oberflächlich angeschaut. Ob weitere Fehler
drinn sind, keine Ahnung, aber ich vermute es.

von Hans Maier (Gast)


Lesenswert?

Hallo,

danke für die Rückmeldung. Deine Einwände leuchten mir soweit ein.
Wenn ich die Interrupts im main komplett vor der Abfrage der Tasten
sperre und erst am Ende des main wieder freigebe, würde das gehen? Also
merkt sich der uC wenn ein Interrupt aufgetreten ist und arbeitet ihn
halt nur ab wenn sie freigegeben sind? Es würde ja schon genügen, wenn
in meinem Programm nur an einer einzigen Stelle im main mal die
Interrupts abgefragt würden, denn der Mensch drückt wohl länger als ein
Durchlauf des mains auf eine Taste.
Ansonsten: wie baut man einen Zustandsautomaten der auch zeitgesteuert
den Zustand wechselt "richtig" auf?

Hans

von Hans Maier (Gast)


Lesenswert?

P.S. Habe mir
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmieren_mit_Interrupts
durchgelsen und verstehe jetzt die Problematik. Bei einem "einfachem"
Zustandautomaten wäre mir die Lösung soweit auch klar, nur nicht wenn
das ganze auch zeitgesteuert laufen soll.

Hans

von Peter D. (peda)


Lesenswert?

Versuche mal, das Ganze als switch() Anweisung umzuschreiben, die ist
eigentlich genau für Zustandsautomaten gedacht.

Dann wird alles viel übersichtlicher.

Und den default-Zweig nicht vergessen !

Nicht davon ausgehen, daß bestimmte Zustände nie auftreten können.


Peter

von Hans Maier (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

anbei die nächste Version mit switch Konstrukten und abschalten der
Interrupts während der Abarbeitung. Werde das ganze nachher mal
ausprobieren. Muss jetzt erst im Garten helfen :=(

Auf jeden Fall: Danke!

Hans

von Dirk (Gast)


Lesenswert?

Hallo,

>Du musst cli() und sei() benutzen um Interrupts an der entsprechende
>Stelle zu sperren um saubere State-Maschine-Übergänge zu erhalten.

Es ist nicht zwingend notwendig, wenn man weiss wann seine Interrupts
ausgeloest werden.

Bei mir bekommt die State Maschine einen eigenen Takt der per Interrupt
erzeugt wird. Der gleiche Timer wird auch dafuer benutzt weitere
Taktquellen fuer andere Routinen zuerzeugen.

Gruß,
Dirk

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
Noch kein Account? Hier anmelden.