Forum: Mikrocontroller und Digitale Elektronik Sprung ohne goto


von René Man (Gast)


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;
                      }
             }

          }
 }
}

von Florian Pfanner (Gast)


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

von Stefan May (Gast)


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.

von René Man (Gast)


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é

von Oryx (Gast)


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

von Johannes Raschke (Gast)


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

von QuadDash (Gast)


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).

von Stefan May (Gast)


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.

von René Man (Gast)


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?

von René Man (Gast)


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é

von Matthias (Gast)


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

von QuadDash (Gast)


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).

von René Man (Gast)


Lesenswert?

jo, das hab ich kapiert.
Ich werde meine while schleifen dann mal umbauen.

Danke für die Infos.

René

von Jörg Wunsch (Gast)


Lesenswert?

@Stefan May:

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

von René Man (Gast)


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é

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
Noch kein Account? Hier anmelden.