Moin, moin. Ich habe hier mal was ausgegraben (Beitrag "Re: Programm bzw. Ablaeufe steuern") und bin dabei, mich durchzuarbeiten. Dazu zwei Fragen: Das Schlüsselwort static holt in der Funktion RunScheduler die Variable NextTask aus der Funktion raus? Oder warum wird diese nicht bei jedem Aufruf neu initialisiert? Wenn man in der Struktur Task die Variable currentState auf 16 Bit erweitert, könnte man diese direkt als Zähler verwenden. Spart pro 'Task' ein Byte und ein paar Funktionsaufrufe u. Zuweisungen. Oder kommt man da in die Rubrik: 'Das gehört sich nicht!' P.S.: Quelltext von Karl Heinz Buchegger noch mal im Anhang
Kernigham & Ritchie Programmieren in C Ausgabe Hanser Verlag von 1983 Seite 87f Kapitel 4.6 Die Speicherklasse "static" Manchmal ist ein Buch verdammt von Vorteil. Oder gurgle: http://de.wikibooks.org/wiki/C-Programmierung:_static_%26_Co. Gruß Joachim
@XXX Naja, mir ging es jetzt nicht so um das 'Gesetz', sondern mehr um die 'Interpretation'. Beim nochmaligen Durchlesen ist mir allerdings klar geworden: Die Variable gehört zur Funktion, ist von außen nicht sichtbar und der Compiler organisiert die Initialisierung.
Ralf schrieb: > Wenn man in der Struktur Task die Variable currentState auf 16 Bit > erweitert, könnte man diese direkt als Zähler verwenden. Spart pro > 'Task' ein Byte und ein paar Funktionsaufrufe u. Zuweisungen. Oder kommt > man da in die Rubrik: 'Das gehört sich nicht!' Würde mich schon noch interessieren. Ich glaube, bei meinen Hobby-Programmierungen geht's manchmal noch 'kreuz und quer'. Nicht dass ich mich da verrenne...
Ralf schrieb: > Das Schlüsselwort static holt in der Funktion RunScheduler die > Variable NextTask aus der Funktion raus? Oder warum wird diese nicht > bei jedem Aufruf neu initialisiert? Sie wird nicht ständig neu initialisiert, weil sie eben static ist. :) > Wenn man in der Struktur Task die Variable currentState auf 16 Bit > erweitert, könnte man diese direkt als Zähler verwenden. Spart pro > 'Task' ein Byte und ein paar Funktionsaufrufe u. Zuweisungen. Oder kommt > man da in die Rubrik: 'Das gehört sich nicht!' Zähler wofür? Der Scheduler muss sich den aktuellen State merken und tut das in currentState. Da wird nix gezählt.
Ralf schrieb: > Da wird schon gezählt. Wo? > Vielleicht hat der Autor mal Zeit? Wozu? Ist schönes Standard-C, das jeder fortgeschrittene Programmierer lesen kann/können sollte. (Auf die Gefahr hin, dass ich mich jetzt übelst blamiere hehe).
Helfer schrieb: >> Vielleicht hat der Autor mal Zeit? > Wozu? Ist schönes Standard-C, das jeder fortgeschrittene Programmierer > lesen kann/können sollte. (Auf die Gefahr hin, dass ich mich jetzt > übelst blamiere hehe). Na, dann lies mal etwas fortgeschritten. Es geht um Multitasking. Das werden den Tasks Rechenzeiten zugeordnet. Und die müssen (Timer) ermittelt werden.
Ralf schrieb: > Na, dann lies mal etwas fortgeschritten. Es geht um Multitasking. Das > werden den Tasks Rechenzeiten zugeordnet. Und die müssen (Timer) > ermittelt werden. Nein, das sehe ich nirgends so. Es gibt zwar einen Hardware Timer und eine ISR, die ein Array von Zählern bearbeitet (AllTimers), aber das hat keinen Einfluss auf den Scheduler. Der Scheduler arbeitet einfach die Task Liste ab und ruft die Task functions regelmässig auf und übermittelt deren aktuellen Status (die functions sind als FSM implementiert). Er kümmert sich nicht um die Timer. StartTimer() enthält keinen Rücksprung zum scheduler, gibt keine Rechenzeit ab, implementiert keinerlei Multitasking. Beispielsweise:
1 | uint8_t Blink_2S( uint8_t state ) |
2 | {
|
3 | switch( state ) { |
4 | |
5 | case INIT: |
6 | StartTimer( 0, 2000 ); // Timer starten, 2 Sekunden |
7 | return WAIT_2S; // beim nächsten Aufruf soll die Statemachine im Zustand WAIT_2S sein |
8 | break; |
9 | |
10 | case WAIT_2S: |
11 | if( !IsTimerStopped( 0 ) ) // Timer abgelaufen? |
12 | return WAIT_2S; // nein: Task bleibt im Zustans WAIT_2S |
13 | //... mehr code
|
Der Scheduler startet den Task (function Blink_2S) erstmal mit dem State INIT. Der initialisiert einen Sw-Timer und definiert den nächsten State. Damit geht's zurück zum Scheduler. Der startet den/die nächsten Tasks und irgendwann wieder Blink_2S, diesemal im State WAIT_2S. Dort wird der SW-Timer geprüft, und erst wenn er abgelaufen ist, in den nächsten State gewechselt. Usw...
Ralf schrieb: > Da wird schon gezählt. > Vielleicht hat der Autor mal Zeit? Kannst Du mal sagen wo? Ich sehe nur, daß NextTask hochgezählt wird. Und ein Zustand hat nichts mit zählen zu tun, deshalb sollte man die nicht vermischen... Aber villeicht postest Du mal den geänderten Code, so wie Du es Dir vostellst.
Also gut: In der Timer-ISR wird für alle Tasks die Startzeit ermittelt, indem AllTimers runtergezählt wird. In RunScheduler werden die Tasks nacheinander aufgerufen. Diese prüfen, ob 'ihre Zeit gekommen' ist, wenn ja, wird currentState umgestellt und beim nächsten Durchlauf der entsprechende Zweig abgearbeitet und neu initialisiert. Ich hätte jetzt die Timer-ISR weiter hinten deklariert und currentState bzw. dann besser currentTime direkt zugewiesen:
1 | for (uint8_t i=0; i < MAX_TASKS; i++) |
2 | if (AllTasks[i].currentTime > 0) |
3 | AllTasks[i].currentTime--; |
In RunScheduler dann entsprechend:
1 | static uint8_t NextTask=0; |
2 | if (AllTasks[NextTask].currentTime > 0) |
3 | (*AllTasks[NextTask].execute)(/* Parameter nicht nötig */); |
4 | NextTask++; |
5 | if (NextTask > MAX_TASKS) |
6 | NextTask=0; |
Spart RAM und und Code... (sag ich) Oder: Mach's lieber nicht, weil... (Begründung)
Ich verstehe nicht, wo Du jetzt den State unterbringst. Die Tasks sind doch State-Maschinen, wo wird jetzt der State gespeichert?
...oder meinst Du, weil im Beispiel eh nur Zeitgesteuert umgeschaltet wird ist der State direkt mit der Zeit verknüpft... Damit reduzierst Du aber die Statemaschine zu einer reinen Zeitsteuerung, und das geht nur mit dem Beispiel... Eine Task, die nicht nur eine LED blinken lässt, sondern den State z.B. für Datenübertragung benutzt (mit entsprechenden Zuständen) kannst Du dann nicht mehr realisieren...
(hatte ich noch nicht gelesen:) Bernhard M. schrieb: > Eine Task, die nicht nur eine LED blinken lässt, sondern den State z.B. > für Datenübertragung benutzt (mit entsprechenden Zuständen) kannst Du > dann nicht mehr realisieren... Dass die LED blinkt, ist ja nur 'Zufall'. Der Task soll ja, egal was er dann macht, starten, wenn seine Zeit ran ist.
Ralf schrieb: > (hatte ich noch nicht gelesen:) > Bernhard M. schrieb: >> Eine Task, die nicht nur eine LED blinken lässt, sondern den State z.B. >> für Datenübertragung benutzt (mit entsprechenden Zuständen) kannst Du >> dann nicht mehr realisieren... > > Dass die LED blinkt, ist ja nur 'Zufall'. Der Task soll ja, egal was er > dann macht, starten, wenn seine Zeit ran ist. Dann wirds aber komplizierter. Dann muss der 'Scheduler' Buch darüber führen, wenn er die Task wieder ausführen darf. Einfacher ist es, wenn du ganz einfach in jede State Maschine einen entsprechenden Wartezustand einführst, der überprüft, ob seine Zeit schon gekommen ist. Wenn nicht, gibt er sofort wieder an den Scheduler zurück; wenn schon dann wechselt die State Maschine in einen anderen Zustand. Ob jetzt letzten Endes der Scheduler die 'Däumchen dreht', oder ob er reihum allen 'Tasks' Gelegenheit gibt nachzusehen, ob ihre Timer schon abgelaufen sind, ist für den µC Jacke wie Hose. Für dich ist aber kooperatives Multitaksing, bei dem sich die Tasks selbst die Zeitsteuerung machen, viel leichter zu implementieren, als nicht kooperatives Multitasking, bei dem der SCheduler einem Task die Kontrolle unter dem Allerwertesten wegziehen kann.
Ich war gerade beim Schreiben und habe vor dem Absenden noch mal schnell einen Seiten-Refresh ausgeführt. Karl Heinz Buchegger schrieb: > Dann wirds aber komplizierter. Ich meine jetzt, das ist was völlig anderes, als ursprünglich gedacht. Hier, was ich erst nur schreiben wollte: -------- Ich denke nur mal laut: Ralf schrieb: > Dass die LED blinkt, ist ja nur 'Zufall'. Der Task soll ja, egal was er > dann macht, starten, wenn seine Zeit ran ist. Ich merke jetzt, dass das für die ursprüngliche Aufgabe der falsche Ansatz ist. Mit meiner Idee kann man zwar zeitlich flexibel die einzelnen Tasks aufrufen, aber das war ja hier nicht gewollt. Mich hat die Übergabe von currentState im Scheduler durcheinander gebracht. Dass nur beim Ablauf des Timers der Task 'sinnvoll produktiv' wird, das ist rein zufällig. Also kann ich mir die Antwort jetzt selbst geben: Kann man zwar machen, hat mit der eigentlichen Aufgabe des Schedulers nichts zu tun. Deswegen "Rubrik: 'Das gehört sich nicht!'" --------
Karl Heinz Buchegger schrieb: > Ralf schrieb: >> (hatte ich noch nicht gelesen:) >> Bernhard M. schrieb: >>> Eine Task, die nicht nur eine LED blinken lässt, sondern den State z.B. >>> für Datenübertragung benutzt (mit entsprechenden Zuständen) kannst Du >>> dann nicht mehr realisieren... >> >> Dass die LED blinkt, ist ja nur 'Zufall'. Der Task soll ja, egal was er >> dann macht, starten, wenn seine Zeit ran ist. > > Dann wirds aber komplizierter. > > Dann muss der 'Scheduler' Buch darüber führen, wenn er die Task wieder > ausführen darf. > ...usw. ... ... hab' ich mir jetzt noch mal genauer durchgelesen. Das ist genau das, was ich etwas spät nun auch erkannt habe. Eine Änderung hätte ich jetzt noch im Kopf: Die Tasks timerinterruptgesteuert (1ms) aufrufen und die Tasks kümmern sich selbst um das 'Zeitzählen'. Vielleicht gibt's ja auch mal Tasks, die auf ganz andere Ereignisse warten müssen...
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.