mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Wer benutzt Protothreads?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Stefan ⛄ F. (stefanus)


Bewertung
3 lesenswert
nicht lesenswert
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?

von Arduino Fanboy D. (ufuf)


Bewertung
0 lesenswert
nicht lesenswert
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...

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Arduino Fanboy D. (ufuf)


Angehängte Dateien:

Bewertung
2 lesenswert
nicht lesenswert
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.

von Bernd K. (prof7bit)


Bewertung
1 lesenswert
nicht lesenswert
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?

von Joe F. (easylife)


Bewertung
0 lesenswert
nicht lesenswert
Versuche mal diesen Makro-Scheiss zu debuggen wenn irgendwas nicht so 
funktioniert wie erwartet...

: Bearbeitet durch User
von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
> Siehe:Im Anhang

Danke, das schaue ich mir mal an.

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
> 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.

: Bearbeitet durch User
von Fitzebutze (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Arduino Fanboy D. (ufuf)


Bewertung
1 lesenswert
nicht lesenswert
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:
Task blink()
{
  while(1)
  {
    PINB = (1<<PB5);
    Pause(500);
  }
}
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 :-)

von Stefan ⛄ F. (stefanus)


Bewertung
1 lesenswert
nicht lesenswert
> 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.

von Peter D. (peda)


Bewertung
2 lesenswert
nicht lesenswert
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.

von W.S. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Bernd K. (prof7bit)


Bewertung
1 lesenswert
nicht lesenswert
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:
/*
 * protothreads.h
 *
 *  Created on: 12.03.2017
 *      Author: bernd
 */

#ifndef PROTOTHREADS_H_
#define PROTOTHREADS_H_

typedef struct {
  void* next;
  unsigned sleepstart;
} task_state_t;


#define _PASTE(x, y) x ## y
#define PASTE(x, y) _PASTE(x, y)
#define LINELABEL PASTE(label, __LINE__)

#define TASK(name) static void name(void)
#define TASK_INIT static task_state_t _ts; if(_ts.next == 0) _ts.next = &&LINELABEL; goto *_ts.next; LINELABEL:
#define TASK_SWITCH  _ts.next = &&LINELABEL; return; LINELABEL:
#define TASK_WAIT(condition) _ts.next = &&LINELABEL; LINELABEL: if (!(condition)) return
#define TASK_SLEEP(sleeptime) _ts.next = &&LINELABEL; _ts.sleepstart=millitime; LINELABEL: if (millitime - _ts.sleepstart < sleeptime) return
#define TASK_SCHEDULE(name)  name()

#endif /* PROTOTHREADS_H_ */


und die main.c
#include "MKL25Z4.h"
#include "gpio.h"
#include "protothreads.h"

volatile unsigned millitime = 0;

TASK(blinka) {
  TASK_INIT;
  while(1) {
    LED_GN_high();
    TASK_SLEEP(600);
    LED_GN_low();
    TASK_SLEEP(100);
  }
}

TASK(blinkb) {
  TASK_INIT;
  while(1) {
    LED_RD_high();
    TASK_SLEEP(611);
    LED_RD_low();
    TASK_SLEEP(100);
  }
}

TASK(blinkc) {
  TASK_INIT;
  while(1) {
    LED_BL_high();
    TASK_SLEEP(627);
    LED_BL_low();
    TASK_SLEEP(100);
  }
}

int main(void) {
  SysTick_Config(48000000/1000);
  gpio_init();

  while(1) {
    asm("wfi");
    TASK_SCHEDULE(blinka);
    TASK_SCHEDULE(blinkb);
    TASK_SCHEDULE(blinkc);
  }
}


void SysTick_Handler(void) {
  ++millitime;
}

: Bearbeitet durch User
von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Auch ok.
Aber ich würde es nicht Protothreads nennen, der Name ist schon 
vergeben. Nenne es doch "BerndThreads".

