hallo an alle! heute mal folgendes: Wie gestatltet man eine effiziente Struktur für ein Programm, daß folgende Eckdaten hat: -auswerten von Tasten-Eingaben (geschieht über peter dannegers routine im ISR) -Aufrufen verschiedener Betriebsmodi enstsprechend der betätigten Tasten -betreiben einer Echtzeituhr (wird zZ. bei neuer Sekunde aus der ISR aufgerufen) -Ausgabe der Uhrzeit,des aktuellen Betriebsmodus und der aktuellen Tastenbelegung auf LCD -je nach Betriebsmodus haben die Tasten unterschiedliche Funktionen und es werden verschiedene Aktionen durchgeführt (einfachstes Beispiel: stellen der Uhr, Ändern von Aktionsparametern, Ausgabe von Daten und Bedienen von Relais etc. über Ports....) Mein Problem dabei ist, daß ich nicht organisiert bekomme, welche Jobs in der Mainloop zu erledigen sind bzw. wie ich sie in entsprechend Unterprogramme verfrachte, ohne dabei den "normalen" Ablauf der kontinuierlich ablaufenden Arbeiten (zB. LCD-refresh, Uhr weiterlaufen lassen) zu stören. Daher habe ich bereits den Uhr-job über die ISR gesichert, da ja die Uhr auch weiterlaufen muß, wenn das Programm gerade was anderes tut. Wie strukturiert man sowas geschickt? Ist die Mainloop dann nur dazu da, nichts zu tun und auf einen Unterprogrammaufruf zu warten? Wie bekomme ich den zu schreibenden Inhalt des LCD "zusammengebaut" und geschrieben? In jedem UP einzeln? Oder sollte man direkt eine StateMaschine bauen, die sich in jedem Betriebszustand um ihre aktuell Aufgabe kümmert (und wie organisiert man dann die ständig anliegenden Jobs)? Ist es mir gelungen, mich verständlich auszudrücken?
Also ich programmiere noch lange nicht effizient, aber... Wenn ich mehrere ISRs habe, dann halte ich sie möglichst kurz und setze (selbstdefinierte) Flags für die Jobs, die die Mainloop dann ausführt (und das Flag wieder löscht). Habe ich nur eine ISR (auch das gibt's), dann wird meist alles in der ISR erledigt, in der Mainloop wird dann nur noch gerechnet (falls erforderlich) und geschlafen. Ab wann die Programmsteuerung mittels Flags (eigene, nicht die des SREG) und anderer Zustandsvariablen (z.B. Menüpunktnummer) als State-Machine gilt, ist mir allerdings (noch) unklar. LCD-Ausgaben mache ich inzwischen timersynchronisiert über einen Ringbuffer. Dabei schreiben die betreffenden Print-Routinen (die die ASCII-Zeichenfolgen erzeugen) und die Locate-Routine nur in den Ringbuffer. Ein Job der Mainloop (mit geringer Priorität) schaufelt dann die Bytes des Ringbuffers einzeln an das LCD, wenn der Timer-Interrupt das LCD-Flag wieder gesetzt hat. Die Prioritäten der Mainloop erreiche ich, indem ich die Jobroutinen nicht als Unterprogramm (RCALL/RET) aufrufe, sondern mit RJMP und RJMP mainloop. Manchmal muss es aber RCALL/RET sein, es kommt auf die Situation an, jedes Programm ist anders. Übrigens glaube ich nicht, dass man mit einigen wenigen Sätzen ein allgemeingültiges Regelwerk der effizienten Programmierung erstellen kann. Das Thema ist so komplex, dass es mehrere (viele?) Bücher füllt. Auch meine ich, dass zum effizienten Programmieren langjährige Erfahrung gehört, man also auf diesem Weg einen großen Teil seiner Fehler selbst machen muss. Auch die hier vertretenen Profis haben einmal angefangen und sich ihr Wissen in mühevoller Kleinarbeit nach und nach angeeignet. ...
Ha, genau das 'Problem' habe ich auch gerade! Ein eigentlich relativ lineares Programm, die zeitkritischen Aufgaben (Tasten,Schrittmotor, Buzzer) werden vom durchlaufenden Timer gelöst. Zudem zählt ein TOI einen 'Softwaretimer' hoch, dessen Überlauf bei einer Sekunde liegt. Ich habe sogar noch einen HW-Timer frei. Wie ich das eigentliche Programm übersichtlich und effizient strukturiere (kein blockieren, kein redundanter Code), leuchtet mir aber noch nicht ein. An, initialisieren: Auf Tastendruck warten (nächster Programmschritt), oder Befehl von serieller Schnittstelle ausführen (nächster Programmschritt, oder was völlig anderes) ... Auf Tastendruck warten, oder Befehl von serieller Schnittstelle ausführen. ... Nach der Initialisierung dann ggf. zyklische Abarbeitung des eigentlichen Programms. Ich könnte mir noch vorstellen, verschiedene Runlevels in eine Variable zu schreiben, anhand derer die Tasten (wiederum eine entsprechende Aktion auslösen... oder eben die Aktionstasks Runlevels haben, die sie dann anhand von 'Ereignissen' hochzählen. Also fürchte ich, daß man nicht um einen Haufen Flags herumkommt..sehe ich das richtig? Vielleicht kann jemand mich auf ein Programm verweisen, daß so eine 'interaktive' Struktur beispielhaft vorführt? Ich werde wohl mal in der Codesammlung stöbern..
@du bist: Also so richtig verstanden habe ich dein Anliegen nicht... Aber: Auf Tastendrücke "wartet" man nicht (jedenfalls nicht ohne Not), die liest man einfach nebenbei im Vorbeigehen ein und entprellt sie. Sind neue "Ereignisse" (z.B. Tastendruck) da, dann reagiert man darauf. Sind keine Ereignisse mehr da (alle Jobs abgearbeitet), wird gepennt. Der nächste Int weckt dann den MC, führt die ISR aus und schaut nach, ob neue "Ereignisse" eingetroffen sind. Klar, ein Programm beginnt mit der Init-Routine, die (meist) nur ein einziges mal (beim Reset) durchlaufen wird. In ihr werden die genutzten Komponenten initialisiert, auch die entsprechenden Interrupts. Dann kommt meist die Mainloop, in der Jobflags (auch Tastenflags der entprellten Tasten) überprüft werden und darauf reagiert wird (in Routine verzweigen, die das Jobflag löscht und den Job ausführt). Sind alle Jobs erledigt, dann wird gepennt. Dann gibt es diverse Interrupts, die auf Ereignisse reagieren und die entsprechenden ISRs aufrufen. Hier kommt es auf die Situation an, ob in der ISR nur ein Jobflag für die Mainloop gesetzt wird oder ob der Job gleich in der ISR erledigt wird. Auch die Frage der Redundanz kann von Fall zu Fall unterschiedlich ausfallen. Manchmal ist es eben schneller, einen kurzen Programmteil mehrmals an verschiedenen Stellen zu haben, als ihn jedesmal neu aufzurufen. Es richtet sich immer danach, was knapper ist, Rechenzeit oder Speicherplatz. Manchmal ist es sogar effizienter, Dinge, die man üblicherweise in einer Schleife erledigt, mehrfach hintereinander zu schreiben um sich den Sprung zu sparen. Das kostet etwas Speicherplatz, kann aber in zeitkritischen Phasen Rechenzeit sparen. ...
Hallo Hannes, danke für Deine Antwort. Das Strukturierungsproblem schlägt sich also auch in meiner Formulierung nieder :-) Das Problem besteht darin, daß mir keine eindeutig 'gute' Methode einfällt, mit der die Jobs/State-Machines/Tasks untereinander kommunizieren können (ohne blockierend auf etwas zu warten). Bei periodischen Aufgaben, oder solchen die nicht-linear sind, ist der Einsatz von Flags oder Zähl-Bytes recht überschaubar (Pieper an für x, LED-machine blinkt n mal, Motor 100 Schritte). Sobald es aber darum geht, z.B. eine Initialisierung (Motor manuell/per RS232 auf Position bringen, Werte in EEProm ablegen) zu strukturieren, wird es komplexer. Zerpflücke ich die Aufgabe in mehrere 'Tasks', auch wenn sich die Funktionalität etwas überschneidet (task_initlevel1->..evel2->..)? Wo wird der (entprellte) Tastenzustand abgefragt - in einer 'Task Taste', oder in der vom Programmablauf her wichtigeren 'task_initlevelx' ? Es scheint mir gerade, als wäre eine einzelne task_init mit verschiedenen runlevel doch nicht übermäßig kompliziert - mal sehen. Ich habe allerdings noch kein exemplarisches, komplettes C Programm aus der Codesammlung gefunden, das in etwa meiner Anwendung nahekommt.
Das kommt immer auf das Problem an. mal ein beispiel: eine steuerung für eine Dreh-Maschine. An der Sind ein Paar Taster dran, dann gibt es da noch einen Not-Aus und einen Motor (der natürlich erst anlaufen muss) und ein paar Lampen. So um das Ding in gang zu bringen gibt es eine Start-Taste und zum Anhalten eine Stop-Taste. Wenn das Ding anläuft, dann soll die Betriebs-Lampe langsam blinken. Wenn ein Fehler vorliegt, dann soll die Lampe schnell blinken und im Betrieb einfach nur leuchten. Also baut man 3 State-Maschines: Eine für die Tasten, eine für die Lampen und eine für den Motor. Dann kommt ein Timer zum Einsatz, der alle 20ms (oder anderer Wert) die 3 FSMs der reihe nach bedient: Tasten einlesen (dabei werden die Tasten gleich mit entprellt), die Lampen ansteuern (dann hat die Lampe halt 4 Betriebsmodi - Aus, Ein, Schnell, Langsam) und dann noch den Motor ansteuern (Stop, Anfahren, Betrieb, Anhalten) In den FSMs gibt es Zähler, die den Verlauf timen (d.h. zwischen den Ein/Ausschalten der Lampe gibt es z.B. mal 50 oder 200 Durchläufe Zeit) Wenn der Zähler auf 0 steht, dann wird halt eine Aktion ausgelöst (z.B. wechsel von Anfahren zu Betrieb) Und in der Hauptschleife kann dann schön geschlafen werden. Der Not-Aus ist auch recht schnell eingebaut: In der Hauptschleife wird auf Interrupts gewartet - also bekommt das Not-Aus eine ISR verpasst, die die FSM entsprechend schnell (und rabiat) auf die neuen Werte setzt.
Hier werden einige Begriffe benutzt, die mir (noch) nichts sagen. Wie würde man denn die Struktur des Programms im Anhang bezeichnen? Es ist allerdings schon etwas älter, heute würde ich einige Dinge etwas anders machen (z.B. Timer). ...
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.