So verlassen Sie die if-Anweisung, die sich in der for-Schleife befindet, und kehren zur void-Schleife in Arduino zurück

Mein Code:

#define MAX 10
const int LED1 = 2;
const int LED2 = 8;
const int LED3 = 4;
const int LED4 = 5;
int val;
int array[MAX];
int counter = 0;
int i;
int old_b = 0;
int error;

void setup() { 
 Serial.begin(9600);
 pinMode(A5, INPUT_PULLUP); 
}

int readButtons(int pin) {
 int b,c;
 c=analogRead(pin);
 Serial.print("analogRead = ");
 Serial.println(c); 
 delay(100);
 if (c>1015) b = 0; 
 else if (c>70 && c<76) b = 1; 
 else if (c<128 && c>122) b = 2; 
 else if (c>169 && c<175) b = 3; 
 else if (c>209 && c<217) b = 4;
 else if (c>247 && c<256) b = 5;
 else if (c>280 && c<291) b = 6;
 else b = 0;
 if (b == old_b) {
  return 0;
  old_b = b;
 } else {
   return b;
   stari_b = b;
 }
}

void loop() {
 while ((val = readButtons(5)) != 5) {
  if (val == 1) {
   array[counter] = 1;
   counter++;
   Serial.print("In ");
   Serial.print(counter);
   Serial.print(" saving ");
   Serial.println("1");
   delay(500);
  } else if (val == 2) {
     array[counter] = 2;
     counter++;
     Serial.print("In ");
     Serial.print(counter);
     Serial.print(" saving ");
     Serial.println("2");
     delay(500);
  } else if (val == 3) {
     array[counter] = 3;
     counter++;
     Serial.print("In ");
     Serial.print(counter);
     Serial.print(" saving ");
     Serial.println("3");
     delay(500);
  } else if (val == 4) {
     array[counter] = 4;
     counter++;
     Serial.print("In ");
     Serial.print(counter);
     Serial.print(" saving ");
     Serial.println("4");
     delay(500);
  }
  if (counter == MAX) {
   counter = 0;
  }
 }

 for (i = 0; i < MAX; i++) {
  if (array[i] == 1) {
     digitalWrite(LED3, HIGH);
     digitalWrite(LED4, HIGH);
     delay(1000);
     digitalWrite(LED3, LOW);
     digitalWrite(LED4, LOW);
     delay(1000);
     Serial.print("Executing ");
     Serial.print(i);
     Serial.print(" ");
     Serial.println(array[i]);
     delay(500);
  }
  if (array[i] == 2) {
     digitalWrite(LED1, HIGH);
     digitalWrite (LED2, HIGH);
     delay(1000);
     digitalWrite(LED1, LOW);
     digitalWrite(LED2,LOW);
     delay(1000);
     Serial.print("Executing ");
     Serial.print(i);
     Serial.print(" ");
     Serial.println(array[i]);
     delay(500);
  }
  if (array[i] == 3) {
     digitalWrite(LED2, HIGH);
     digitalWrite(LED4, HIGH);
     delay(1000);
     digitalWrite(LED2, LOW);
     digitalWrite(LED4, LOW);
     delay(1000);
     Serial.print("Executing ");
     Serial.print(i);
     Serial.print(" ");
     Serial.println(array[i]);
     delay(500);
  }
  if (array[i] == 4) {
     digitalWrite(LED1, HIGH);
     digitalWrite(LED3, HIGH);
     delay(1000);
     digitalWrite(LED1, LOW);
     digitalWrite(LED3, LOW);
     Serial.print("Executing ");
     Serial.print(i);
     Serial.print(" ");
     Serial.println(array[i]);
     delay(500);
  }

 } 
}

Also habe ich Tasten so verbunden , außer ich habe sie 5. Wenn eine der ersten vier gedrückt wird, speichert Sketch 1,2,3 oder 4, um anzugeben, welche Tasten gedrückt wurden, und speichert es in einem Integer-Array. Wenn die fünfte Taste (die "GO"-Taste) gedrückt wird, endet die Schleife und startet die for-Schleife, die entsprechend den gedrückten Tasten LEDs blinkt. Ich möchte das machen, während Arduino LEDs blinkt, das heißt, während die for-Schleife ausgeführt wird, wenn zu irgendeinem Zeitpunkt die fünften "GO" -Tasten gedrückt werden, sollte es aufhören, LEDs zu blinken und das Programm von Anfang an zu starten ( void loop { ... } ), das heißt wieder auf das Drücken der ersten vier Tasten warten.


