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
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 ...".
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!?
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.
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!
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".
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:
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.
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?
@ 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.
Ü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: