Tinkercad Übung 9 - LED Lauflicht (3)

Pausiere die LED Animation mit Schleifen

Das Ergebnis dieser Übung ist ähnlich wie in Übung 8, nur nutzt du Schleifen dafür.
  • Empfohlenes Vorwissen: Bedienung Tinkercad Circuits, Übung 7, Übung 8
  • Neue Inhalte: Schleifen (for, while), Rechnen mit Variablen

Schritt 1 - Demo-Entwurf öffnen und inspizieren

Kopiere diesen Demo-Entwurf (eine der Animationen aus Übung 7):
Abbildung 1 - LED-Animation aus dem Demo-Entwurf

Starte die Animation und betätige den Taster. Schau in das Programm:

Das Programm in Demo-Entwurf konfiguriert alle LED-Pins (4 bis 13) als Ausgang. loop() schaltet die LEDs nacheinander einzeln ein. Sobald alle eingeschaltet sind, werden sie wieder ausgeschaltet. Das ergibt 10 Animationsschritte.

Schritt 2 - While-Schleife

Der Demo-Entwurf beinhaltet sehr viele sich wiederholende Befehls-Zeilen, z.B.:

pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
usw...

und

// Animationsschritt 1:
digitalWrite(4, HIGH); // Pin 4 einschalten
delay(ANIMATIONSGESCHWINDIGKEIT);

// Animationsschritt 2:
digitalWrite(5, HIGH);
delay(ANIMATIONSGESCHWINDIGKEIT);
usw...

und

// alle ausschalten:
digitalWrite(4, LOW); // Pin 4 ausschalten
digitalWrite(5, LOW);
usw...

Diese Animation ist sehr einfach: Es werden einfach alle Pins mit Verzögerung einzeln eingeschaltet und dann wieder ausgeschaltet. Dafür werden die immer gleichen Befehle benutzt - nur ändert sich die Pin-Nummer immer.

Abhilfe schafft eine Zählschleife, die den Befehl eine vorgegebene Anzahl oft wiederholt und dabei mitzählt.

Als Pseudocode:

// zähle von 4 bis 13 (Nummern der Pins), merke aktuellen Zähler in "zaehler"
  // konfiguriere Pin mit Nummer zaehler als Ausgang

In C++ gibt es 3 Arten von Schleifen, die du auch im Cheat Sheet findest:
  1. while-Schleife
  2. do-while-Schleife
  3. for-Schleife (Zählschleife)
Auszug aus dem Cheat Sheet:
Abbildung 2 - Schleifen im Cheat Sheet

While-Schleife:

Die einfachste Schleife ist die while()-Schleife. "while" übersetzt sich ins Deutsche zu "solange".

Als Pseudocode:

// wiederhole solange Bedingung wahr ist:
  // Befehle, die wiederholt werden sollen

In C++:

while (Bedingung) {
  // Befehle, die wiederholt werden sollen
}

Die while()-Schleife wiederholt die Befehle im Schleifenkörper solange die Bedingung im Schleifenkopf wahr ist. Die Bedingung steht in Klammern. Die Befehle in geschweiften Klammern.

Wie bereits in Übung 8 beschrieben, ist eine Bedingung ein Vergleich, z.B.

Ist 12 == 12? → wahr
Ist 13 == 12? → unwahr

Das ist allerdings nur halb richtig. Ist ein Vergleich ausgewertet, hat er ein Ergebnis:
  • entweder wahr, auf englisch true,
  • oder unwahr, auf englisch false
Die beiden Vergleiche oben werden also durch ihr Ergebnis ersetzt:

true, entspricht 1
false, entspricht 0

Das Ergebnis ist ein logischer (oder auch boolscher) Wert. Logische Werte können nur die Werte true oder false annehmen.

Lassen wir also die Schleife laufen, solange 12 == 12 ist:

while (12 == 12) {
  // Befehle, die wiederholt werden sollen
}

Der Vergleich wird durch sein Ergebnis ersetzt:

while (true) {
  // Befehle, die wiederholt werden sollen
}

Die Bedingung muss also streng genommen nicht immer ein Vergleich sein: Man kann zum Beispiel auch direkt true als Bedingung reinschreiben. Die Bedingung ist ein logischer oder boolscher Wert.

Das wollen wir nun ein bisschen  genauer untersuchen:

Füge folgenden Code an Anfang des setup()-Bereichs hinzu (Wie du den Serial Monitor verwendest und Text dort hinschickst, hast du in Übung 6 gelernt):

setup() {
  Serial.begin(9600);
  Serial.println("Programm gestartet");
  while (true) {
    Serial.println("Schleife laeuft");
    delay(1000);
  }
  Serial.println("Schleife zu Ende");
}

Öffne den Serial Monitor und starte die Simulation. Was beobachtest du?
Ändere while(true) zu while(false).
Probiere ein paar Vergleiche auch, z.B. while(1==1) oder while(2>15).

while(true):
Abbildung 3 - Endlosschleife

Du siehst, der setup()-Bereich wird betreten, da der Serial Monitor "Programm gestartet" ausgibt und dann wird jede Sekunde "Schleife laeuft" ausgegeben. Wir kommen aus der Schleife aber nie wieder heraus. Sie wird unendlich wiederholt, da die Bedingung true ja immer wahr ist.

Das ist übrigens auch einer der Gründe, warum Apps oder Computerprogramme manchmal einfach einfrieren: Da gibt es irgendwo eine Schleife, die auf etwas wartet und immer wiederholt wird, aber die Bedingung wird nie unwahr, weswegen das Programm weiterhin in der Schleife feststeckt.

In EV3 würde das so aussehen:
Abbildung 4 - Endlosschleife in EV3

while(false):
Abbildung 5 - Schleife wird übersprungen

Du siehst, die Schleife wird direkt übersprungen und nie ausgeführt, da der Serial Monitor anstatt "Schleife läuft" "Schleife zu Ende" ausgibt. Die Bedingung false ist immer unwahr. Sie wird im Schleifenkopf überprüft und der Schleifenkörper nie betreten.

Schritt 3 - Zählen mit While-Schleife

Um die Schleife eine bestimmte Anzahl von Wiederholungen ausführen zu lassen, muss sich die Bedingung ändern:
  • Erst ist sie wahr und der Schleifenkörper wird betreten.
  • Sie bleibt wahr und der Schleifenkörper wird wiederholt.
  • Dann ist sie unwahr und die Schleife wird beendet.
Da sich die Bedingung ändern können soll, benötigen wir eine Variable. Variablen hast du in Übung 8 kennen gelernt.

Um die LED-Pins 4 bis 13 durchzuzählen, muss die Variable mit 4 initialisiert und dann bei jedem Schleifendurchgang hochgezählt werden. Erreicht sie 13, ist das der letzte Schleifendurchgang.

Als Pseudocode:

// Zähler = 4
// wiederhole solange Zähler <= 13 ist:
  // Befehle, die wiederholt werden sollen
  // Zähler hochzählen (plus 1 rechnen)

Sowas  nennt man eine Zählschleife. In EV3 würde sie so aussehen:
Abbildung 6 - Zählschleife in EV3

In C++:

int zaehler = 4;
while (zaehler <= 13) {
  // Befehle, die wiederholt werden sollen
  zaehler = zaehler + 1;
}

Die Variable soll eine Zahl abspeichern, ist also vom Typ int. Mit Variablen kann gerechnet werden - das funktioniert bei Variablen, die Zahlen abspeichern, wie du das von Mathe (Arithmetik) her gewohnt bist.

Gleichung:

x + 1 = 2

Jetzt nicht verwirren lassen: In der Artithmetik ist Istgleich (=) der Vergleichsoperator. In C++ ist == der Vergleichsoperator und = der Zuweisungsoperator. Der Zuweisungsoperator speichert das Ergebnis von der rechten Seite in die Variable auf der linken Seite. Dafür muss die rechte Seite aber erstmal ausgerechnet werden. Die Befehlszeilen

int zaehler = 4;       // zaehler hat den Wert 4
zaehler = zaehler + 1  // zaehler hat den Wert 5

vereinfachen sich dann zu

zaehler = 4 + 1  // zaehler hat den Wert 5

und dann zu

zaehler = 5;

Dafür gibt es auch 2 Kurzschreibweisen;

zaehler += 1// Kurzschreibweise 1 (Verbundoperator)
zaehler++;     //  Kurzschreibweise 2 (Inkrement)

Plus (+) ist ein artithmetischer Operator.
Schau doch mal in die Arduino-Referenz, was es noch für artithmetische Operatoren gibt:
https://www.arduino.cc/reference/de/

Eine Übersicht der Operatoren findest du auch im Cheat Sheet:
Abbildung 7 - Operatoren Übersicht im Cheat Sheet

Du kannst den Arduino für dich auch kompliziertere Formeln ausrechnen lassen, z.B.:

int a = 10; // a hat jetzt den Wert 10
int b = 20; // b hat jetzt den Wert 20
int c = ((a + b) / 3) * 10; // c hat jetzt den Wert 100

In der letzten Zeile wird zuerst die rechte Seite ausgerechnet und wie in der Arithmetik auch, die inneren Klammern zuerst (Punktrechnung vor Strichrechnung gilt auch). a und b werden durch ihre momentanen Werte ersetzt:

int= ((10 + 20) / 3) * 10; // c hat jetzt den Wert 100

Die innerste Klammer wird zuerst ausgerechnet:

int= (30 / 3) * 10;        // c hat jetzt den Wert 100

Dann die äußere:

int= 10 * 10;              // c hat jetzt den Wert 100

Dann wird multipliziert:

int c = 100;                  // c hat jetzt den Wert 100

Und zu guter letzt speichert der Zuweisungsoperator das Ergebnis in Variable c ab.

Du könntest mit einem Arduino also auch einen Taschenrechner programmieren.

Aber zurück zur Zählschleife.

Ändere die while()-Schleife im setup()-Bereich so ab, dass sie 10x ausgeführt wird. Öffne den Serial Monitor und starte die Simulation. Was beobachtest du?
Nutze den Serial Monitor, um dir den aktuellen Wert von deiner Zähl-Variable bei jedem Schleifendurchgang anzuzeigen.
Abbildung 8 - Zählschleife mit while()

Ändere die Bedingung, sodass die Schleife immer noch von 4 bis 13 zählt, aber für den Vergleich nicht kleinergleich als (<=), sonder kleiner als (<) verwendet.

Schritt 4 - For-Schleife

Da Zählschleifen sehr häufig vorkommen, gibt es dafür eine kompaktere Schreibweise: Die for-Schleife:

for (int zaehler = 4; zaehler <= 13; zaehler = zaehler + 1) {
  // Befehle, die wiederholt werden sollen
}

Zum Vergleich nochmal die while-Schleife aus Schritt 3:

int zaehler = 4;
while (zaehler <= 13) {
  // Befehle, die wiederholt werden sollen
  zaehler = zaehler + 1;
}

Der Schleifenkopf der for-Schleife hat 3 Teile, die durch Semikolon (;) getrennt sind:
  1. Deklaration und Initialisierung der Zähl-Variable (Startwert)
  2. Bedingung (schleife wird so lang ausgeführt wie diese wahr bleibt)
  3. Aktion. In unserem Fall rechnen wir die Zählvariable + 1 (wir inkrementieren sie).
Schau doch mal in die Arduino-Referenz, was die zu for-Schleifen zu sagen haben:

Ersetze die while()-Schleife im setup()-Bereich durch eine for-Schleife. Öffne den Serial Monitor und starte die Simulation. Es sollte sich vom Verhalten des Programms her nichts verändert haben.

Schritt 5 - Sich wiederholende Befehle ersetzen

Lang hat's gedauert, aber jetzt wollen wir endlich die sich wiederholenden Befehle ersetzen.

Teste erstmal, ob die LED-Animation noch funktioniert. Starte dazu die Simulation.

Beende die Simulation wieder und im setup()-Bereich ersetze alle Zeilen, die einen LED-Pin als Ausgang setzen durch eine einzige, die in einer Zählschleife ausgeführt wird. Die LED-Pin-Nummern (4, 5, ...) sollen jetzt nicht mehr als Magic Number im Code stehen, sondern dort soll deine Zählvariable eingesetzt werden.

pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
usw...

Teste, ob die LED-Animation noch funktioniert.
Ersetze auch die einzelnen Animationsschritte im loop()-Bereich durch einen Animationsschritt, der in einer Schleife wiederholt wird.

// Animationsschritt 1:
digitalWrite(4, HIGH); // Pin 4 einschalten
delay(ANIMATIONSGESCHWINDIGKEIT);

// Animationsschritt 2:
digitalWrite(5, HIGH);
delay(ANIMATIONSGESCHWINDIGKEIT);
usw...

Teste, ob die LED-Animation noch funktioniert.
Ersetze auch das Ausschalten aller LEDs am Ende des loop()-Bereichs durch eine Schleife.

// alle ausschalten:
digitalWrite(4, LOW); // Pin 4 ausschalten
digitalWrite(5, LOW);
usw...

Teste, ob die LED-Animation noch funktioniert.

Lösungsvorschlag: https://www.tinkercad.com/things/3X4o1aUNs6T-kkg-robotik-ubung-9-lauflicht-3-zahlschleifen

Was wir hier gemacht haben, nennt sich "refactoring". Das bedeutet, wir haben den Programmcode umgeschrieben, aber die Funktion ist dabei gleich geblieben. Der Code sieht trotzdem komplett anders aus. Meist fängt man schnell mal an was auszuprobieren und sobald es funktioniert spendiert man ein bisschen Zeit, um den Code schöner, schneller, kompakter und leichter lesbarer zu machen. Man refactort.

In der Zählschleife, die die Animationsschritte abarbeitet: Ändere die Aktion, sodass sie die Zählvariable nicht nur +1, sondern +2 rechnet. Starte die Simulation. Was beobachtest du?

Schritt 6 - Animation mit Taster pausieren

Wie im Übung 8 soll die Animation wieder mit dem Taster pausiert werden können.

Um die Animation wirklich zu pausieren, muss das Programm "anhalten", solange der Taster gedrückt bleibt. Anhalten kann man ganz einfach mit einer Schleife, die nichts tut:

while ( Taster an Pin 2 gedrückt ) {
  // nichts tun und warten
}

Der Schleifenkörper ist leer. Die Schleife wird natürlich trotzdem sehr schnell wiederholt und die Bedingung im Schleifenkopf trotzdem regelmäßig ausgewertet. Nur passiert im Schleifenkörper halt nichts.

Dafür gibt es sogar eine Kurzschreibweise:

while ( Taster an Pin 2 gedrückt );

Wenn du dich an Übung 8 erinnerst, ist es wichtig, dass die Animation zu jeder Zeit unterbrochen werden kann und nicht immer komplett am Stück ausgeführt wird. Das heißt, dass die Reaktion auf den Taster in der for-Schleife stattfinden muss, damit wir vor jedem Animationsschritt pausieren können.

// zählt von 4 bis 13
for (int pin=4; pin <= 13; pin++) {
    // in Schleife festhängen, wenn Taster gedrückt ist:
    while ( Taster an Pin 2 gedrückt ) {
      // nichts tun und warten
    }
  digitalWrite(pin, HIGH); // Pin mit Nummer pin einschalten
  delay(ANIMATIONSGESCHWINDIGKEIT);
}

Baue dies in das Programm ein. Starte die Simulation und halte den Taster gedrückt.

Was passiert, wenn du den Zustand des Tasters nicht mit HIGH, sondern mit LOW vergleichst?
Starte die Simulation und halte den Taster gedrückt.
Abbildung 9 - Animation mit Taster pausieren

Zusammenfassung

Du hast die while- und for-Schleifen kennen gelernt und sie zum Zählen verwendet - die while()-Schleife auch als Endlosschleife. Das hast du zuerst im Serial Monitor getestet und dann schrittweise den Demo-Entwurf refactort, sodass er zwar gleich funktioniert, aber wesentlich kompakter programmiert ist. Dann hast du eine while-Schleife mit leerem Schleifenkörper verwendet, um das Programm damit zu pausieren.

Weiter zu Übung 10

Kommentare

Beliebte Posts aus diesem Blog

Tinkercad Übung 6 - LED mit Taster ansteuern

Tinkercad Übung 11 - LED dimmen

Tinkercad Übung 15 - Ultraschallsensor auslesen