Ich habe es geschafft, dies zu machen:

#define MAX 50
const int LED1 = 2;
const int LED2 = 3;
const int LED3 = 4;
const int LED4 = 5;
int array[MAX];
int old_b = 0;
int val;
int counter = 0;
int i;
int temp;
int L1;
int L2;

void setup () {
  pinMode (A5, INPUT_PULLUP);
  Serial.begin(9600);
}

int readButtons (int pin) {
  int b, c;
  c = analogRead(pin);
  Serial.print("analogRead =  ");
  Serial.println(c);
  delay(100);
  if (c > 1015) b = 0;
  else if (c > 70 && c < 76) b = 1;
  else if (c > 122 && c < 128) b = 2;
  else if (c > 169 && c < 175) b = 3;
  else if (c > 209 && c < 217) b = 4;
  else if (c > 247 && c < 256) b = 5;
  else if (c > 280 && c < 291) b = 6;
  else b = 0;
  if (b == old_b) {
   return 0;
   old_b = b;
  } else {
    return b;
    old_b = b;              
    }                           
}

void loop () {
  while ((val = readButtons(5)) != 5) {
    if ((val == 1) || (val == 2) || (val == 3) || (val == 4)) {
      array[counter] = val;
      Serial.print("In  ");
      Serial.print(counter);                
      Serial.print(" saving ");            
      Serial.println(val);
      delay(200);
      counter++;
      if (counter == MAX) {
        counter = 0;
      } 
    }
  }

  temp = counter;
  counter = 0;

  for (i = 0; i < temp; i++) {
    if (array[i] % 2 == 0) {
      L1 = 2;
      L2 = array[i] / 3 + 3; 
    } else {
      L2 = 5;
      L1 = array[i] % 3 + 3;    
      }

    if (readButtons(5) != 5) {
     digitalWrite (L1, HIGH);
     if (readButtons(5) != 5) {
      digitalWrite (L2, HIGH);
      delay(1000);
      digitalWrite (L1, LOW);
      digitalWrite (L2, LOW);
      if (readButtons(5) == 5) {
        i = temp;
      }
     } else {
       digitalWrite (L1, LOW);
       i = temp; 
     }
    }

  }
}

Ich habe also noch eine Funktion, die erkennt, ob eine Taste gedrückt wurde. Zuvor habe ich ermittelt, in welchem ​​Bereich Werte für bestimmte Schaltflächen liegen. Imwhile () {...}Schleife, während die 5. Taste nicht gedrückt wird, "schaut" Arduino, ob eine der ersten vier Tasten gedrückt wurde. Wenn es war, speichern Sie es in einem Array und wenn nicht, "suchen Sie weiter". Wenn die 5. Taste gedrückt wird, brechen wir aus und speichern die letzte bekannte Stelle im Array, in der der Wert gespeichert wurde, und setzen den Stellenzähler auf Null, sodass Arduino beim erneuten Starten die Werte an der ersten Stelle des Arrays speichert. Dann bestimme ich basierend auf dem, was gespeichert ist, welcher Pin/LED aufleuchten wird. Wenn zu irgendeinem Zeitpunkt während der blinkenden LEDs die 5. Taste erneut gedrückt wird, stoppt Arduino das Blinken und wartet erneut auf die Betätigung der ersten vier Tasten. So soll es theoretisch funktionieren. In der Praxis kann ich es immer noch nicht dazu bringen, jedes Mal zu blinken, wenn die 5. Taste erneut gedrückt wird. Ich muss mehrmals drücken, manchmal zwei oder manchmal sogar noch mehr Male. ich nicht

