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
Ohje! Biete Lösung, suche Problem. Da nehm ich lieber ne Handvoll Old school Statemachines und muss keinerlei Handstände machen.
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".
:
Bearbeitet durch User
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.
Da fehlt aber irgendwie die Zuordnung der Ausgangsaktionen zu den Zuständen und das Setzen der neuen Zustände, oder ist das woanders definiert?
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?
Beitrag #6514757 wurde von einem Moderator gelöscht.
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.
@ 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.
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?
Ü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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.