Hallo, ich habe mal ein Problem und komme nicht so richtig weiter. Auf einem Display steht ein Auswahlwahlmenü und ich möchte bei der Anweisung 3 zurück in die erste Schleife springen ohne aber goto zu benutzen. Wie geht das ? ein return(main) springt zu weit hoch. Danke für jede Instruktion. /*********************************************************************** ******* * Hauptprogramm ************************************************************************ ******/ main() { //Variablen . . . /*********************************************************************** ******/ //Initalisierung . . . . /*********************************************************************** *****/ //Hauptschleife /*********************************************************************** ****/ while (1) { while(...) { Anweisung 1 } while (...) { Anweisung 2 } while (...) { switch (zahl) { case 1: { while (...) {;} //nichts break; } case 2: { while (...) {;} //nichts break; } case 3: { while (...) {;} //nichts break; } case 4: { while (...) { Anweisung 3 } break; } } } } }
Hallo, eine möglichkeit währe, in der Anweisung 3-Schleife ein Variable zu sezten. z.B. abbruch=1;. Danach nutzt du den break;-Befehl um die while-Schleife bei Anweisung 3 zu abbrechen. In den anderen while-Schleifen musst du dann halt auch noch abbruch auswerten und gegebenfalls auch einen break durchführen. Gruß, Florian
Warum willst Du kein goto benutzen? Hat Dir jemand erzählt, daß ein Goto ganz schlechter Programmierstil ist? Bullshit: Benutze den Goto, daß ist die beste und übersichtlichste Möglichkeit. ciao, Stefan.
@Stefan, ja so ist es , in jedem Buch wird von goto abgeraten. Warum ist mir auch nicht ganz klar. Wenn ich mir mal den *.lst file ansehe ist der goto auch ein LJMP . Soll heissen, wenn ich mit Assembler schreiben würde, dann würde ich doch auch LJMP benutzen, oder irre ich? @Florian ich werde deinen Tipp mal ausprobieren. Danke Gruss René
Hallo Rene, dass mit der Vermeidung von dem goto ist schon ganz richtig. Aber weshalb gibt es den goto dann überhaupt? Um genau den oben beschriebenen Sprung zu machen. Wenn die Zielmarke gut benannt (Dokumentiert) und auch der Sprung noch mit einem Kommentar versehen wird, ist das sinnvoll. Alle anderen Lösungen sind auch nicht besser lesbar. Dies soll kein Freibrief für die Verwendung von goto sein, sondern nur in Einzelfällen macht der goto Sinn. Oryx
Sorry, aber ich versteh' die Frage nicht! Das ist doch ne While- Schleife, die fängt doch von allein wieder oben an! Oder wo soll jetzt das Sprungziel sein? Viele Grüße Johannes
Mir ist die Verwendung der vielen whiles ebenso schleierhaft.
Möglicherweise kann man das Problem anders anpacken, dann gibts das
goto-Problem nicht.
Aus der Ferne sieht das so aus, als könnte man jedes while (ausser
dem while(1)) ersatzlos streichen. Zwischenrein noch ein paar
Zuweisungen an die Variable zahl (die vielleicht besser 'state'
heissen sollte; und damit natürlich auch verbunden die Verwendung der
Variablen als Statevariable... 'zahl' würdest du dann in den
einzelnen States auswerten... aber das ist schon Kaffeesatzleserei) und
schon sieht das wie ne normale Statemaschine aus.
@René Man: Wie eine normale Statemaschine aufgebaut ist, ist die schon
klar, oder? (Klingt provokativ, ist es aber nicht!)
> ich möchte bei der Anweisung 3 zurück in die erste Schleife springen
das wäre dann mit der Zuweisung: state=1; erledigt.
----, (QuadDash).
goto kann vor allem bei Fehlerbehandlung sehr hilfreich sein. Hier zum Beispiel (Routine zum senden über TWI-Interface): -----8<---8<----8<----8<---- uint8_t n = 0; int rv = 0; restart: if (n++ >= 250) return -1; begin: /* * SEND START CONDITION */ TWCR = _BV (TWINT) | _BV (TWSTA) | _BV (TWEN); while (!(TWCR & _BV (TWINT))); switch (TW_STATUS) { case TW_REP_START: case TW_START: break; case TW_MT_ARB_LOST: goto begin; default: return -1; } /* * SEND SLAVE ADDRESS + WRITE */ TWDR = SERVO | TW_WRITE; TWCR = _BV (TWINT) | _BV (TWEN); while (!(TWCR & _BV (TWINT))); switch (TW_STATUS) { case TW_MT_SLA_ACK: break; case TW_MT_SLA_NACK: goto restart; case TW_MT_ARB_LOST: goto begin; default: goto error; } /* * SEND REGISTER ADDRESS */ TWDR = reg; TWCR = _BV (TWINT) | _BV (TWEN); while (!(TWCR & _BV (TWINT))); switch (TW_STATUS) { case TW_MT_DATA_ACK: break; case TW_MT_DATA_NACK: goto quit; case TW_MT_ARB_LOST: goto begin; default: goto error; } /* * SEND REGISTER VALUE */ TWDR = value; TWCR = _BV(TWINT) | _BV(TWEN); while (!(TWCR & _BV(TWINT))); switch (TW_STATUS) { case TW_MT_DATA_NACK: goto error; case TW_MT_DATA_ACK: break; default: goto error; } quit: /* * SEND STOP CONDITION */ TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); return rv; error: rv = -1; goto quit; -----8<---8<----8<----8<---- IMHO: Gotos sparsam verwenden, allerdings sollte man nicht krampfhaft versuchen einen anderen, vermeintlich besseren, Weg zu finden. In dem Vorschlag von Florian z.B. würde das ein weiteres Byte im RAM-Speicher und viele Bedingte Assembler-Anweisungen kosten. ciao, Stefan. P.S. Mich würde mal ein Zitat aus einen Buch interessieren, wo von Gotos abgeraten wird.
@QuadDash ich habe keine Ahnung was du mit einer Statemaschine meinst. Aber vielleicht mal zur Aufgabenstellung: Ein Display, zwei Tasten, Uhr und Tempfühler Im Normalmodus wird die Temp und Uhrzeit angezeigt. Bei Druck auf Taste [Menü] erscheint ein 4-zeiliges Menü Bei weiteren Druck auf [Menü]kann man den Cusor an die gewünschte Stelle setzen und mit der anderen Taste bestätigen . Somit geht es z.b. ab der case 1: Marke zur Eingabe der Uhrzeit, Speichern und senden über I2C Bus... Das gleiche für Timer-Schaltzeit, und Temp.Alarm. case4: ist der Menüpunkt zurück zur Normalanzeige. Und nun kommt eine Statemaschine ??? wie jetzt?
@Stefan Das Buch von M. Baldischweiler " Der Keil C51 Compiler" Teil1. Es wird aber auch in anderen C-Büchern (nicht nur für µP) darauf hingewiesen. Gruss René
Hi Eine einfache state Maschine: int main(void) { unsigned char state=0; while(1) { switch(state) { case 0: do_something(); if(TASTE1) state=1; else if(TASTE2) state=2; break; case 1: do_something_other(); if(TASTE1) state=1; else if(TASTE2) state=3; else state=0; : : : : } } } Eine Menüstruktur kann so geschickt implementiert werden. So kannst du ohne Probleme von ganz oben (state 213) problemlos wieder ganz runter springen indem du state einfach auf 0 setzt. Matthias
#define IDLE 0 #define MENUE_1 1 #define MENUE_2 2 while(1) { switch (state) { case IDLE: if (Taste==Menütaste) state=MENUE_1; break; case MENUE_1: // gibt Menübildschirm aus; Cursor auf 1.Zeile anzeigen MenuAnzeigen(CursorAufZeile1); if (Taste==Cancel) LCD_Normalmodus(); /* Temp. und Uhrzeit anzeigen */ state=IDLE; if (Taste==runter) state=MENUE_2; break; case MENUE_2: // gibt Menübildschirm aus; Cursor auf 1.Zeile anzeigen MenuAnzeigen(CursorAufZeile2); if (Taste==runter) state=MENUE_3; if (Taste==hoch) state=MENUE_1; if (Taste==Plus) Uhr_Minuten++; break; ... } Prinzip: Wenn sich "was getan" hat (z.B. Taste gedrückt), dann wird die entsprechende Aktion veranlasst und der Variablen 'state' ein neuer Wert zugewiesen. Beim nächsten while-Durchlauf verzweigt die switch-Anweisung dann in den neuen State. Dieser wird solange wiederholt(!), bis sich wieder "was getan" hat. D.h. wenn sich "nix" tut, dann wird dieser State ewig wiederholt - macht ja nix. Du kannst im Anschluß an diese Statemaschine natürlich noch eine weitere Statemaschine für eine andere Aufgabe ausführen. Oder die einzelnen States wieder in Statemaschinen unterteilen. So kann z.B. in Statemaschine1 im State WARTEN_AUF_UART case WARTEN_AUF_UART: if (Zeichen_ueber_UART_eingetroffen) { tuwas(); if (AnzahlZeichen==MAX) state=STATE_AUSWERTUNG; } ... Statemaschine 2: switch... case WARTEN_AUF_TASTE: ... praktisch "gewartet" werden, bis über UART ein Zeichen eingetroffen ist und quasi gleichzeitig noch auf Tastendruck reagiert werden, ohne das eines davon verpasst wird, weil der Prozessor aktiv in einer (kleinen) while-Schleife warten muß. Negativbeispiel für gleiche Anwendung: while(ZeichenÜberUARTeingetroffen()) { } ; while(TasteGedrückt()) { } ; Kapiert oder hab ich mich zu kompliziert ausgedrückt? (Natürlich gibts elegantere Möglichkeiten ein Menüsystem aufzubauen, aber bevor nicht klar ist was ne Statemaschine ist, würd ich das mit den Zeigern noch n bisschen rausschieben ;) ). Ich seh grad Matwei hat auch schon ne Statemaschine gepostet... trotzdem werd ich mein Getippe jetzt nicht mehr löschen :) ----, (QuadDash).
jo, das hab ich kapiert. Ich werde meine while schleifen dann mal umbauen. Danke für die Infos. René
@Stefan May: Nix dagegen, daß Du meinen Code zitierst, aber warum hast Du eigentlich MAX_ITER durch die harte Zahl 250 ersetzt?
Hallo Leute, ich habe die vielen verschaltelten while´s durch die StateMachine ersetzt und bin begeistert. Ich habe jetzt viel mehr Überblick... ... so macht´s doch Spass... Danke und schönen Tag. René
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.