Willkommen bei EE.SE! Ihre Frage bezieht sich auf die C-Programmierung und ist nicht spezifisch für einen Mikrocontroller, geschweige denn für das Arduino. Ich habe vorgeschlagen, es auf eine andere Website zu verschieben, die besser für Softwarefragen geeignet ist.
@ Joe Ich bin anderer Meinung. Die Frage ist, wie man eine Situation schafft, die die Ausführung der Hauptmethode nach einem Tastendruck oder einer anderen externen Eingabe neu startet. Zum Beispiel, wie man einen Hardware-Interrupt für diesen Zweck nutzt. Außerdem gab das OP an, dass es für Arduino ist, das auf dem Atmel AVR-Mikrocontroller basiert.
Obwohl ich mit einem Arduino nicht vertraut bin, muss Folgendes passieren: 1. Schreiben Sie einen Interrupt, um den Tastendruck zu verarbeiten. 2. Lassen Sie den Interrupt nicht dorthin zurückkehren, wo Sie zuletzt waren, sondern zum Anfang von loop. Wie Sie dies tun, hängt von Ihrem Mikrocontroller und der Art und Weise ab, wie er Interrupts behandelt (Stacks / Unstacks).
Diese Frage scheint nicht zum Thema zu gehören, da es um reine Programmierung geht.
Die Verwendung eines Interrupts zum Ausbrechen aus einer Schleife ist enorm gefährlich. Wenn Sie nicht alle verschachtelten Schleifen in geordneter Weise verlassen, hat der Stapel (bestenfalls) toten Speicher oder ist beschädigt. Was das OP will, ist eine reine Programmierfrage in der Sprache C und unabhängig vom Prozessor.
@JoeHass: Ich habe meine Frage bearbeitet und denke, dass sie jetzt zum Thema gehört.
Ich bin mir nicht sicher, warum dies überhaupt in den Superuser verschoben wurde, dort ist nur computerbezogene Software ein Thema. Wenn es in den Stapelüberlauf verschoben worden wäre, hätte ich es vielleicht rechtfertigen können.
@alexan_e: Weißt du vielleicht, wie lange es dauern wird, bis du wieder beim Thema bist?
Wenn es genügend Wiedereröffnungsstimmen von Benutzern erhält, die das Recht haben, solche Stimmen abzugeben.
@Mate - Schauen Sie sich meine aktualisierte Antwort an. Schau mal, ob es dir weiterhilft.
Als ich sah, dass dies zu SuperUser migriert wurde, verlor ich den Respekt vor dem SE-Moderationssystem. Ich freue mich, dass es hier wieder aufwärts geht.
@Mate, deine letzte Änderung ist wahrscheinlich eine weitere Frage wert, da diese ziemlich breit und überfüllt wird (zu viele Änderungen und Kommentare). Ich werde es gerne beantworten, wenn Sie es separat fragen. Wenn Sie schon dabei sind, ziehen Sie bitte in Betracht, die Antwort zu akzeptieren, die für Sie am hilfreichsten war (indem Sie auf das Häkchen neben der Stimmenzahl klicken). Danke!

Antworten (3)

Sind Sie sicher, dass Sie jederzeit beenden müssen, wo immer sich der Ausführungszeiger befindet? Normalerweise könnten Sie einfach den Tastendruck am Ende der Schleifen whileund überprüfen forund anrufen, returnwenn er gedrückt wird.

Manchmal denken wir, unser Programm muss etwas tun und die Ausführung unterbrechen, aber nach sorgfältiger Prüfung kommen wir zu dem Schluss, dass es eine Schleifeninteraktion beenden und dann beenden kann.

Können Sie uns sagen, was innerhalb der Schleifen gemacht wird, damit wir sicherstellen können, was benötigt wird?

Bearbeiten: Wie ich vermutet habe, besteht das Problem mit Ihrem Programm darin, dass Ihre Verzögerungsintervalle zu lang sind, z. B. delay(1000)und delay(500), und Sie können nicht nach Tastendrücken suchen, während das Programm wartet.

Um das zu beheben, erstellen Sie die folgende Funktion, die Ihre Aufrufe zum Verzögern ersetzt:

// waits for the specified miliseconds 
// or return (almost) immediately if button is pressed
// return value indicates whether button was pressed or not
boolean waitOrButtonPressed(int miliseconds) {
  for (int i = 0; i < miliseconds / 10; i++) {
    if (readButtons(5)) == 5)
      return true;
    delay(10);
  }
  return false;
}

Ersetzen Sie dann innerhalb Ihrer forSchleife Ihre Aufrufe delay(1000)durch den folgenden Code:

if (waitOrButtonPressed(1000)) return;

Die von mir erstellte Funktion lässt Ihr Programm fast wie warten delay(), kehrt aber zurück true, wenn während dieser Zeit eine Taste gedrückt wurde. In diesem Fall ifwird die obige Anweisung beendet loop(), die vom Arduino-generierten Code erneut aufgerufen wird.

