mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Sprung ohne goto


Autor: René Man (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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;
                      }
             }

          }
 }
}

Autor: Florian Pfanner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Stefan May (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: René Man (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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é

Autor: Oryx (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Johannes Raschke (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: QuadDash (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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).

Autor: Stefan May (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: René Man (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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?

Autor: René Man (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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é

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: QuadDash (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#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).

Autor: René Man (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jo, das hab ich kapiert.
Ich werde meine while schleifen dann mal umbauen.

Danke für die Infos.

René

Autor: Jörg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Stefan May:

Nix dagegen, daß Du meinen Code zitierst, aber warum hast Du
eigentlich MAX_ITER durch die harte Zahl 250 ersetzt?

Autor: René Man (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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é

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.