Forum: Projekte & Code Minimalistisches kooperatives Multitasking.


von Stefan A. (king-crash)


Angehängte Dateien:

Lesenswert?

Hallo,

Vor einiger Zeit habe ich für Mikrocontroller ein paar Makros zur 
Vereinfachung von Abläufen geschrieben. Die Grundidee ist, dass Adressen 
von Sprunglabels in eine Variable geschrieben werden um bei einem 
erneuten Aufruf der beinhaltenden Funktion an der entsprechenden Stelle 
wieder zu starten. So ist das Ganze selbstverständlich in den 
Möglichkeiten stark begrenzt, für einfache Dinge dafür aber umso 
leichter anzuwenden.

Die zwei zentralen Makros sind:
NT_INIT muss an den Anfang der zu unterbrechenden Funktion, hierdurch 
wird die Variable für die Sprungadresse und der Sprungbefehl eingefügt.
NT_BREAK speichert die Aktuelle Programmadresse in der Variablen und 
springt zurück

Es muss darauf geachtet werden, dass nach jedem Rücksprung sämtliche 
Stackvariablen der Funktion einen undefinierten Inhalt haben!

Im Anhang auch noch ein Testprogramm und seine expandierte Form. Zu 
sehen ist, dass die Sprunglabels einfach automatisch aufwärts nummeriert 
werden.

Grüße

von Falk B. (falk)


Lesenswert?

Ohje! Biete Lösung, suche Problem.
Da nehm ich lieber ne Handvoll Old school Statemachines und muss 
keinerlei Handstände machen.

von Nick M. (Gast)


Lesenswert?

Stefan A. schrieb:
> Es muss darauf geachtet werden, dass nach jedem Rücksprung sämtliche
> Stackvariablen der Funktion einen undefinierten Inhalt haben!

Dann ändere doch bitte den Titel auf: "Satanistisches ..." statt 
"Minimalistisches ...".

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Stefan A. schrieb:
> Die zwei zentralen Makros sind:
> NT_INIT muss an den Anfang der zu unterbrechenden Funktion, hierdurch
> wird die Variable für die Sprungadresse und der Sprungbefehl eingefügt.
> NT_BREAK speichert die Aktuelle Programmadresse in der Variablen und
> springt zurück

... kannst du mal die Unterschiede/Vorteile zu longjmp() und setjmp() 
aus setjmp.h aufzeigen - ich sehe prakmatisch keine!?

von Stefan A. (king-crash)


Lesenswert?

Der Vorteil ist, dass bei Verwendung von Sprunglabels durch die Makros 
keine explizite Benamung der Rücksprungstellen erfolgen muss und auch 
keine libc gebraucht wird.
Man könnte das Ganze statt um goto auch um longjmp/setjmp bauen aber für 
kleine µC Projekte verwende ich die libc normalerweise nicht.
Ich denke das sollte einer der am wenigsten Overhead erzeugenden 
Methoden.

@ Falk B.
Falsch. Die Vorgehensweise war genau anderst rum. Ich hatte das damalige 
Projekt mit einer expliziten Statemachine aufgebaut und bin wahnsinnig 
geworden die ganzen Zustände mit Nummer zu managen. Es war damals für AT 
Kommandos. Wenn einmal ein Einschub für Code erfolgen sollte musste die 
gesamte Nummerierung geändert werden. Letzten Endes erzeugen die Makros 
nichts anderes als eine Statemachine, nur eben im Hintergrund und der 
Benutzer muss sich nicht darum kümmern. Im Grunde könnte man es 
vermutlich auch um switch/case bauen, ich vermute aber dass das 
anspringen einer Adresse schneller ist.

von Falk B. (falk)


Lesenswert?

Stefan A. schrieb:
> @ Falk B.
> Falsch. Die Vorgehensweise war genau anderst rum. Ich hatte das damalige
> Projekt mit einer expliziten Statemachine aufgebaut und bin wahnsinnig
> geworden die ganzen Zustände mit Nummer zu managen.