von WaMin (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von WaMin (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
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()

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
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
von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
> Putty ist voll davon

Wow! Ich habe das noch nie außerhalb von µIP gesehen.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
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.

von WaMin (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von WaMin (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Bernd K. (prof7bit)


Bewertung
2 lesenswert
nicht lesenswert
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.

von WaMin (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von WaMin (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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.

von Arduino Fanboy D. (ufuf)


Bewertung
0 lesenswert
nicht lesenswert
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:
#pragma once // include guard


typedef  void Task; //  Task Type

typedef void * TaskMark;


// grundlegene Worte um einen Task Bereich einzugrenzen
#define taskBegin()                                               \
static  TaskMark taskMark = &&TaskLableStart;                     \
static unsigned long __attribute__((unused)) taskTimeStamp = 0;   \
goto *taskMark ; \
  TaskLableStart:  
  
  
#define taskEnd()               \
  for(;;)                       \
  {                             \
          taskSwitch();         \
  }                             
//;}


#define LC_CONCAT2(s1, s2) s1##s2
#define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2)



// Task Kontrol Worte, diese werden Taskwechsel einleiten
#define taskSwitch()                          \
do {                                             \
      taskMark = &&LC_CONCAT(taskLable, __LINE__);    \
      return;                                       \
      LC_CONCAT(taskLable, __LINE__):;           \
    } while (0)
    
    
#define taskPause(Task_interval) taskTimeStamp = millis(); while((millis() - taskTimeStamp) < (Task_interval)) taskSwitch()
#define taskWaitFor(Task_condition) while(!(Task_condition)) taskSwitch();



// Benennen und anspringen von Schrittketten Verzweigungen
#define taskStepName(Task_stepname) Task_step_##Task_stepname  :
#define taskJumpTo(Task_stepname)  goto  Task_step_##Task_stepname 


// Task Prioritaet festlegen
// 0 == hoch(wird bei jedem Durchlauf ausgeführt) bis 65535 (AnzahlAusfuehrungen == Durchlaeufe/pri )
#define taskPriority(pri)                                   \
do                                                          \
{                                                           \
  static uint16_t taskPri;                                  \
  taskPri++;                                                \
  if(taskPri < (pri) ) return;taskPri=0;                    \
} while(0)



von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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
von A. S. (achs)


Bewertung
1 lesenswert
nicht lesenswert
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.

von Arduino Fanboy D. (ufuf)


Bewertung
0 lesenswert
nicht lesenswert
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...

von Arduino Fanboy D. (ufuf)


Bewertung
0 lesenswert
nicht lesenswert
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.

von S. R. (svenska)


Bewertung
0 lesenswert
nicht lesenswert
WaMin schrieb:
> Mit Sicherung des Stacks?

Nein, denn dann wären es keine Protothreads mehr.

von S. R. (svenska)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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.

von Arduino Fanboy D. (ufuf)


Bewertung
0 lesenswert
nicht lesenswert
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?

von S. R. (svenska)


Bewertung
0 lesenswert
nicht lesenswert
Arduino F. schrieb:
> Oder habe ich was übersehen?

Nö, das passt so. Johann ist gerade nur aktiv am Zurückrudern, ist aber 
nicht schlimm. :-)

von Wolfgang R. (wjr)


Bewertung
0 lesenswert
nicht lesenswert
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.

von A. S. (achs)


Bewertung
0 lesenswert
nicht lesenswert
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.

von temp (Gast)


Bewertung
1 lesenswert
nicht lesenswert
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.

von 900ss D. (900ss)


Bewertung
2 lesenswert
nicht lesenswert
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
von 900ss D. (900ss)


Bewertung
0 lesenswert
nicht lesenswert
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
von Mehmet K. (mkmk)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
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?

von 900ss D. (900ss)


Bewertung
0 lesenswert
nicht lesenswert
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 ;-)

von 900ss D. (900ss)


Bewertung
0 lesenswert
nicht lesenswert
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.

von 900ss D. (900ss)


Bewertung
0 lesenswert
nicht lesenswert
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

von Arduino Fanboy D. (ufuf)


Bewertung
0 lesenswert
nicht lesenswert
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!

von Arduino Fanboy D. (ufuf)


Bewertung
0 lesenswert
nicht lesenswert
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.

von 900ss D. (900ss)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Wolfgang R. (wjr)


Bewertung
0 lesenswert
nicht lesenswert
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.

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.