Pimp my Code!

Inhaltsverzeichnis

IR-Auslöser für Pentax-Kameras

Akku-Ladekurve

Wir bauen uns eine Piepliothek!

Ein Automat ohne delay()
Pimp my Code!

Tipps + Tricks



Pimp my Code!

Der Code im letzten Abschnitt funktioniert zwar sehr gut, kann aber verbessert werden. Dabei geht es teilweise um Dinge, die man leicht als „Sesselpuperei“ abtut und die zunächst zusätzliche Tipperei bedeuten – am Ende steht jedoch ein Programm, das weniger Resourcen benötigt.

Zunächst sollte man dafür sorgen, dass der Compiler ein paar Informationen bekommt, die ihm bei der Fehlersuche helfen. Etwas, das man sich angewöhnen sollte: Variablen, die sich während des Programmablaufs nicht ändern, als konstant zu deklarieren.

const int d[]={10, 200, 10, 1000}; // Dauer der Phasen in ms

Weiterhin kann man den Code kompakter gestalten, indem man Teile überflüssig macht und weglässt. Man kann zum Beispiel die Variable millisMem in jedem switch-Abschnitt aktualisieren und so jeweils den else-Teil weglassen. Die Umkehrung der Bedingung im if bewirkt zudem, dass der damit zusammenhängende Code nur dann ausgeführt wird, wenn sich etwas ändert.

Der erste Fall sieht dann so aus:

case ON1:
      if(millis()-millisMem > d[0])
      {
        millisMem=millis();
        digitalWrite(LEDPin, LOW);
        phase=OFF1;
      }
      break;

Durch die Aktualisierung von millisMem in jedem switch-Abschnitt ergibt sich außerdem, dass im if der folgenden Abschnitte die Berechnung der Summe von d[0] bis d[3] entfällt.

Dann fällt auf, dass es bei digitalWrite() nur darum geht, die LED abwechselnd ein- und auszuschalten. Anstatt HIGH und LOW einzusetzen, verwendet man eine Variable des Typs boolean und ändert diese in jedem switch-Abschnitt:


boolean state=true; // Deklaration und Initialisierung

case ON1:
      …
      digitalWrite(LEDPin, state); // Hier wird "false" als LOW
                                   // und "true" als HIGH
                                   // interpretiert.
      …
      state=!state; // Das Ausrufezeichen bedeutet "nicht" und
                    // bewirkt, dass "true" zu "false"
                    // wird und umgekehrt.
      …
case OFF1: …

George Boole war ein englischer Mathematiker, Logiker und Philosoph. Wer eine Abwechslung vom Programmieren sucht, kann den entsprechenden Wikipedia-Artikel lesen.

Alle Verbesserungen am Stück:

// Blinken ohne delay()

const byte LEDPin=13; // Pin, an dem die LED haengt
enum Phase {ON1, OFF1, ON2, OFF2}; // "Namen" fuer die Phasen
const int d[]={10, 200, 10, 1000}; // Dauer der Phasen in ms
Phase phase; // Enthaelt die aktuelle Phase
unsigned long int millisMem; // Merker fuer millis()
boolean state; // Status, der gesetzt werden soll

void setup()
{
  pinMode(LEDPin, OUTPUT);
  phase=ON1;
  millisMem=millis();
  state=true;
  digitalWrite(LEDPin, state);
}

void loop()
{
  switch(phase)
  {
    case ON1:
              if(millis()-millisMem > d[0])
              { phase=OFF1; millisMem=millis(); state=!state;
              digitalWrite(LEDPin, state); }
              break;
    case OFF1:
              if(millis()-millisMem > d[1])
              { phase=ON2; millisMem=millis(); state=!state;
              digitalWrite(LEDPin, state); }
              break;
    case ON2:
              if(millis()-millisMem > d[2])
              { phase=OFF2; millisMem=millis(); state=!state;
              digitalWrite(LEDPin, state); }
              break;
    case OFF2:
              if(millis()-millisMem > d[3])
              { phase=ON1; millisMem=millis(); state=!state;
              digitalWrite(LEDPin, state); }
              break;
  }
}

Um die Variable index ergänzt (als Ersatz für phase) und stark geschrumpft ist der Automat nur noch auf den zweiten Blick erkennbar – wenn überhaupt:

// Blinken ohne delay()

// Basiert auf Code von "agmue" (s. Arduinoforum)

const byte LEDPin=13; // Pin, an dem die LED haengt
const int d[]={10, 200, 10, 1000}; // Dauer der Phasen in ms
unsigned long int millisMem;   // Merker fuer millis()
bool state;           // Status, der gesetzt werden soll
byte index;           // Ersetzt den Typ Phase und die
                      // gleichnamige Variable

void setup()
{
  pinMode(LEDPin, OUTPUT);
  millisMem=millis();
  state=true;
  index=0;
  digitalWrite(LEDPin, HIGH);
}

void loop()
{
  if (millis()-millisMem > d[index])
    {
      millisMem=millis();
      state=!state;
      digitalWrite(LEDPin, state);
      index++;
      index=index%(sizeof(d)/sizeof(d[0]));
    }
}

Die Meldungen, die beim Übersetzen in der IDE erscheinen (Version 1.6.5, Ziel: Arduino Nano mit ATmega328):

Version aus vorigem Abschnitt:

Der Sketch verwendet 1.322 Bytes (4%) des Programmspeicherplatzes. Das Maximum sind 30.720 Bytes.
Globale Variablen verwenden 25 Bytes (1%) des dynamischen Speichers, 2.023 Bytes für lokale Variablen verbleiben. Das Maximum sind 2.048 Bytes.

Lange Version von oben:

Der Sketch verwendet 1.182 Bytes (3%) des Programmspeicherplatzes. Das Maximum sind 30.720 Bytes.
Globale Variablen verwenden 16 Bytes (0%) des dynamischen Speichers, 2.032 Bytes für lokale Variablen verbleiben. Das Maximum sind 2.048 Bytes.

Die kurze Version:

Der Sketch verwendet 1.054 Bytes (3%) des Programmspeicherplatzes. Das Maximum sind 30.720 Bytes.
Globale Variablen verwenden 23 Bytes (1%) des dynamischen Speichers, 2.025 Bytes für lokale Variablen verbleiben. Das Maximum sind 2.048 Bytes.

Die verbesserte Version hat gegenüber der Vorversion einen um 149 Bytes geringeren Platzbedarf – die kompakte Variante ist um weitere 121 Bytes sparsamer.

Der Gewinn an Speicherplatz erscheint mit insgesamt 270 Bytes zwar nicht sonderlich groß, aber wer viel vorhat, kommt schnell an die Grenzen des nicht allzu großen Speichers und freut sich über jedes eingesparte Byte.

Wenn der eingesparte Speicher nicht so wichtig ist, bevorzuge ich die lange Version von oben, weil ich sie für leichter verständlich halte.

Formelfreakfutter

Möglicherweise ist man im stark verkürzten Code über die Zeile

      index=index%(sizeof(d)/sizeof(d[0]));

gestolpert und hat sich gefragt, was das soll.

Die Antwort ist ziemlich einfach: Mit (sizeof(d)/sizeof(d[0])) wird die Anzahl der Elemente in d ermittelt und mit dem aktuellen Wert von index in einer Modulo-Operation verrechnet. Wenn index den Wert 4 hat, ist das Ergebnis dieser Rechnung 0. Die Variable index wird also immer wieder von vier auf null zurückgesetzt.

Das Besondere daran: Da der Index jedes Mal neu berechnet wird, verlässt er nie den gültigen Bereich – und zwar unabhängig von der Anzahl der Elemente in d.

Die Modulo-Operation ergibt den Rest einer Division. Der passende Abschnitt der Wikipedia befindet sich hier.

Weiter zu: Tipps + Tricks


Nach oben Inhaltsverzeichnis

Valid HTML 4.01 Transitional