Denken Sie daran, die zu wartenden Millisekunden beizubehalten, wenn Sie Ihre Aufrufe ersetzen, delay()um das Warteverhalten Ihres Programms unverändert zu lassen.

Bearbeiten 2: Ramblings

Fälle wie dieser erinnern mich an den Film Tron , in dem die Hauptfigur in einen Computer gezogen wird, wo die Zeit viel langsamer vergeht als im wirklichen Leben. Also, wie im Film, sind 1000 Millisekunden (1s) VIEL ZEIT für die MCU. In dieser Zeit können Sie viel mehr tun, als zu warten. Versuchen Sie, die langen Wartezeiten zu unterbrechen, und machen Sie nützliche Dinge mit Ihren MCU-Zyklen.

Erfahren Sie auch, wie Sie Interrupts verwenden, wie andere in ihren Kommentaren und Antworten erwähnt haben. Es ist nur eine andere (etwas kompliziertere) Art, Dinge zu erledigen, während Sie warten. In Ihrem Fall könnten Interrupts verwendet werden, um den Tastendruck zu erkennen, ohne ihn jedes Mal überall überprüfen zu müssen. Hier ist ein Ort, an dem Sie anfangen können, etwas über Interrupts mit Arduino zu lernen .

Ich habe versucht, Ihre Funktion zu verwenden, aber ich konnte sie nicht zum Laufen bringen. Interrupts habe ich auch geprüft. In jedem Beispiel habe ich festgestellt, dass eine Funktion aufgerufen wird, wenn sie ausgelöst wird, aber ich würde sie benötigen, wenn sie ausgelöst wird (nur während der Ausführung der for-Schleife), um das gesamte Programm von Anfang an zu starten, dh die Ausführung der void-Schleife {...} zu starten. Ist das möglich?
@Mate Warum hat es nicht funktioniert? Was geschieht? Hat es gut kompiliert? Oder tut es jetzt das, was Sie beabsichtigt haben? Was den Interrupt betrifft, könnten Sie den Tastendruck im Interrupt erkennen (o Pin Level Change One) und dort ein Flag setzen, aber Sie müssten trotzdem prüfen, ob das Flag in der Schleife gesetzt wurde, ähnlich wie wir es versuchen mit der Funktion, die ich dir gegeben habe. Wie andere würde ich Ihnen nicht raten, zu versuchen, das Beenden der Funktion loop () direkt aus dem Interrupt zu erzwingen. Das würde erfordern, dass Sie Dinge auf sehr niedrigem Niveau erstellen, die schwierig zu erstellen, zu debuggen und zu warten sind und die Sie wirklich nicht tun müssen.
In meinem Code wusste ich nicht genau, wie ich Ihren Tastendruck erkennen sollte, also habe ich einfach hinzugefügt, was ich normalerweise in meinem Code tun würde. Sie müssen also den Teil if (digitalRead(fifthButton) == HIGH)so ändern, wie Sie es tun, um zu überprüfen, ob Ihre Taste gedrückt wurde. Mir ist aufgefallen, dass Sie einen analogen Pin verwenden, um zu erkennen, ob diese Taste gedrückt wird, daher muss dies möglicherweise behoben werden.
Ok, jetzt sehe ich, dass Sie einen Spannungsteiler an einem analogen Pin verwenden, um mehrere Tasten mit einem einzigen Pin zu lesen. Super, das mache ich auch ständig. Also habe ich die Art und Weise geändert, wie ich überprüfe, ob die 5. Taste in meiner Antwort gedrückt wird. Überprüfen Sie, ob es jetzt funktioniert.
Wenn ich die 5. Taste für den Beginn des Blinkens und für das Ende des Blinkens drücke, erkennt Arduino dies nicht sofort, dh ich muss mehrmals drücken, um mit dem Blinken zu beginnen, und mehrmals, um das Blinken zu stoppen. Wie oft ich drücken muss, um das Blinken zu starten/beenden, ist jedes Mal anders.
Das liegt auch an Ihren Anrufen bei delay(). Das Problem ist, dass Sie nicht überprüfen können, ob die Taste gedrückt wird, wenn sich Ihre MCU in delay () befindet. Daher wird nur sehr schnell, ein- oder zweimal pro Sekunde, nach Tastendrücken gesucht. Wenn Sie das Glück haben, den Knopf zu drücken, während er die kurze Überprüfung durchführt, dann funktioniert es. Das ist auch der Grund, warum Sie denken, dass Sie die Taste mehrmals drücken müssen, und die Anzahl der erforderlichen Betätigungen ist variabel. Ich denke, Sie sollten Ihre While-Schleife ersetzen, in der Sie definieren, wie LEDs blinken sollen, und stattdessen einen Pin-Change-Interrupt verwenden, um Tastendrücke zu erfassen. Können Sie das tun?
Ich bin noch nicht sehr vertraut mit Interrupts. Wäre es irgendwie möglich, wenn ich alle Verzögerungen (500) entfernen würde und da Verzögerungen (1000) zum Blinken von LEDs verwendet werden, wenn ich diese 1000 ms irgendwie mit millis() bekommen könnte?
Sie können Millis() sicher verwenden, um Ihren Code warten zu lassen. Die Grundidee besteht darin, millis() zu speichern, wenn Sie die Überprüfung durchführen, und dann nur erneut zu überprüfen, wenn millis() oldMillis + someDelay ist. Dann testen Sie zwischendurch auf Tastendrücke. Kannst du das versuchen?
Ich wollte irgendwie diese 1s mit für LEDs bekommen.

