Hallo, ich arbeite zur Zeit an meinem ersten Projekt. Werde bald mit der Programmierung einer Menüführung mittels Drehencoder und LCD beginnen. Hierzu vorher noch eine Grundsatzfrage. Zur Erläuterung: Die Abläufe, die bearbeitet werden sollen, sollen über das Menü ausgewählt werden. Innerhalb des Programms sind sie als Funktionen definiert. Ich möchte also programmieren: Menü Option1 = Funktion1 Option2 = Funktion2 etc. Der normale Programmablauf soll durch das Auswählen des Menüs immer beendet werden. Beispiel: Funktion1 wurde gewählt und läuft in einer Endlosschleife. Aktuell ist PortA == 0x01. Wenn ich jetzt zurück in das Menü möchte, wird der Ablauf unterbrochen und ich springe in das Menü. Wie stelle ich jedoch sicher, dass alle Ausgänge (das Display ausgenommen) wieder in ihren Ursprungszustand (nicht gesetzt) zurückkehren? Je nachdem welche Funktion wann unterbrochen wird sind die gesetzten Ausgänge andere. Ich hoffe meine Frage ist verständlich und ihr könnt mir hier mal auf die Sprünge helfen :-) Gruß Max Nun meine Frage:
Du könntest Du jeweils zu den Funktionen Funktion1 und Funktion2 etc. eine "Abschlussfunktion" Abschluss1 und Abschluss2 basteln und einen Zeiger darauf in einer Variablen speichern. Sobald im Menue eine andere Funktion aktiviert wird, rufst Du erstmal die Abschlussfunktion auf und setzt dann vor der Aktivierung der anderen Funktion auch die Variable für die zugeordnete Abschlussfunktion. Noch ein Tip zur Abarbeitung in main: Es ist sinnvoll im Menü entweder ein Flag oder einen Zeiger auf eine Funktion zu verwenden. In jedem Fall muss die Funktion so gestaltet werden, das sie immer nur kleine Schritte macht und danach die Kontrolle wieder an main zurückgibt (Stichwort: State-Machine, Zustandsautomat) Soetwas wie ein MenuFlag kontrolliert auch ob, das Menü aktiviert wurde.
1 | int * (Funktion)(); |
2 | |
3 | main () |
4 | while (1) { |
5 | Funktion (); |
6 | if (MenuFlag) { |
7 | ...
|
8 | Funktion = Funktion1(); |
9 | ...
|
10 | }
|
11 | }
|
12 | }
|
Ungefähr so.
Hallo Max. Ich würde es so machen: 4 C-Files: model.c (enthält nur Variablen)
1 | int ausgewaehltes_menu; |
controller.c (Tastenabfragen)
1 | void controller_task() { |
2 | if (taste1) |
3 | ausgewaehltes_menu = HAUPTMENU; |
4 | ...
|
5 | }
|
view.c (Die Darstellung);
1 | void view_task() { |
2 | if (ausgewaehltes_menu = HAUPTMENU) { |
3 | Zeichne_Das_Hauptmenu(); |
4 | }
|
5 | ...
|
6 | }
|
und schließlich das Hauptprogramm: main.c
1 | Init(); |
2 | while (1) { |
3 | controller_task(); |
4 | view_task(); |
5 | }
|
Hallo, danke für die Tipps! @ Achim_42: Gute Idee, das macht es übersichtlicher. @ Umsonst: Angenommen Funktion1 schaltet PORTA in zwei verschiedene Zustände und Funktion2 in 4 verschiedene Zustände. Dann müsste ich schon insgesamt 6 Zustandsvariablen mit den entsprechenden "Löschfunktionen" bereit halten. Macht man das wirklich so? Klar, kann ich machen - kommt mir nur so umständlich vor :) Gruß Max
>@ Umsonst: Angenommen Funktion1 schaltet PORTA in zwei verschiedene >Zustände und Funktion2 in 4 verschiedene Zustände. Dann müsste ich schon >insgesamt 6 Zustandsvariablen mit den entsprechenden "Löschfunktionen" >bereit halten. >Macht man das wirklich so? Klar, kann ich machen - kommt mir nur so >umständlich vor :) Ich bin nicht sicher ob ich Dich oder Du mich richtig verstanden hast. Schalte Funktion1 PORTA oder welche und wieviele Ports auch immer in irgendwelche Zustände. OK. Definiert sei nun genau ein_ Zustand aller Ports der bei _verlassen von Funktion1 bestehen soll, dann sezt die Abschlussfunktion (löschen würde ich hier nicht als Begriff verwenden, denn was ist ein gelöschter Port?) genau diesen einen Zustand.
Möglicherweise ist Deine unausgesprochen Frage, diese: >Es werden von Funktion1 PORTA in zwei verschiedene >Zustände geschaltet und von Funktion2 in 4 verschiedene Zustände. Wenn ich also diese wieder rückgängig mache, muss ich mir merken welche Zustände ich gesetzt habe. Falls ich das nicht so mache, dann setze ich Portpins zurück, die ja garnicht gesetzt sind. Deswegen merke ich mir die Zustände, damit ich das vermeide. Ich hoffe meine Vermutung Deines Gedankenganges ist richtig. Falls nicht, bitte mit Butterkugeln werfen. Frühstücke gleich. Die Antwort ist: In der Regel spielt es in Bezug auf die Funktion, keine Rolle, ob Du nun einen Port insgesamt auf einen Zustand setzt oder nur einzelne Pins. Zeitlich günstiger ist es, alle Portpins gemeinsam auf einen Sollzustand zu setzen (Siehe Bitoperationen). Allerdings gibt es den Fall, in dem Du beim Abschalten aus einem der beiden Zustände die in Funktion1, bzw. einem der vier Fälle in Funktion4 eine bestimmte Reihenfolge einhalten musst. (Weil z.B. irgendeine mechanische reps. motorisierte Technik dranhäng). Dann ist das natürlich wichtiger und muss so gemacht werden. Aber das Problem hättest Du dann bei jeder Lösung und es müsste mit Zustandsvariabeln gelöst werden. Wobei der Port selbst dann als Variable herhalten könnte, falls sein Zustand sich ein-eindeutig auf den Zustand der gesteuerten Mimik abbilden lässt. OK?
>@ Umsonst: Angenommen Funktion1 schaltet PORTA in zwei verschiedene >Zustände und Funktion2 in 4 verschiedene Zustände. Dann müsste ich schon >insgesamt 6 Zustandsvariablen mit den entsprechenden "Löschfunktionen" >bereit halten. Die Rechnung lässt sich günstiger aufmachen. Funktion1 -> Eine_ Zustandsvariable mit _zwei Zuständen Funktion2 -> Eine_ Zustandsvariable mit _vier Zuständen Nicht aber pro Zustand eine Variable.
Ich bin nicht sicher, ob wir uns alle richtig verstehen. Ich mache den Fall einfach mal konkreter: Alle Funktionen durchlaufen immer mehrere Phasen, in denen sie unterschiedliche Ports setzen. Wenn ich zurück in das Menü möchte, muss natürlich egal sein, in welcher Phase sich die Funktion gerade gefindet. Die Phasen können auch mehrere Minuten lang sein. FunktionX Hat zwei Zustände. FunktionY Hat drei Zustände. Ich habe euch jetzt so verstanden: MerkerFunktionX (int) ist entweder =1 (Zustand 1) oder =2 (Zustand 2) MerkerFunktionY (int) ist entweder =1 (Zustand 1), =2 (Zustand 2) oder =3 (Zustand 3). Wenn ich zurück in das Menü möchte, frage ich ab, welcher Merker gesetzt ist. Wenn MerkerFunktionX==2 ist, weiß ich, dass ich aus FunktionX komme, die sich zuvor in Zustand 2 befunden hat. Ich setze also die Ports zurück, die in Zustand 2 gesetzt sind. Besser? ;-) Gruß Max
:
Bearbeitet durch User
>Ich bin nicht sicher, ob wir uns alle richtig verstehen. Ich mache den >Fall einfach mal konkreter: Du musst daran denken, dass sich die Antworten hier teilweise auf verschiedene Aspekte Deines Problems beziehen und auch unterschiedliche Ansätze verfolgen. Das macht es sicher ein wenig schwierig für Dich. Es ist also besser wenn Du den Beitrag zitierst, auf den Du Dich beziehst. Das dazu. :-) >Wenn ich zurück in das Menü möchte, muss natürlich egal sein, in welcher Phase sich die Funktion gerade gefindet. Es ist überhaupt die Frage ob es so etwas wie ein "zurück" überhaupt gibt. Es ist sinnvoll zwischen dem, was der Benutzer wahrnimmt und dem was das Programm tut. In Bezug auf letzeres ist die übliche Technik die, das über Flags in der Funktion main bwz. in einer untergeordneten Funktion entschieden wird, was als nächstes geschieht. Das Paradigma ist, das einer "virtuellen" Nebenläufigkeit. Das ist der eine Aspekt. Auf den bezieht sich die Anregung mit den Flags. Es ist weiter überhaupt die Frage, ob es für Deine konkrete Anwendung überhaupt notwendig ist, dass bekannt ist, in welcher Phase sich eine "unterbrochene" Funktion gerade befindet, das müsstest Du mal beantworten. Das ist dann der Fall, wenn Du je nach Zustand auf unterschiedlichen Wegen zu dem "neutralen" Zustand der Ports zurückkehren musst. Falls nicht, dann kannst Du, egal in welchem Zustand die Funktion ist, die Ports immer auf dieselbe Weise zurücksetzen. Es scheint mir, als wenn Du die Anregung mit den Flags auch auf dieses Thema beziehst. Aber das ist nicht zwingend notwendig. Jedenfalls wären es aber ganz andere Flags als die für die Steuerung der "Nebenläufigkeit".
Umsonst schrieb: > Es > ist also besser wenn Du den Beitrag zitierst, auf den Du Dich beziehst. Wird gemacht ;-) Umsonst schrieb: > Du musst daran denken, dass sich die Antworten hier teilweise auf > verschiedene Aspekte Deines Problems beziehen und auch unterschiedliche > Ansätze verfolgen. Das macht es sicher ein wenig schwierig für Dich. Die Hinweise zum allgemeinen Aufbau habe ich zur Kenntniss benommen. Ich denke ich habe "nur" noch das Problem der "Nebenläufigkeit". Umsonst schrieb: > Es ist weiter überhaupt die Frage, ob es für Deine konkrete Anwendung > überhaupt notwendig ist, dass bekannt ist, in welcher Phase sich eine > "unterbrochene" Funktion gerade befindet, das müsstest Du mal > beantworten. Ja ist es. Der jeweilige Zustand wird dem Anwender durch LEDs signalisiert. Kehrt dieser in das Auswahlmenü zurück, sollten die entsprechenden "Zustands-LEDs" wieder zurück gesetzt werden. Gruß Max
Das Ziel muss immer sein, Funktionalitäten zu trennen, damit das Ganze leichter beherrschbar wird. Eine andere Sache ist, das es bestimmte Entwicklungsmuster gibt. Bisjetzt sehe ich hier vier verschiedene Muster. 1. Scheinbare Nebenläufigkeit mit in main ausgewerteten Flags. 2. Statemachines um Anwendungsfunktionen in kleine Unterabschnitte zu gliedern, die es erlauben, das main häufig die anderen Flags auswerten kann. 3. Zustandsvariablen die es erlauben aus beliebigen Zuständen der Anwendungsfunktion in eine definierten Endzustand zu kommen. 4. Eine (von mir) so genannte Abschlussfunktion die den unter 3. genannten Endzustand herstellt. Meine erste Anwort hat Punkt 4. als Lösung für die Frage: >Wie stelle ich jedoch sicher, dass alle Ausgänge (das Display >ausgenommen) >wieder in ihren Ursprungszustand (nicht gesetzt) >zurückkehren? angeregt. Das hat mit den anderen Punkten nichts zu tun. In der ersten Antwort habe ich aber auch auf Punkt 1 und Punkt 2 hingewiesen, damit Du gleichzeitig das Menü abhandlen kannst aber auch Deine Anwendungsfunktion ablaufen kann. Punkt 3. ist dazugekommen weil Du schriebst: >Angenommen Funktion1 schaltet PORTA in zwei verschiedene >Zustände und Funktion2 in 4 verschiedene Zustände. Dann müsste ich schon >insgesamt 6 Zustandsvariablen mit den entsprechenden "Löschfunktionen" >bereit halten. Das ist ein Thema, das mit den übrigen Punkten nur am Rande zu tun hat. Ich hoffe das ist jetzt klarer geworden. Du musst daran denken, das Du in grösseren Projekten, fast immer mehrere Paradigmen (oder auch Entwicklungsmuster) gleichzeitig und kombiniert anwenden wirst. Das sind keine Widersprüche. Du musst Dich nicht für eines entscheiden. Das spielt alles eine Rolle. Konzentrieren wir uns vielleicht erstmal auf Deine Ausgangsfrage, damit es einfacher wird. >Wie stelle ich jedoch sicher, dass alle Ausgänge (das Display >ausgenommen) >wieder in ihren Ursprungszustand (nicht gesetzt) >zurückkehren? Ist Dir da soweit klar wie ich das meine? Beschreibe das doch mal mit Deinen eigenen Worten.
Umsonst schrieb: > 1. Scheinbare Nebenläufigkeit mit in main ausgewerteten Flags. > 2. Statemachines um Anwendungsfunktionen in kleine Unterabschnitte zu > gliedern, die es erlauben, das main häufig die anderen Flags auswerten > kann. > 3. Zustandsvariablen die es erlauben aus beliebigen Zuständen der > Anwendungsfunktion in eine definierten Endzustand zu kommen. > 4. Eine (von mir) so genannte Abschlussfunktion die den unter 3. > genannten Endzustand herstellt. Umsonst schrieb: > Du musst daran denken, das Du in grösseren Projekten, fast immer mehrere > Paradigmen (oder auch Entwicklungsmuster) gleichzeitig und kombiniert > anwenden wirst. Das sind keine Widersprüche. Du hast Recht, ich kombiniere verschiedene Entwicklungsmuster. Ganz sicher aktuell 3 und in Zukunft 1 und 4. Umsonst schrieb: > Ist Dir da soweit klar wie ich das meine? Beschreibe das doch mal mit > Deinen eigenen Worten. Ja, ich denke mir ist das etwas klarer geworden. Ich werde nun mittels der Zustandsmerker entsprechende Abschlussfunktionen schreiben. Das teste ich mal in einfach (Taster 1 startet Funktion 1, Taster 2 startet Funktion 2 und mit Taster 0 lassen sich die Funktion verlassen. Alle Ausgänge werden dadurch in einen definierten Zustand gesetzt [quasi das spätere Auswahlmenü]). Wenn hier Probleme auftreten, melde ich mich wieder. Falls ich noch einen Denkfehler habe, hilft mir die Umsetzung sicher dabei :) Vielen Dank schonmal für die bisherige Hilfe! Gruß Max
>Ich denke ich habe "nur" noch das Problem der "Nebenläufigkeit". OK. Lass uns das für einen Moment auf später verschieben. Umsonst schrieb: >> Es ist weiter überhaupt die Frage, ob es für Deine konkrete Anwendung >> überhaupt notwendig ist, dass bekannt ist, in welcher Phase sich eine >> "unterbrochene" Funktion gerade befindet, das müsstest Du mal >> beantworten. >Ja ist es. Der jeweilige Zustand wird dem Anwender durch LEDs >signalisiert. Kehrt dieser in das Auswahlmenü zurück, sollten die >entsprechenden "Zustands-LEDs" wieder zurück gesetzt werden. Hier verbindest Du in Deiner Antwort die Anwendungsfunktion mit dem Menü, resp. der Nebenläufigkeit. Darum geht es aber bei meiner Frage nicht. Meine Frage bezieht sich auf etwas Anderes. Nimm einmal an, Du hättest nur eine einzige Funktion, irgendeinen sich immer wiederholenden Ablauf mit ein paar Verzweigungen. Kein Menü oder sonstwas. Das einzige, was man (z.B. per Taster) auslösten kann, ist ein "Anhalten". Die Frage ist, ob man einfach, egal in welchem Zustand sich die Funktion befindet, alles auf Null schalten kann oder nicht. Ein Beispiel: Es handelt sich um eine Lichtschaltung. Im Regelfall werden Lampen in einer bestimmten Reihenfolge umgeschaltet. Das Abschalten bestünde darin, einfach alle Lampen auszuschalten. In diesem Fall spielt es keine Rolle in welcher Phase sich die Funktion gerade befindet. Ein weiteres Beispiel: Es handelt sich um eine Maschinensteuerung. Teil davon ist, das ein 12-Tonnen-Gewicht angehoben wird. Dann greift eine 5 Mikrometer Pinzette darunter und dreht an irgendwas. Stoppt man zu einem beliebigen Zeitpunkt, dann befindet sich möglicherweise die Pinzette gerade unter dem 12-Tonnen-Gewicht. Ein "Abschalten" würde sinnvoll so aussehen, das man erst noch die Pinzette zurückzieht und die 12-Tonnen absenkt. In diesem Fall spielt es also sehr wohl eine Rollte, in welcher Phase sich die Funktion gerade befindet. Ist das soweit klar? Die Frage lautet also in Bezug auf Dein konkretes Projekt, ob es eher dem ersten Beispiel oder dem zweiten entspricht.
>Alle Ausgänge werden dadurch in einen definierten Zustand gesetzt [quasi >das
spätere Auswahlmenü]).
Nein! Das Menü hat mit der Anwendung programmtechnisch nichts zu tun!
Schade das Du das Thema hier abbrichst.
Viel Erfolg noch.
Umsonst schrieb: > Schade das Du das Thema hier abbrichst. Quatsch, wir haben doch quasi zur selben Zeit gepostet! So war das nicht gemeint :) Umsonst schrieb: > Nein! Das Menü hat mit der Anwendung programmtechnisch nichts zu tun! Okay. Umsonst schrieb: > Nimm einmal an, Du hättest nur eine einzige Funktion, irgendeinen sich > immer wiederholenden Ablauf mit ein paar Verzweigungen. > Kein Menü oder sonstwas. Das einzige, was man (z.B. per Taster) > auslösten kann, ist ein "Anhalten". > Die Frage ist, ob man einfach, egal in welchem Zustand sich die Funktion > befindet, alles auf Null schalten kann oder nicht. JETZT verstehe ich was du meinst! Umsonst schrieb: > Ein Beispiel: Es handelt sich um eine Lichtschaltung. Im Regelfall > werden Lampen in einer bestimmten Reihenfolge umgeschaltet. Das > Abschalten bestünde darin, einfach alle Lampen auszuschalten. In diesem > Fall spielt es keine Rolle in welcher Phase sich die Funktion gerade > befindet. Ja, das ist völlig egal, da hast du Recht! Ich war so auf den Zustand aus, da ich dachte, ich wüsste dann genau welche Ausgänge gesetzt sind, damit ich diese zurück setzen kann! In meinem Fall reicht es aus, wenn alle LED-Ausgänge auf Null geschaltet werden. Super erklärt! Gruß Max
:
Bearbeitet durch User
Ich habe so das Gefühl, hier wird's Zeit für den magischen Satz "Also eigentlich will ich folgendes machen" :-) Ich würde das Problem in zwei Schritten lösen: (1) Das Menüsystem so implementieren, dass es völlig unabhängig (!) vom sonstigen Ablauf "stand-alone" funktioniert. D. h. so, dass ich das Menü vollumfänglich aufrufen und nach Herzenslust beliebig lange darin navigieren und es beenden kann ohne dass das irgendeine Auswirkung auf irgendwelche anderen Teile des Programms hat. Alles muss also währenddessen so weiterlaufen, als ob die Menüaktivität gar nicht existiert. Nebenläufigkeit ist das Stichwort dazu. (2) Die "Aktionsstellen" im Menücode finden und dorthin die Funktionsaufrufe schreiben, die die Zustandsmaschinen, welche die LEDs oder sonstwas schalten, startet/stoppt/in bestimmte States setzt/whatever. Wo diese Aktionsstellen sind, ergibt sich praktisch von selbst.
@ Max Gut. Dir "Nebenläufigkeit" erreichst Du, in dem Du die Abfolge von Menu-System und Anwendungsfunktion nicht fest programmierst, sondern in dem Du Flags benutzt um jeweils auf innere oder äussere Ereignisse zu reagieren. Dazu brauchst Du sehr wahrscheinlich auch das oben genannte Muster 2. Statemachine. (Selbst wenn Du es in diesem Fall nicht zwingend brauchst, wäre es eine gute Gelegenheit es mal anzuwenden um damit Erfahrungen zu sammeln). Das hängt damit zusammen, ob und in welchem Maße das Menüsystem und die Anwendungsfunktion längere Zeit braucht um bestimmte Aktionen auszuführen. Das aktualisieren des Displays z.B. dauert möglicherweise bis in die Millisekunden hinein. Deine Anwendung mag vielleicht auf ein gewisses Ereignis (z.B. eine Lichtschranke) warten müssen. Gibt es dazu irgendwelche Fragen?
Um mal auf den Aspekt 'Menü' mehr einzugehen versuche ich mal konzeptionell zu beschreiben, wie ich das mal gelöst hatte: Menüeinträge Für Menüeinträge hatte ich mir eine Datenstruktur (struct) erstellt, die insbesondere den Text und einen Pointer auf die zugehörige Funktion beinhaltet. Solche Menüeinträge hatte ich dann als fette Tabelle im Flash Rom gehalten. (Für meine verwendete AVR Plattform gab's da einiges bzgl. Zugriff aufs Flash zu beachten, insbesondere was Strings betrifft.) Navigation Eine Indexvariable auf einen Menüeintrag der Tabelle repräsentiert welchen Menüeintrag der User gerade angewählt hat. Diese Indexvariable wird vom Rotaryencoder (oder up/down Buttons, o.ä) gesteuert. Darstellung Eine Funktion mit einem Index auf einen Menüeintrag als Parameter liest aus der Tabelle der Menüeinträge den darzustellenden Text und schreibt ihn auf das LCD. Ausführung der Funktion des Menüeintrags Wählt der User per Tastendruck (z.B. Pushbutton des Rotaryencoders) den Eintrag aus, wird zunächst die Menüschleife verlassen. Welchen Menüeintrag der User dabei gewählt hatte, steht noch in der Indexvariable auf den Menüeintrag. Mit diesem Index holt man sich nun den Funktionspointer aus der Tabelle mit Menüeinträgen und führt sie aus. Mit steigenden Anforderungen sind bei mir sind die Structs mit den Menüeinträgen ziemlich gewachsen und enthalten nun auch noch: - Indizes auf Vater- / Untermenüeinträge für verschachtelte Menüs - Art des Menüpunkts (z.B. Wert einstellen Aktion ausführen zum Vater- oder Untermenü navigieren) - Indizes auf Variablen, die durch einen Menüpunkt verändert werden sollen. (z.B. Helligkeit der Hintergrundbeleuchtung) - Min / Max Werte dafür - Pointer auf Funktionen, die beim Betreten / Verlassen eines Menüpunkts oder beim Verändern eines Wertes ausgeführt werden. - Pointer auf eine Worker Funktion um nebenläufige Aufgaben während der Menünavigation ausführen zu können. Mittlerweile reicht mir ein einfacher Index auf den gewählten Menüeintrag nicht mehr aus. Es muss auch noch ein Zustand verwaltet werden (z.B. User stellt Parameter ein / User navigiert durchs Menü) Mittlerweile ist meine Menülösung recht mächtig geworden. Der Hauptschwachpunkt ist dabei die Verwaltung der Verweise der Menüpunkte untereinander bei verschachtelten Menüs. Fügt man irgendwo Menüpunkte ein oder löscht sie, verändern sich ja die Indizes der nachfolgenden Menüpunkte. Da muss man alle Verweise händisch wieder anpassen, was nervig und fehlerträchtig ist.
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.