Benutzt irgendeiner von euch Adam Dunkels Protothreads in Projekten, die nicht mit seinem TCP/IP Stack zu tun haben? Ich nutze es in nur einem Projekt mit µIP. Ich frage mich, ob sein Konstrukt für andere Programme empfehlenswert ist?
Ich nutze einen (selbstgeschriebenen) Bruder davon. Habe mir beim Dunkel dafür einiges abgeschaut. Nutze es hauptsächlich um endliche Automaten zu basteln. Für nebeläufige Dinge. Also: Schleifen unterbrechen, und als DropIn Ersatz für delay(). Was mir beim Dunkel nicht gefällt, ist die komplizierte/unübersichtliche Handhabung, das habe ich komplett eliminiert/verborgen. Kann ich dir gerne zukommen lassen, wenn du möchtest...
Ja bitte, zeige mal deine Lösung. Ich finde nämlich, dass die Protothreads den Code von Endlichen Automaten deutlich besser lesbar machen. Allerdings muss man starke Einschränkungen bei der Nutzung lokaler Variablen berücksichtigen, die gerade wegen der Protothreads nicht immer offensichtlich (aber immerhin logisch) sind.
Stefan U. schrieb: > Allerdings muss man starke > Einschränkungen bei der Nutzung lokaler Variablen berücksichtigen, die > gerade wegen der Protothreads nicht immer offensichtlich (aber immerhin > logisch) sind. Ja, das ist bei meinem Kram genau so. Habe keinen Weg gefunden, das abzuändern. Die Alternative: Preemptives Multitasking. Siehe: Im Anhang, als Arduino Lib, mit ein paar einfachen Beispielen.
Das ist sicherlich ne solide 9.5 auf der WTF!!!?-Skala von [0..10] Ich kenne persönlich mehr als eine Person die von diesen gleichermaßen genialen wie auch grenzwertig kriminellen Makro-Konstrukten glasige Augen, Kammerflimmern und Schnappatmung bekommen würden. Ich glaube das sollte man nur verwenden wenn man eine ausführliche Erläuterung der Funktionsweise, der Nebenwirkungen und (wichtig!) der Wechselwirkungen mit anderen Sprachkonstrukten als Kommentar voranstellt, zusammen mit Links auf weiterführende Literatur. Ob es das wert ist?
Versuche mal diesen Makro-Scheiss zu debuggen wenn irgendwas nicht so funktioniert wie erwartet...
:
Bearbeitet durch User
> Versuche mal diesen Makro-Scheiss zu debuggen
Geht bestimmt nicht gut.
Sobald ich das Programm anhalte, fällt die Netzwerk-Kommunikations aus.
Wenn ich das bei einem Roboter machen würde, würder er völlig faösche
Bewegungen durchführen.
Ich kann mir ohnehin kaum vorstellen, laufende Zustandsautomaten zu
debuggen - da nutze ich eher Logmeldungen.
Hi, warum eine wiederaufgewärmte uralte C-Obfuscation-Platte mit irreführendem Namen immer noch Anhänger findet, ist mir schleierhaft. Auf jeden Fall gilt bei mir die Doktrin: Funktionalität einer Mainloop, insbesondere wenn Teil eines bare metal OS, muss explizit dastehen. Somit würden Protothreads in der Code Review gnadenlos durchfallen. Aber wenn ihr mit akademischen Spielereien glücklich seid, warum nicht. Ich sehe den Nutzen jedenfalls nicht, im Gegenteil, aber das Debug-Argument wurde ja bereits gebracht.
Rechtfertigung: Fitzebutze schrieb: > Obfuscation In "Obfuscation" steckt eine Verschleierungsabsicht. Das muss ich ja mal ganz klar abweisen! Der Anwendungscode wird übersichtlicher. Der Präprozessor Output ist allerdings für Menschen schwerer lesbar. Den Compiler kümmert es nicht. Zum Thema "Debuggen" bin ich etwas befangen. Das Debuggen, der Makros, war schon ein Abenteuer. Das Debuggen einer Anwendung ist dagegen kein Problem, wenn man denn verstanden hat, was die Makros tun. Wie ist es überhaupt soweit gekommen? Ein bisschen Erfahrung habe ich ja auch mit anderen Systemen. Wenn man da eine Nebenläufigkeit braucht, dann baut man sich einen Prozess/Thread/Task und überlässt einem Scheduler, dieser Task heimlich die Kontrolle zu entziehen, andere Dinge zu erledigen, und dann an der alten Stelle weiter zu machen. Da ist "das heimliche verschwinden aus der Funktion" ein akzeptiertes/gewolltes Verhalten. Meine kleine Arduino Welt, hauptsächlich bestehend Mega328 und Tiny85, bietet da nicht allzuviel Unterstützung. Aber nichts desto Trotz, führt meist kein Weg an Nebenläufigkeiten vorbei. Irgendwie MUSS man das abhandeln. Immer wieder, an 1000 Ecken. Preemtives Multitasking, z.B. RTOS, ist Ressourcen intensiv. Jede Task braucht eine Struktur im RAM. U.A, einen Stack Bereich und ein paar Flags. Auf meinen Zwergen ist RAM knapp. Meine Tests haben ergeben, dass die Anzahl Tasks zu begrenzt ist. Und das Debuggen ist da auch keine reine Freude! Und da ist im Grunde einer der Vorteile meiner Makros zu sehen. Diese Tasks geben den Stack immer wieder frei. Stackneutral? Ist das das richtige Wort? Für statische Variablen werden 2 Byte reserviert. Der Rückkehr Punkt. Welcher gleichzeitig den Zustand eines endlichen Automaten abbildet. Wenn man denn eine Task als einen solchen Automaten betrachtet. Wird eine Zeitsteuerung benötigt, kommen noch 4 Byte dazu. Ist ein schlankeres Multitasking auf den AVR Dingern möglich? Bei einem preemptiven System würde sich keiner über das Bild wundern:
1 | Task blink() |
2 | {
|
3 | while(1) |
4 | {
|
5 | PINB = (1<<PB5); |
6 | Pause(500); |
7 | }
|
8 | }
|
Jedem ist klar, dass der Kontrollfluss in der Funktion jederzeit unterbrochen werden kann. Ja, auch, dass Pause() aktiv Rechenzeit abgibt. Fitzebutze schrieb: > muss explizit dastehen Auf dem ersten Blick ist zu erkennen, dass das blink() Konstrukt ewig blinkt. Will man das alles explizit notieren, dann muss man die Schleife eliminieren, und die Zeitverwaltung ausbreiten. Und das, meine Lieben, ist der zweite Vorteil der Makros. 1. Was eine Schleife ist, bleibt eine Schleife. Und das ist eine ganz ungeliebte Aufgabe für mich: Ich mag keine "Schleifen ausrollen". Wenn etwas 5 mal getan werden will, dann möchte ich da eine for Schleife verwenden. Das empfinde ich als offensichtlich. Noch etwas komplexer wirds, wenn da Schleifen geschachtelt vorkommen, mit Break und Continue, Gespickt mit ein paar if Konstrukten. Das was da am Ende raus kommt, wenn es dann mal platt gebügelt ist, ist wirklich oft schwer lesbar. Auf jeden Fall, ist es seiner ursprüngliche Struktur beraubt. Und man muss schon genau hinschauen, wenn man da eine zählende Schleife identifizieren will. 2. Das Arduino delay() wird 1:1 durch taskPause() ersetzt. Eine Pause ist eine Pause und bleibt eine Pause. Die ganze Belastung, mit der Zeitabhandlung, ist komplett raus, auf ein (sichtbares) Statement in der Anwendung reduziert. Die beiden Punkte vereinfachen die Übernahme von vorhandenem Code in eine von Nebenläufigkeiten geprägte Anwendung. Bernd K. schrieb: > Ob es das wert ist? Ich, für mich, bin damit im Reinen! Die Antwort: Ja! Stefan hat gefragt, und seine Antwort bekommen. Was er damit anstellt, überlasse ich ganz ihm. Ob das Vorgehen empfehlenswert ist? KA... Für Anfänger nicht geeignet, würde ich mal sagen. Etwas erfahrenen, stellen sich die Nackenhäärchen auf. (incl. mir) Übrigens: Bernd K. schrieb: > grenzwertig kriminellen Makro-Konstrukten Es gibt in meiner Schublade noch eine Variante, welche das switch-case nicht vergewaltigt, dafür allerdings fleißig gotos verwendet. :-) irgendwie muss man ja hüpfen :-)
> Stefan hat gefragt, und seine Antwort bekommen. > Was er damit anstellt, überlasse ich ganz ihm. Ich hab's mir inzwischen angeschaut. Ist dem Lösungsansatz von Herrn Dunkels sehr ähnlich. Vor allem hat es meines Meinung nach die gleichen vor- und Nachteile, insofern passte dein Post sehr gut zum Thema. Ich persönlich stehe nicht so sehr auf Code-Generatoren und "Magie" im Hintergrund. Im Java Enterprise Umfeld stößt man ständig auf sowas. Diese Makros (bzw. in Java die Annotationen) machen den Code zweifellos besser lesbar. Aber sie verbergen eine Menge Details, die man wissen muss, um die Makros korrekt anzuwenden. Und genau das stört mich mehr, als ich den Vorteil wertschätze. Vermutlich liegt das auch daran, dass ich Programmierer der alten Schule bin. Ich habe mit Assembler angefangen und ich schaue heute noch ab und zu in das Assembler Listing, um sicher zu stellen, das der Compiler auch wirklich den Code erzeugt hat, den ich haben wollte. Ich bin mir aber auch bewusst, dass diese Arbeitsweise nicht mehr zeitgemäß ist. Insofern könnten diese gar nicht so neuen Protohtreads für jüngere Entwickler wesentlich attraktiver sein, als für mich. Wenn man die bisherigen Antworten hier betrachtet, scheinen mir Protothreads dennoch nicht empfehlenswert zu sein. Zu wenig Interesse und zu viel Ablehnung lese ich da heraus.
Arduino F. schrieb: > Im Anhang, als Arduino Lib, mit ein paar einfachen Beispielen. Ja, das sieht sehr elegant aus. Allerdings hab ich ne Weile gebraucht, dahinter zu steigen und den Präprozessoroutput darf man sich wirklich nicht ansehen. Ich denke allerdings, daß die Kollegen mich steinigen würden, wenn ich das auf Arbeit einsetzen würde. Ich nehme daher die klassischen switch/case Statemaschines und definiere die Cases mit beschreibenden States per Enum-Liste. Verzögerte Aufrufe mache ich mit meinem Scheduler: Beitrag "Wartezeiten effektiv (Scheduler)" Zu RTOS haben ich mich noch nicht durchringen können. Der RAM-/CPU-Bedarf ist heutzutage weniger wichtig. Aber bei einen RTOS entstehen ne Menge neuer Probleme, die man erstmal erkennen, verstehen und lösen muß, wie z.B. unbekannte Ausführungs-/Unterbrechungsreihenfolge, Datenkonsistens, Deadlocks, Fehlererkennung usw.
Stefan U. schrieb: > Wenn man die bisherigen Antworten hier betrachtet, scheinen mir > Protothreads dennoch nicht empfehlenswert zu sein. Ich habe mich schon vor Jahren über diesen Unfug ausgeworfen. Wer Multitasking wirklich benötigt, soll es auch dediziert machen oder es eben bleiben lassen. Ansonsten sag ich, daß es sinnvoller ist, sich einen nicht blockierenden Code anzugewöhnen, als sich auf solche Präprozessor-Kapriolen zu verlegen. Es geht ja, wenn man ereignisorientiert programmiert. Dann sind Wartezeiten einfach passé. W.S.
Arduino F. schrieb: >> grenzwertig kriminellen Makro-Konstrukten > Es gibt in meiner Schublade noch eine Variante, welche das switch-case > nicht vergewaltigt, dafür allerdings fleißig gotos verwendet. Hab mir mittlerweile zum Spaß grad auch mal was gebastelt, unter Verwendung von computed goto (gcc only) Ist meiner Meinung nach einfacher zu schlucken und zu verdauen als der vergewaltigte switch:
1 | /*
|
2 | * protothreads.h
|
3 | *
|
4 | * Created on: 12.03.2017
|
5 | * Author: bernd
|
6 | */
|
7 | |
8 | #ifndef PROTOTHREADS_H_
|
9 | #define PROTOTHREADS_H_
|
10 | |
11 | typedef struct { |
12 | void* next; |
13 | unsigned sleepstart; |
14 | } task_state_t; |
15 | |
16 | |
17 | #define _PASTE(x, y) x ## y
|
18 | #define PASTE(x, y) _PASTE(x, y)
|
19 | #define LINELABEL PASTE(label, __LINE__)
|
20 | |
21 | #define TASK(name) static void name(void)
|
22 | #define TASK_INIT static task_state_t _ts; if(_ts.next == 0) _ts.next = &&LINELABEL; goto *_ts.next; LINELABEL:
|
23 | #define TASK_SWITCH _ts.next = &&LINELABEL; return; LINELABEL:
|
24 | #define TASK_WAIT(condition) _ts.next = &&LINELABEL; LINELABEL: if (!(condition)) return
|
25 | #define TASK_SLEEP(sleeptime) _ts.next = &&LINELABEL; _ts.sleepstart=millitime; LINELABEL: if (millitime - _ts.sleepstart < sleeptime) return
|
26 | #define TASK_SCHEDULE(name) name()
|
27 | |
28 | #endif /* PROTOTHREADS_H_ */ |
und die main.c
1 | #include "MKL25Z4.h" |
2 | #include "gpio.h" |
3 | #include "protothreads.h" |
4 | |
5 | volatile unsigned millitime = 0; |
6 | |
7 | TASK(blinka) { |
8 | TASK_INIT; |
9 | while(1) { |
10 | LED_GN_high(); |
11 | TASK_SLEEP(600); |
12 | LED_GN_low(); |
13 | TASK_SLEEP(100); |
14 | }
|
15 | }
|
16 | |
17 | TASK(blinkb) { |
18 | TASK_INIT; |
19 | while(1) { |
20 | LED_RD_high(); |
21 | TASK_SLEEP(611); |
22 | LED_RD_low(); |
23 | TASK_SLEEP(100); |
24 | }
|
25 | }
|
26 | |
27 | TASK(blinkc) { |
28 | TASK_INIT; |
29 | while(1) { |
30 | LED_BL_high(); |
31 | TASK_SLEEP(627); |
32 | LED_BL_low(); |
33 | TASK_SLEEP(100); |
34 | }
|
35 | }
|
36 | |
37 | int main(void) { |
38 | SysTick_Config(48000000/1000); |
39 | gpio_init(); |
40 | |
41 | while(1) { |
42 | asm("wfi"); |
43 | TASK_SCHEDULE(blinka); |
44 | TASK_SCHEDULE(blinkb); |
45 | TASK_SCHEDULE(blinkc); |
46 | }
|
47 | }
|
48 | |
49 | |
50 | void SysTick_Handler(void) { |
51 | ++millitime; |
52 | }
|
:
Bearbeitet durch User
Auch ok. Aber ich würde es nicht Protothreads nennen, der Name ist schon vergeben. Nenne es doch "BerndThreads".
Arduino F. schrieb: > TaskMacro.zip Naja, das sieht ganz nett aus. Coroutinen bringen viele moderne Sprachen ja auch mit. Aber in einem Punkt halte ich es für sehr gefährlich und irreführend: Durch ein TaskSwitch (und alle Macros, die dieses aufrufen, wie das taskPause) werden lokale Variablen ungültig. Das ist nicht offensichtlich und es ist gefährlich. Das ist auch genau der Punkt, den Coroutinen richtig machen: Der Stack ändert sich nicht plötzlich. In C könnte man sowas vielleicht mit setjmp machen.
Bernd K. schrieb: > Hab mir mittlerweile zum Spaß grad auch mal was gebastelt, > unter Verwendung von computed goto (gcc only) Auch da wird der Stack bei einem Task switch ungültig. Außerdem funktioniert die Präemption ja nur aus der Task-Hauptroutine heraus. Das mag bei den trivialen LED-Blink-Beispielen OK sein, aber für ein größeres Programm kann das doch keine Lösung sein.
Putty ist voll davon: https://github.com/Yasushi/putty/blob/20458bcedb5935f5e8cd629c8398a29f71cfcd9d/ssh.c#L414 ssh1_rdpkt() ssh2_rdpkt() do_ssh_init() ssh_gotdata() do_ssh1_login() do_ssh1_connection() do_ssh2_transport() ssh2_setup_x11() ssh2_setup_agent() ssh2_setup_pty() ssh2_setup_env() do_ssh2_authconn()
Stefan U. schrieb: > Aber ich würde es nicht Protothreads nennen, der Name ist schon > vergeben. Ist ein allgemeiner Begriff, sowohl die Vorsilbe "proto" als auch der Begriff Protothread aus der Informatik, es gibt bereits mehrere Implementationen: https://de.wikipedia.org/wiki/Protothread
:
Bearbeitet durch User
> Putty ist voll davon
Wow! Ich habe das noch nie außerhalb von µIP gesehen.
WaMin schrieb: > Auch da wird der Stack bei einem Task switch ungültig. Es wird überhaupt kein Stack benutzt, alle lokalen Variablen sind static. > Außerdem funktioniert die Präemption ja nur aus der Task-Hauptroutine > heraus. Ja, deshalb (und auch zur Verwendung lokaler Variablen) müsste man eine deutliche Warnung für den zukünftigen Leser voranstellen, zusammen mit Literaturhinweisen. > Das mag bei den trivialen LED-Blink-Beispielen OK sein, aber für ein größeres Programm kann das doch keine Lösung sein. Das Putty ssh.c Beispiel hab ich ja oben schon gepostet. Das geht sicherlich weit über LED-Blinken hinaus. Boost bietet übrigens auch ne Implementation davon an.
Bernd K. schrieb: > Putty ist voll davon: In einer Datei, die 10244 Zeilen hat. Was dort produziert wurde, ist doch kaum noch wartbar. Nicht zuletzt durch diese Coroutinenfunktionalität.
Bernd K. schrieb: > Es wird überhaupt kein Stack benutzt, alle lokalen Variablen sind > static. Ja eben. Und das ist eine Besonderheit, die man beachten muss. Es ist aber keineswegs offensichtlich, dass hier der Stack über diesen scheinbaren Pause-Funktionsaufruf ungültig wird, weil es eben kein Aufruf, sondern ein verstecktes return/goto ist. Bernd K. schrieb: > Ja, deshalb (und auch zur Verwendung lokaler Variablen) müsste man eine > deutliche Warnung für den zukünftigen Leser voranstellen, zusammen mit > Literaturhinweisen. Man verrenkt sich hier, um etwas zu erreichen, was ganz einfach offen kodiert (z.B. als normale Statemachine mit normalen returns) viel lesbarer und wartbarer ist. Bernd K. schrieb: > Boost bietet übrigens auch ne Implementation davon an. Mit Sicherung des Stacks? Denn wenn der Stack gesichert wird, dann fände ich diese Art von Coroutinen ziemlich gut. Aber so halt nicht. In Sprachen, die Coroutinen vernünftig implementieren, verwende ich sie ja selbst gerne.
WaMin schrieb: > Man verrenkt sich hier, um etwas zu erreichen, was ganz einfach offen > kodiert (z.B. als normale Statemachine mit normalen returns) viel > lesbarer und wartbarer ist. Nicht unbedingt. Der Code wird auf jeden fall um den Faktor 2 kürzer und der Kontrollfluss durch die einzelnen States ist in normalem lesbaren C hingeschrieben, nicht außeinandergerissen in zig losgelöste Zustände deren Reihenfolge und Übergänge man nicht mehr direkt sehen kann. Es hat schon Vorteile, wenn nur nicht diese switch-Vergewaltigung drin wär. Deshalb gefällt mir die goto-Variante auch wesentlich besser, die find ich klarer, unverhohlener und daher einfacher zu vermitteln.
Bernd K. schrieb: > der Kontrollfluss durch die einzelnen States ist in normalem lesbaren C > hingeschrieben, Eben genau nicht. Der Stack verschwindet plötzlich mit dem scheinbaren Aufruf einer Funktion. Deshalb ist der Code hier an einer Stelle komplett geteilt, die nicht offensichtlich für einen C-Programmierer ist. Wenn man das switch/case explizit hinschreibt, können diese zwei Codeteile auch aufeinander folgen. Nur die Trennung an der Pause-Stelle sieht man dann explizit, weil der C-Block endet (oder mindestens der case). Das ist nichts neues für einen C-Programmierer.
Bernd K. schrieb: > Der Code wird auf jeden fall um den Faktor 2 kürzer Und das ist natürlich auch falsch. Das gilt nur für das triviale LED-Blink-Beispiel, das nur 1-2 Befehle zwischen den Pausen hat.
Bernd K. schrieb: > unter Verwendung von computed goto (gcc only) Im Falle von AVR, d.h. avr-gcc ist zu beachten, dass dort Computed Goto nicht unterstützt wird.
Johann L. schrieb: > Bernd K. schrieb: >> unter Verwendung von computed goto (gcc only) > > Im Falle von AVR, d.h. avr-gcc ist zu beachten, dass dort Computed Goto > nicht unterstützt wird. Also, dieses zuckt ganz gut in meiner Arduino IDE:
1 | #pragma once // include guard
|
2 | |
3 | |
4 | typedef void Task; // Task Type |
5 | |
6 | typedef void * TaskMark; |
7 | |
8 | |
9 | // grundlegene Worte um einen Task Bereich einzugrenzen
|
10 | #define taskBegin() \
|
11 | static TaskMark taskMark = &&TaskLableStart; \
|
12 | static unsigned long __attribute__((unused)) taskTimeStamp = 0; \
|
13 | goto *taskMark ; \
|
14 | TaskLableStart:
|
15 | |
16 | |
17 | #define taskEnd() \
|
18 | for(;;) \
|
19 | { \
|
20 | taskSwitch(); \
|
21 | }
|
22 | //;}
|
23 | |
24 | |
25 | #define LC_CONCAT2(s1, s2) s1##s2
|
26 | #define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2)
|
27 | |
28 | |
29 | |
30 | // Task Kontrol Worte, diese werden Taskwechsel einleiten
|
31 | #define taskSwitch() \
|
32 | do { \
|
33 | taskMark = &&LC_CONCAT(taskLable, __LINE__); \
|
34 | return; \
|
35 | LC_CONCAT(taskLable, __LINE__):; \
|
36 | } while (0)
|
37 | |
38 | |
39 | #define taskPause(Task_interval) taskTimeStamp = millis(); while((millis() - taskTimeStamp) < (Task_interval)) taskSwitch()
|
40 | #define taskWaitFor(Task_condition) while(!(Task_condition)) taskSwitch();
|
41 | |
42 | |
43 | |
44 | // Benennen und anspringen von Schrittketten Verzweigungen
|
45 | #define taskStepName(Task_stepname) Task_step_##Task_stepname :
|
46 | #define taskJumpTo(Task_stepname) goto Task_step_##Task_stepname
|
47 | |
48 | |
49 | // Task Prioritaet festlegen
|
50 | // 0 == hoch(wird bei jedem Durchlauf ausgeführt) bis 65535 (AnzahlAusfuehrungen == Durchlaeufe/pri )
|
51 | #define taskPriority(pri) \
|
52 | do \
|
53 | { \
|
54 | static uint16_t taskPri; \
|
55 | taskPri++; \
|
56 | if(taskPri < (pri) ) return;taskPri=0; \
|
57 | } while(0)
|
Arduino F. schrieb: > Johann L. schrieb: >> Bernd K. schrieb: >>> unter Verwendung von computed goto (gcc only) >> >> Im Falle von AVR, d.h. avr-gcc ist zu beachten, dass dort Computed Goto >> nicht unterstützt wird. > > Also, dieses zuckt ganz gut in meiner Arduino IDE: Und was sagt das? Es gibt Computed Goto, für welche avr-gcc falschen Code erzeugt. Und (dein) Code kann Anwesenheit von Fehlern zeigen, aber nicht deren Abwesenheit.
:
Bearbeitet durch User
ich finde die Mechanismen genial und die Disskussion darüber gut! Es erhellt die Verständnisschwierigkeiten, die einige mit der Umwandlung von Tasks in SPS-Loops haben. Es beleuchtet mehrere Aspekte und kann wirklich helfen, den Taskianern die Möglichkeiten der SPS-Loop zu vermitteln. Ich hätte nichtmal was dagegen, wenn jemand sowas in überschaubarem Code anwendet. Dann könnte man nämlich sehr einfach mal Loopcode daneben stellen.
Johann L. schrieb: > Und was sagt das? > > Es gibt Computed Goto, für welche avr-gcc falschen Code erzeugt. > > Und (dein) Code kann Anwesenheit von Fehlern zeigen, aber nicht deren > Abwesenheit. Ich sehe den Unterschied von "nicht unterstützt" und "falschem Code". "falschem Code" ist mir bisher nicht bekannt gewesen und auch noch nicht aufgefallen. Werde mich aber kundig machen...
WaMin schrieb: > Eben genau nicht. Der Stack verschwindet plötzlich mit dem scheinbaren > Aufruf einer Funktion. > Deshalb ist der Code hier an einer Stelle komplett geteilt, die nicht > offensichtlich für einen C-Programmierer ist. Ich habe volles Verständnis dafür, dass sich dir dabei die Fußnägel aufrollen. Glaub mir, da durfte ich auch durch. Es ist eine andere Sicht auf die Dinge! Besser: Eine Projektion. Als Erkennungsmerkmal setze ich den Datentype "Task" davor. Einen typischen c-Krieger sollte das zumindest misstrauisch machen. Hmm... Man(ich) prägt diesem dummen AVR seine Sicht der Dinge auf. Der gcc spielt mit. Die Kehrseite: Man(ich) muss die Kröte mit den lokalen Variablen schlucken.
Johann L. schrieb: >> unter Verwendung von computed goto (gcc only) > Im Falle von AVR, d.h. avr-gcc ist zu beachten, dass dort Computed Goto > nicht unterstützt wird. Das ist in der Form falsch: https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html Die hier gezeigte Nutzung funktioniert auch auf AVRs.
S. R. schrieb: > Johann L. schrieb: >>> unter Verwendung von computed goto (gcc only) >> Im Falle von AVR, d.h. avr-gcc ist zu beachten, dass dort Computed Goto >> nicht unterstützt wird. > > Das ist in der Form falsch: > https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html > > Die hier gezeigte Nutzung funktioniert auch auf AVRs. Es ist nunmal so dass es mehr als 1 Testfall für Computed Goto in der GCC Testsuite gibt, für den avr-gcc falschen Code erzeugt und crasht. Ich wollte nur darauf hingewiesen haben.
Wenn ich das richtig verstanden habe, tritt das nur bei der Differenzbildung von Lables auf. Den Fall haben wir hier nicht. Ist auch nicht nötig das hier zu verwenden. Oder habe ich was übersehen?
Arduino F. schrieb: > Oder habe ich was übersehen? Nö, das passt so. Johann ist gerade nur aktiv am Zurückrudern, ist aber nicht schlimm. :-)
Ich bin am Überlegen. - einmal das Prinzip gefressen - 2 Include-Dateien mit je ca 20 Zeilen Codes - 2 Überlebens-Regeln - don't use switch-case - don't rely on local variables Eigentlich ein niedriger Preis, um die Segnungen des Konzeptes "strukturierte Programmierung" wieder nutzen. siehe http://dunkels.com/adam/pt/examples.html "Radio dirver" mit dem Vergleich beider Ansätze. Imho ist die State Machine schon ein bißchen Rückkehr zum "guten alten Spaghetti-Code", oder? Funktioniert, kann effizient sein, fordert Disziplin und begleitende Erläuterung. Wenn man Erfahrung hat, kein Problem. Sieht man an den Glaubenskriegen hier und anderswo. Ich bin grad' dabei, mir eine "Werzeugkiste" für verschiedene Projekte zusammenzustellen, wie sie wahrscheinlich jeder von Euch Profis im Lauf der Jahre angesammelt hat. Ein typisches Beispiel braucht: - 4 Servo-Controller mit je ADC und PWM, PID dazwischen - Tasten, Quad-Encoder und LCD - evtl. CAN - evtl. UART-Debug - evtl. IP-Netz Da werde ich um die Nebenläufigkeit nicht rum kommen. Bin Hobbyist, hab vor 30 Jahren 6502 auf Karo-Pepier assembliert und seitdem mit wechselnder Intensität Hochsprachen, zuletzt meist auf Linux, gemacht. Bin grad durch die Blink-tutorials mit den Delay-Loops durch und stelle nun fest, daß es so für komplexere Aufgaben nicht gehen kann.
Wolfgang R. schrieb: > Da werde ich um die Nebenläufigkeit nicht rum kommen. Für Nebenläufigkeit sind diese Tasks aber m.E. weniger geeignet als eine SPS-Loop. Für Protokolle sind die Prototasks hingegen ideal. Wenn 1000 Byte zu verarbeiten sind ... egal. Dann warten die anderen Threads. Wobei die eigentliche Nebenläufigkeit (Tasten-Entprellen, Uart, Encoder) meist ja auch ohne RTOS nebenläufig und priorisiert sind ... durch Interrupts.
In ein paar Projekten habe ich so was auch schon mal verwendet. Allerdings wurden die einzelnen "Threads" in C++ Klassen gepackt. Damit kann man die nötigen Variablen in der Klasse lassen und hat damit das ganze räumlich zusammen. Ist ja nun auch nicht so ein Hexenwerk, dass man das nicht verstehen könnte. Auch das Debuggen nach etwas Gewöhnung geht ganz gut. Am gefährlichsten sehe ich noch das PtWaitUntil bzw. taskWaitFor im Code von oben. Solchen Funktionen sollte man immer irgend eine maximale Zeit o.ä. mitgeben damit verhindert wird, dass nur noch der Watchdog hilft wenn's mal hängt. Aber das ist nicht nur bei dieser Art Implementierung so.
Ich benutze das hier auf AVR und selbst angepasst auch auf ARM. https://github.com/joe7575/Arduino-MOS Funktioniert ausgesprochen gut. Ich finde der Code ist lesbarer, da er eher linear runter geschrieben werden kann. Einzigen Punkt, den man beachten muss, ist eben, dass lokale Variable ihrem Wert verlieren bei einem "Kontextswitch". Kann ich aber mit leben. Mit den Protothreads hab ich vor Jahren auch mal ein "grösseren" Projekt im Job gemacht. Das gefiel mir nicht so gut wegen dem Verzicht auf switch. Huch ja, da werden Makros auf "böse" Art verwendet. Das finde ich genauso eine unsinnige Diskussion, wie goto ist verboten. In meinen Augen Blödsinn. Alles mit Bedacht und der nötigen Sorgfalt eingesetzt ist das alles kein Problem finde ich.
:
Bearbeitet durch User
Johann L. schrieb: > Es ist nunmal so dass Gibt's dafür einen Bugreport? Bisher hat das bei mir immer funktioniert. Würde ich mir gerne mal ansehen.
:
Bearbeitet durch User
In der Vergangenheit hatte ich für kleinere Projekte mit Atmega & Co. sehr oft und gerne Protothread eingesetzt. Seit ich aber komplett auf STM32 umgestiegen bin, setzte ich vermehrt CMSIS-RTX ein. Protothread kommt nur noch selten zum Zug.
Das ist ja komisch, plötzlich melden sich ganz viele Leute, die Protothreads gerne verwendet haben. Davor gab es fast nur Ablehnung. Geht hier noch alles mit Rechten Dingen zu?
Na ja, ich kam erst zum antworten, als die "Ablehner" schon alle ihre Antworten geschrieben haben. :-) Hat glaube ich nix mit Magie o.ä. zu tun ;-)
900ss D. schrieb: > Mit den Protothreads hab ich vor Jahren auch mal ein "grösseren" Projekt > im Job gemacht. Das gefiel mir nicht so gut wegen dem Verzicht auf > switch. Falls das zur Verwirrung führt, ich meinte die Adam Dunkels "Original-Protothreads". Dort kann man kein switch verwenden. Bei dem von mir oben aufgeführten "MOS" geht das schon.
900ss D. schrieb: > Johann L. schrieb: >> Es ist nunmal so dass > > Gibt's dafür einen Bugreport? Hab ihn schon gefunden. Falls es noch jemanden interessiert: https://gcc.gnu.org/ml/gcc-bugs/2014-05/msg00126.html
Stefan U. schrieb: > Geht hier noch alles mit Rechten Dingen zu? Magie? Ach... Ich glaube, dass hat eher mit dem üblichen "Sozialverhalten" hier im Forum zu tun. Als ich deine Frage gelesen habe, musste ich entscheiden, Antwort oder nicht. Zu dem Zeitpunkt war mir klar, dass einige "Urteiler" aus den Büschen gesprungen kommen. Angst vor Haue! Aber, wie man sieht, manchmal lohnt es sich, da durch zu gehen. Und mir gefallen die Protothreads, dann stehe ich auch dazu. Ist ja auch ein heißes Eisen, mit dem krummen Switch oder dem gerne verfluchten GoTo. Konfliktpotential genug... Wie auch immer.... Eine drohende Eskalation, ist zu einem konstruktiven Dialog geworden. Ganz ehrlich: Da habe ich so kaum mit gerechnet. Ja, das Ganze hat sich gelohnt. Danke für deine Eröffnung, und einen Dank an die Anderen, denn aus den Codestrecken/Texten, kann ich für meine TaskMakros einiges mitnehmen. Auf Dauer, werden sie eine Renovierung bekommen. z.B. im Bereich Task Synchronisierung (bei mir quasi noch nicht vorhanden) Auch, denke ich, dass so mancher stumme Mitleser eine andere Sicht auf die Dinge vorgeführt bekommen hat. Weiter so!
900ss D. schrieb: > Falls das zur Verwirrung führt, ich meinte die Adam Dunkels > "Original-Protothreads". Dort kann man kein switch verwenden. Bei dem > von mir oben aufgeführten "MOS" geht das schon. Dem Dunkel hat auch eine Goto Variante im Ordner. Zumindest in der Arduino Lib ist die mit drin.
Arduino F. schrieb: > Zumindest in der Arduino Lib ist die mit drin. Deshalb hab ich die dann später verwendet. Bin gespannt wann es auf einem AVR das erste Mal knallt da es auf AVR ja ein Problem im GCC gibt. Ist bei mir bisher nicht aufgetreten.
Arduino F. schrieb: > Stefan U. schrieb: >> Geht hier noch alles mit Rechten Dingen zu? > Magie? > > Ach... > Ich glaube, dass hat eher mit dem üblichen "Sozialverhalten" hier im > Forum zu tun. (...) > Eine drohende Eskalation, ist zu einem konstruktiven Dialog geworden. :-))))) Jungs, den Profi erkennt man daran, daß er drüber steht! Es ist doch immer wieder überraschend, wie Menschen, von denen man eigentlich von Berufs wegen Rationalität im Überdruss erwarten würde, in die animalischen Grundmuster verfallen, sobald sie mehr als eine Hand voll Bytes im Blick haben müssen. Es freut mich, wenn ich mit meiner Anfängerfrage ein Stück Vernunft aus dem Versteck locken konnte. Wer sich mal richtig an der Irrationalität der eigenen Zunft erfreuen möchte, kann sich diesen Thread (den ganzen, nicht nur den verlinkten Einsprung) zu Gemüte führen, den Meister Dunkels selber verlinkt hat: http://www.8052.com/forum/read/91275 Das ist definitv mehr Soziologie als Programmieren. Aber offensichtlich nicht off-topic. Und, ja, danke an die Befürworter. Ich werd's probieren.
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.