1. Fehler. Für sowas nimmt man enums! Die machen den Code DEUTLICH 
lesbarer und um die Verwaltung der Kodierung kümmert sich der Compiler! 
Das ist sein Job, den kann er auch deutlich besser als der 
Programmierer.

https://www.mikrocontroller.net/articles/Statemachine#Reaktionen_auf_.C3.A4u.C3.9Fere_Ereignisse

> Es war damals für AT
> Kommandos.

Einen Parser für AT-Kommandos haben schon Tausende geschrieben und mal 
ganz sicher nicht mit so einem Chaosansatz wie du.

> Wenn einmal ein Einschub für Code erfolgen sollte musste die
> gesamte Nummerierung geändert werden.

Siehe oben! Du hast ein paae elementare Grundlagen von Hochsprachen 
nicht verstanden und gurkst noch mit Assemblerdenkweise rum!

> Letzten Endes erzeugen die Makros
> nichts anderes als eine Statemachine, nur eben im Hintergrund und der
> Benutzer muss sich nicht darum kümmern.

Zu welchem Preis! Lesbarkeit? Seiteneffekte? nein Danke!
Selbst die Protothreads der Herrn Dunkel sind eher umstritten und mehr 
eine Idee als eine praktische Lösung.

http://dunkels.com/adam/pt/

> Im Grunde könnte man es
> vermutlich auch um switch/case bauen, ich vermute aber dass das
> anspringen einer Adresse schneller ist.

Unsinn! Außerdem ist die ultimative Optimierung wegen ein paar Takten 
seltenst nötig und sinnvoll. Viel mehr geht es um gute Struktur, 
Lesbarkeit und Wartbarkeit des Codes!

von Ben S. (bensch123)


Lesenswert?

Ich weiß, dass manche sich wieder über die Kommentare und meinen 
aufregen, aber manchmal muss man die Leute halt auf den Boden der 
Tatsachen zurückholen.

Wenn Leute von Desktopprogrammierung auf µC Programmierung wechseln.
In 99% der Fällen braucht man kein Betriebssystem, irgendwelches 
"Multithreading" oder freeRTOS. Einzig und alleine braucht man 
geschickte Programmierung. Mit DMA kann man schon soviel 
parallelisieren, das reicht fast immer aus.

Das Beste ist dann, wenn man sowas auf einem Atmega oder sonstigen 
8-bitter implementieren möchte. Ist halt hipp und toll. Was für ein 
Blödsinn.

Ich habe noch nie eine µC Firmware geschrieben, die unter 1000 loops pro 
Sekunde in der main macht, eher 5000. DMA reicht da vollkommen als 
"Multithreading".

: Bearbeitet durch User
von Stefan A. (king-crash)


Lesenswert?

Der Code wird durch Verwendung der Makros deutlich lesbarer und vor 
allen Dingen wartbarer. Und das Beste daran ist, es ist ein 
Angebot/Idee. Jeder möge selber entscheiden ob die offensichtlichen 
Nachteile die Vorteile in seinem speziellen Fall überwiegen oder nicht.
Im Vergleich zu einer switch/case Statemachine kann ich nur Vorteile 
erkennen. Enums lösen das Problem der Umstellbarkeit, nicht der 
Übersicht.
Hier ein Auszug aus deinem Link:
1
...
2
typedef enum { NORDSUED_ROTGELB, NORDSUED_GRUEN, NORDSUED_GELB, ALLE_ROT_1,
3
               OSTWEST_ROTGELB, OSTWEST_GRUEN, OSTWEST_GELB, ALLE_ROT_2} state_t ;