Eine Methode, die verwendet werden kann und zum Zurücksetzen des MCU führt (ich bin mir nicht sicher, ob dies der Effekt ist, nach dem Sie suchen), besteht darin, einen externen Interrupt für die Schaltfläche zu verwenden und innerhalb des Interrupts den Watchdog mit einem kleinen Timeout zu aktivieren und lass es ablaufen und setze die mcu zurück.

+1 Dies ist ungefähr die einzige sichere Möglichkeit, mit einem Tastendruck aus der Hauptschleife in einem eingebetteten Programm auszubrechen. Wenn das OP mit einem harten Zurücksetzen des Prozessors leben kann, wäre dies der richtige Weg.
Wenn es in Ordnung ist, kann ich die Frage ändern und den eigentlichen Code schreiben, den ich verwende. Ich habe eine etwas allgemeinere Frage gestellt, damit sie für mehr Dinge verwendet werden kann als für das, was ich zu erreichen versuche.
@Mate Wenn Sie der Meinung sind, dass dies Ihre Frage nicht beantwortet und dass der detaillierte Code Ihnen hilft, eine geeignetere Lösung zu finden, können Sie dies wahrscheinlich tun. Ich hoffe, es ändert nichts am Wesen der Frage.

Aus diesem Grund hat C die goto- Anweisung; zum Vorwärtsbewegen aus vielen verschachtelten Schleifen.

Die meisten Leute werden Ihnen sagen, dass Sie es nicht verwenden sollen, und es ist wahrscheinlich, dass Sie es umgehen können, aber es ist sicherlich eine Lösung für Ihr Problem.

Warum die Ablehnung?
Meistens (wenn auch nicht immer) wird goto verwendet, sein ziemlich schlechter Codierungsstil. Deshalb sagen Ihnen die meisten Leute, dass Sie es nicht verwenden sollen. Es gibt normalerweise bessere Möglichkeiten, damit umzugehen.
... und die Ablehnung kam nicht von mir
Sie haben den zweiten Satz in meiner Antwort ziemlich zusammengefasst. Also, klar stimme ich zu. Dennoch ist es eine gültige Lösung.
Deshalb kam das DV nicht von mir!
Wer auch immer die Ablehnung gemacht hat, es ist legitim. Denn das gotosoll gar nicht erst erwähnt werden. Zu sagen, „es ist ein ziemlich schlechter Programmierstil“, ist immer noch eine Untertreibung. gotoist ein Relikt von (programmierähnlichem) Assembler, wo es ein natürlicher Teil des Vokabulars ist. Das Aufteilen von langem Code in Funktionen, deren Aufruf bei Bedarf und die Verwendung von return, breakoder continuemacht die Verwendung von gotoobsolet. Außerdem erzeugt eine tiefe Verschachtelung algorithmische Komplexität, die vermieden werden sollte. Unterm Strich ist es nicht nötig, gotowenn Sie sich mit Programmierung auskennen. Ernsthaft.
@Peterino Danke, ich mache mir seit vier Jahren große Sorgen darüber. Ich bin froh, dass Sie sich einbringen konnten, ohne tatsächlich etwas hinzuzufügen.