Inhaltsverzeichnis
bedingte Kompilierung, Makros
bedingte Compilierung
Wenn man erstmal so richtig am programmieren ist, möchte man sich z.B. verschiedene Sachen ausgeben lassen. Natürlich kommt da die serielle Schnittstelle ins Spiel. Also spickt man seinen Quelltext an den entsprechenden Stellen mit Serial.print Ausgaben. Sehr schön. So, fertig, alles läuft und jetzt? Jetzt müssen diese blöden Ausgaben natürlich auch wieder raus. Dann findet man wieder eine Fehler und muss das ganze Zeug wieder einbauen. 1. Möglichkeit ist natürlich alles immer ein und auszukommentieren. Aber auch das ist viel Arbeit. Wie gut, daß Arduino einen recht umfangreichen C-Compiler benutzt. Dieser beherscht bedingte Kompilierung. D.h. der Compiler kann anhand von sog. Compilerschaltern erkennen, ob er den Code, den er gerade vorsich hat, compilieren soll oder nicht. Schön und Gut aber wie hilft uns das jetzt? Naja, wir machen uns einen solchen Schalter selber und nennen den z.B. debug.
Definieren ist ganz einfach:
#define debug
Und Abfragen ist auch simple
#ifdef debug ... #endif
Alles was zwischen den beiden Compiler Direktiven (so nennen sich die Dinger mit dem #) steht wird nur mit compiliert wenn auch der Schalter debug definiert wurde. Zum Ein und Ausschalten reicht es nun einfach den define ein bzw. auszukommentieren. Also #define debug schaltet alles ein, //#define debug schaltet alles aus.
Klasse. Gibt's auch 'ne Möglichkeit auf das nicht vorhandensein einer definition zu prüfen? Aber klar:
#ifndef debug ... #else ... #endif
Und hier sieht man auch, daß es auch bei diesen Direktiven einen else Zweig gibt. (Natürlich auch beim #ifdef). So können wir jetzt überall schreiben:
... #ifdef debug Serial.begin(57600); #endif ... #ifdef debug Serial.print(value, HEX); #endif
Makros
Na, so haben wir den Quelltext schon mal leichter im Griff. Für's debuggen ist aber deutlich mehr Schreibarbeit. Kann man das evt. noch verkürzen? Ja auch das, jetzt wird's aber etwas tricky. Es bleibt bei dem #ifdef define. Wir fügen jetzt 2 sog. Makros zur Ausgabe hinzu. Ein Makro ist eine sog. Ersetzung. Überall wo der compiler das Makro findet wird es auch den Text ersetzt, den wir bei der Definition angegeben haben. Beispiel:
#define dbgOut(S) \ Serial.print(S);
d.h. der Compiler ersetzt alle dbgOut mit dem Parameter S durch Serial.print(S); Man kann natürlich auch das Makro leer lassen, dann würde der Compiler den Befehl entfernen.
#define dbgOut(S)
Na bitte, jetzt kombinieren wir das noch mit der bedingten Compilierung und schon haben wir eine recht einfache Möglichkeit, debug Ausgaben ein bzw. auszuschalten.
#ifdef debug #define dbgOut(S) \ Serial.print(S); #define dbgOutLn(S) \ Serial.println(S); #else #define dbgOut(S) #define dbgOutLn(S) #endif
Ich habe noch ein zweites Makro für Serail.println() hinzugefügt. Will man jetzt im Programm was ausgeben, reicht ein einfaches dbgOut(„Wir sind hier“);
weiteres Beispiel
Wie wir noch später sehen werden, können wir mit dem Arduino auch Programme für andere Controller erzeugen. Beispielsweise für den ATTiny85. Das ist der kleine Bruder vom ATMega. Besitzt nur ein DIP8 Gehäuse und hat nur 5 Leitungen zur freien Verfügung. ber für kleinere Sachen reicht das ja völlig. Problem:
Ich würde gerne das Programm am Arduino testen und entwicklen und dann erst später für den ATTiny kompilieren und brennen. Nix einfacher als das, denn die IDE stellt bereits fertige Definitionen zur Verfügung.
Hat man z.B. als Target den ATTiny85 aktiv, wird automatisch ein #define __AVR_ATtinyX5__ aktiv.
d.h. man kann z.B. mit
// Hardwareanbindung #ifndef __AVR_ATtinyX5__ // Arduino Hardware const byte Dout_0 = 4; const byte Dout_1 = 5; const byte Dout_2 = 6; const byte Dout_3 = 7; #else // ATTiny85 Hardware const byte Dout_0 = 0; const byte Dout_1 = 1; const byte Dout_2 = 2; const byte Dout_3 = 3; #endif
die komplette Hardwaredefinition automatisch umschalten. Und man kann natürlich das ganze auch wieder komplett verschalchteln. Was macht wohl das hier:
#ifndef __AVR_ATtinyX5__ #ifdef debug Serial.begin(57600); Serial.flush(); #endif #endif