4
...
5
   switch( state ) {
6
    case OSTWEST_GELB:
7
      Ampel1( ROT );
8
      Ampel2( GELB );
9
      state = ALLE_ROT_1;
10
      break;
11
 
12
    case ALLE_ROT_1:
13
      Ampel1( ROT );
14
      Ampel2( ROT );
15
      state = NORDSUED_ROTGELB;
16
      break;
17
...
Das sind pro Zustand 3 Zeilen für die Verwaltung und nur 2 Zeilen 
eigentlicher Code.
Zum Vergleich:
1
...
2
      NT_INIT();
3
...
4
      Ampel1( ROT );
5
      Ampel2( GELB );
6
      NT_BREAK();
7
8
      Ampel1( ROT );
9
      Ampel2( ROT );
10
      NT_BREAK();
11
...
Und es gibt dabei wie gesagt keinerlei Nachteile zum obigen Code.

von Thomas F. (tommf)


Lesenswert?

Da fehlt aber irgendwie die Zuordnung der Ausgangsaktionen zu den 
Zuständen und das Setzen der neuen Zustände, oder ist das woanders 
definiert?

von Stefan A. (king-crash)


Lesenswert?

Das ist die Funktionalität des Makros. Du musst keine expliziten 
Zustandsnamen vergeben. NT_BREAK mach dies im Hintergrund automatisch.
Probier einfach das Beispiel aus, es funktioniert natürlich auch auf dem 
PC.

von Thomas F. (tommf)


Lesenswert?

Sorry, aber wenn ich aus dem Code den Ablauf nicht verstehe, soll ich 
versuchen, dass mit dem Debugger rauszubekommen?

In deinem Code ist zu Zuordnung von "Ampel1( ROT ); Ampel2( GELB );" zum 
Zustand OSTWEST_GELB nicht ersichtlich. Woher soll ein Makro diese 
Information bekommen?

Beitrag #6514757 wurde von einem Moderator gelöscht.
von Stefan A. (king-crash)


Lesenswert?

Das Beispiel mit den Ampeln ist eine beispielhafte Anwendung meiner 
Makros auf den Code in Falks Link. 
https://www.mikrocontroller.net/articles/Statemachine#Reaktionen_auf_.C3.A4u.C3.9Fere_Ereignisse

Zum Testen und besseren Verständnis habe ich im ersten Post die Datei 
"test.c" angefügt.

Beitrag #6514764 wurde von einem Moderator gelöscht.
von Stefan A. (king-crash)


Lesenswert?

@ A. S.
_LINE_ geht sicherlich auch.
goto und setjump/longjump sollten gleichwertig verwendet werden können.
switch/case funktionieren wenn zusätzliche Klammerblöcke vorkommen nicht 
mehr.

von Thomas F. (tommf)


Lesenswert?

Sowas wie
1
  case OSTWEST_GRUEN:
2
      Ampel1( ROT );
3
      Ampel2( GRUEN );
4
      if( Induktionsschleife() ) {
5
        state = OSTWEST_GELB;
6
      }
7
      break;
im Ampelbeispiel lässt sich aber nicht machen, d.h. das ist nur für 
feste Abläufe gedacht?

von Stefan A. (king-crash)


Lesenswert?

Über NT_BREAK_EXPL(labelname) und NT_BREAK_THEN_LABEL(labelname) kann 
man zusätzlich auch eigene Labelnamen vergeben und anspringen.
In diesem fall also:
1
...
2
      NT_BREAK_EXPL(OSTWEST_GRUEN);
3
      Ampel1( ROT );
4
      Ampel2( GRUEN );
5
      if( Induktionsschleife() ) {
6
        NT_BREAK_THEN_LABEL(OSTWEST_GELB);
7
      }
8
...
Dann ist der Vorteil in diesem Fall zum größten Teil dahin.

Ich kann dafür aber auch sowas hier machen:
1
...
2
if(ADCWERT > 100)
3
{
4
  funktionA();
5
  NT_BREAK();
6
  funktionB();
7
  NT_BREAK();
8
}
9
else
10
{
11
  funktionC();
12
  NT_BREAK();
13
  funktionD();
14
  NT_BREAK();
15
}
16
...
Was mit switch/case nicht geht.

von Joachim B. (jar)


Lesenswert?

Stefan A. schrieb:
> Vor einiger Zeit habe ich für Mikrocontroller ein paar Makros zur
> Vereinfachung von Abläufen geschrieben.

Beitrag "Wartezeiten effektiv (Scheduler)"

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.