Minimalistisches kooperatives Multitasking.

OP #6513667
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
Angehängte Dateien:
(Firma: @home) #6513911
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!?
OP #6514603
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.
#6514625
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!
#6514635
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".
OP #6514688
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.
#6514751
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.
Beitrag #6514764 wurde von einem Moderator gelöscht.
OP #6514882
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren