Erik Bartmann

Mit Arduino die elektronische Welt entdecken


Скачать книгу

einen Rückgabewert an den Aufrufer zurückliefern, wie das im Moment hier der Fall ist und es gibt Funktionen, die führen etwas aus, ohne dass ein Wert an den Aufrufer zurückgeliefert wird:

      float a = 5.4, b = 7.36; float summe = a + b; float mittelwert = summe / 2;

      Beim Mittelwert zweier Zahlen werden diese addiert und das Ergebnis durch 2 geteilt. Willst du nun an mehreren Stellen im Sketch diesen Mittelwert bilden, wären immer wieder die beiden unteren Zeilen einzufügen. Das wird jedoch durch die Definition einer Funktion erleichtert. Diese könnte wie folgt aussehen:

      float mittelwert(float a, float b) { return (a + b)/2; }

      Das sollten wir uns genauer ansehen, denn da kommen viele Dinge zusammen:

[Bild]

      Die erste Zeile einer Funktion wird als Signatur bezeichnet und ist die Deklarationszeile einer Funktion. Innerhalb der geschweiften Klammern befindet sich die Definition einer Funktion. Soll eine Funktion etwas an den Aufrufer zurückliefern, steht zu Beginn der sogenannte Rückgabedatentyp, der hier float ist. Das macht jedoch zwingend eine return-Anweisung innerhalb der Definition erforderlich, denn diese ist im Endeffekt für die Rückgabe eines Wertes verantwortlich, womit die Funktion nach ihrem Aufruf auch letztendlich verlassen wird. Man kann einer Funktion keinen, einen oder mehrere Werte beim Aufruf übergeben. In unserem Fall werden zwei Werte vom Datentyp float erwartet, die beim Aufruf an die dort aufgeführten Parameter a beziehungsweise b übergeben werden. Diese Parameter arbeiten wie lokale Variablen, die nach dem Verlassen der Funktion wieder aus dem Speicher entfernt werden, da sie nicht weiter benötigt werden. Die beiden prominentesten Funktionen in der Arduino-Entwicklungsumgebung sind natürlich die setup- und loop-Funktion, die immer vorhanden sein müssen:

      void setup() {/* ... */} void loop() {/* ... */}

      Es ist zu sehen, dass beide den Rückgabedatentyp void besitzen, was übersetzt leer bedeutet, weil sie keinen Wert zurückliefern. Zudem ist keine Parameterliste zu sehen, was an den leeren runden Klammerpaaren zu erkennen ist. Es können demnach auch beim Aufruf keine Werte mit übergeben werden. Natürlich gäbe es noch viel mehr über Funktionen zu berichten, doch das ist dann Thema von weiteren Bastelkapiteln oder Teil von C++-Tutorials und würde den Umfang dieses Buches etwas sprengen.

      In Kapitel 2 – ich hatte es erwähnt – hast du schon etwas über Befehle erfahren. Sie teilen dem Mikrocontroller mit, was er zu tun hat. Ein Sketch besteht aber in der Regel aus einer ganzen Reihe von Befehlen, die sequentiell abgearbeitet werden. Das Arduino-Board ist mit einer bestimmten Anzahl von Ein- und Ausgängen versehen, an die du diverse elektrische und elektronische Komponenten anschließen kannst. Wenn der Mikrocontroller auf bestimmte Einflüsse von außen reagieren soll, schließt du beispielsweise einen Sensor an einen Eingang an. Die einfachste Form eines Sensors ist ein Schalter oder Taster. Wenn der Kontakt geschlossen wird, soll zum Beispiel eine LED leuchten. Der Sketch muss also eine Möglichkeit haben, eine Entscheidung zu treffen: Ist der Schalter geschlossen, dann versorge die LED mit Spannung (LED leuchtet); ist der Schalter offen, dann trenne die LED von der Spannungsversorgung (LED wird dunkel).

[Bild]

      Was ist eine Kontrollstruktur?

[Bild]

      Kontrollstrukturen sind Anweisungen in imperativen Programmiersprachen. Sie kommen zum Einsatz, um den Ablauf eines Programms zu steuern und es in eine bestimmte Richtung zu lenken. Dabei kann eine Kontrollstruktur entweder eine Verzweigung oder eine Schleife sein. Zumeist wird ihre Ausführung über logische (boolesche) Ausdrücke gesteuert.

      Wir werfen zu Beginn einen Blick auf ein Flussdiagramm, das uns zeigt, wie der Ablauf der Sketch-Ausführung in bestimmte Bahnen gelenkt wird, so dass es sich nicht mehr um einen linearen Verlauf handelt. Der Sketch steht beim Erreichen einer Kontrollstruktur an einem Scheideweg und muss sehen, wie es weitergehen soll. Als Entscheidungsgrundlage dient ihm eine Bedingung, die es zu bewerten gilt.

      Die if-Anweisung

      Programmtechnisch nutzen wir die if-Anweisung‌. Es handelt sich um eine Wenn-dann-Entscheidung‌.

[Bild]

      Abb. 3: Das Flussdiagramm einer if-Kontrollstruktur

      Wurde die Bedingung als wahr erkannt, folgt die Ausführung einer oder auch mehrerer Anweisungen. Hier wieder ein kurzes Beispiel:

      if(tasterStatus == HIGH) digitalWrite(ledPin, HIGH);

      Wenn mehrere Befehle in einer if-Anweisung ausgeführt werden sollen, musst du einen Befehlsblock mit den geschweiften Klammerpaaren bilden. Er wird dann als komplette Befehlseinheit ausgeführt:

      if(tasterStatus == HIGH) { digitalWrite(ledPin, HIGH); Serial.println("HIGH-Level erreicht."); }

      Die if-else-Anweisung

      Es gibt noch eine erweiterte Form der if-Kontrollstruktur. Es handelt sich dabei um eine Wenn-dann-sonst-Entscheidung‌, die sich aus einer if-else-Anweisung ergibt. Das entsprechende Flussdiagramm sieht wie folgt aus:

[Bild]

      Abb. 4: Das Flussdiagramm einer if-else-Kontrollstruktur

      Das folgende Codebeispiel zeigt dir die Syntax der if-else-Anweisung:

      if(tasterStatus == HIGH) digitalWrite(ledPin, HIGH); else digitalWrite(ledPin, LOW);

      Die switch-case-Anweisung

      Sollen mehrere Abfragen hintereinander erfolgen, kann das natürlich über ein Konstrukt mehrfacher if-then-else-Anweisungen erfolgen. Es gibt jedoch noch eine einfachere Variante, die vereinfacht zu schreiben und damit auch besser lesbar ist, die sogenannte switch-case-Anweisung (Abbildung 5).

      Die Syntax dazu sieht wie folgt aus:

      switch(var) { case label1: // Anweisung(en) break; case label2: // Anweisung(en) break; default: // Anweisung(en) break; }

[Bild]

      Folgende Parameter sind hierbei erlaubt:

       var: eine Variable mit den erlaubten Datentypen int und char.

       label1, label2: Konstanten mit den erlaubten Datentypen int und char.

      Du solltest unbedingt darauf achten, dass nach der Ausführung einer Anweisung der Schleifendurchlauf mit break unterbrochen wird, da sonst die folgenden Sprungmarken ebenfalls geprüft und die dort aufgeführten Anweisungen vielleicht ausgeführt werden. Die letzte break-Anweisung Quelltext nach der default-Anweisung ist nicht zwingend erforderlich.

      Operatoren‌

      Natürlich gibt es bei den Kontrollstrukturen und den zu testenden Bedingungen nicht nur die Prüfung auf Gleichheit. Die folgende Tabelle zeigt alle C++-Vergleichsoperatoren:



Tabelle 2: Vergleichsoperatoren‌
Vergleichsoperator