Materialien zum Unterricht

Blinki

Ziel: An einem Anschluss des Controllers wird eine Leuchtdiode angeschlossen. Diese Diode soll blinken.

Was muss das erste Programm im Einzelnen machen?

  1. Einen Port auswählen.
  2. Den Pin des Portes, an dem die Leuchtdiode angeschlossen ist, als Ausgang festlegen.
  3. Die Leuchtdiode einschalten.
  4. Einige Zeit warten.
  5. Die Leuchtdiode ausschalten.
  6. Einige Zeit warten.
  7. Mit Punkt 3 fortfahren.

Erste Analyse des Programmes:

Nun geht es los.

Unser C-Programm hat immer die gleiche Struktur:

Einbinden der Include-Dateien

#include <avr/io.h>
#include <util/delay.h>

Die beiden Dateien io.h und delay.h sind im AVR-Studio enthalten. Die erste enthält Informationen über den Controller und muss eingebunden werden. Die zweite brauchen wir beim Blinklicht für die Warteschleife.

Definieren von Konstanten

Zur besseren Lesbarkeit der Programme und schnellen Veränderung werden einige Konstanten definiert.

#define LED_PORT		PORTB
#define LED_DDR DDRB
#define LED_GN PB0

LED_PORT ist der Port, an dem die Leuchtdiode angeschlossen ist, in diesem Fall Port B.
LED_DDR ist das Datenrichtungsregister, logischer Weise wieder das für Port B.
LED_GN gibt den Pin an, an dem die Leuchtdiode angeschlossen ist, her Pin 0.

Hauptprogramm

Nun geht es richtig los.

Das Hauptprogramm wird mit

int main(void)
              

eingeleitet. Konkret heißt das, dass die Funktion main (Hauptfunktion) einen Rückgabewert von Typ Integer hat und nichts (void) übergeben bekommt. Das ist die in C übliche Einleitung einer Funktion.

Der eigentliche Inhalt der Funktion wird in geschweifte Klammern eingeschlossen:

int main(void)
{

}

Als erstes muss das Datenrichtungsregister eingestellt werden. Der Anschluss für die Leuchtdiode wird auf Ausgang gestellt, also eine 1 eingetragen.

int main(void)
{
  LED_DDR = 0xff;

}

Da uns im Moment die anderen Pins egal sind, schalten wir einfach alle auf Ausgang. Das lässt sich jederzeit noch ändern. 0xff ist die in C übliche Schreibweise für 1111 1111 und wird auf der Seite über die Zahlensysteme erklärt.

Damit wären Punkt 1 und Punkt 2 erledigt. Nun kommt der Clou. Im Programmablaufplan folgen jetzt die Punkte 3 bis 7. Der 7. Punkt beinhaltet aber nur ein Rückspringen auf Punkt 3, es liegt eine endlos laufende Schleife vor. Die Schleife wird mit

while(1){
}

eingeleitet. Die while-Schleife wird solange ausgeführt, wie der Ausdruck in der runden Klammer den logischen Wert true oder 1 liefert. Da wir eine 1 in die Klammer geschrieben haben, wird die while-Schleife ununterbrochen ausgeführt, wir haben eine unendliche Schleife.

Eine solche unendliche Schleife ist bei Mikrocontrollern üblich, da sie die gewünschten Funktionen ständig ausführen sollen. (z.B. dauerndes Abfragen eines temperaturabhängignen Widerstands und Anzeigen des Temperaturwertes)

In der geschweiften Klammer steht das, was gemacht werden soll. Auf der Seite Bitmanipulationen kann man nachlesen, wie man einen logischen Wert ständig ändert, also die Blinkfunktion ausführt.

Die while-Schleife wird erweitert zu

while(1){
    LED_PORT ^= (1<<LED_GN);
    }
LED_PORT ist das ausgewählte PortB.
^= bedeutet Exclusiv-ODER-Verknüpfung mit der folgenden Bitfolge und Zuweisung zum LED_PORT.
Der letzte Ausdruck erzeugt die Bitfolge. Es wird eine 1 um LED_GN nach links verschoben. LED_GN ist aber die Position der angeschlossenen Leuchtdiode und wurde in der Konstantendefinition mit PB0 festgelegt. PB0 ist nun in den am Anfang eingebundenen Include-Dateien mit einer 0 festgelegt. Das heißt, es wird eine 1 direkt an die Stelle 0 geschoben.

Fertig.

Testen

Der spannendste Augenblick beim Programmieren ist das erste Starten und Testen des Programms. Meistens erkennt man erst jetzt, welche Fehler sich eingeschlichen haben und an welcher Stelle die Vorüberlegungen falsch waren und man noch mal ordentlich Geist investieren muss.

Im AVR-Studio erfolgt die Übersetzung des C-Programms in ein maschinenlesbares Programm über

Build, Build

oder die Taste F7.

 

Im unteren Fenster erscheinen die verschiedene Meldungen über den Ablauf der Übersetzung. Grüne Punkte sind o.k., gelbe Punkte geben Warnungen aus, die aber für den sicheren Ablauf des Programms unwichtig sind.

Richtig schlimm sind rote Punkte. Dann ist im Programm ein Fehler, der es mit Sicherheit nicht ordentlich laufen lassen wird.

Interessant ist nun die Frage, was die Ports beim Laufen des Programms machen. Dazu schalten wir in das I/O View-Fenster:

und suchen dort unseren Port B:

Wir sehen das Ausgaberegister PORTB, das Datenrichtungsregister DDRB und das Eingangsregister PINB.

Die grauen Kästchen weisen darauf hin, das der Inhalt der Bits noch unbestimmt ist, da das Programm noch nicht läuft.

Jetzt geht es aber los: Über Debug, Start Debugging wird das Testen gestartet. Die Bits der Register sind leer, es sind überall Nullen eingetragen. In der Hauptfunktion main(void) erscheint an der geschweiften Klammer ein gelber Pfeil. Das Programm ist gestartet und wartet im Ablauf an der Stelle des Pfeils.

Den nächsten Programmschritt können wir mit F11 ausführen lassen. Der Pfeil rutscht auf die nächste Programmzeile. Mit dem nächsten F11 wird die Zeile zum Setzen des Datenrichtungsregisters ausgeführt. Die Kästchen des Registers werden schwarz, alle Bits sind mit 1 gesetzt.

Jedes weitere Drücken von F11 lässt den Pfeil in der Endlosschleife hängen und das Bit0 des Port B wechselt ständig zwischen 1 und 0. Prima, das wollten wir ja.

Wenn wir das Programm nun auf den Controller übertagen und mit Reset starten, erleben wir eine Entäuschung: unser schönes Programm macht nicht das, was wir wollen. Die Leuchtdiode leuchtet dauerhaft, nix Blinken.

Watt nu, es ging doch in der Simulation alles so schön. Wir haben die Warteschleife vergessen. Der Controller arbeitet so schnell, dass die Leuchtdiode zwar blinkt, wir aber mit unserem träge Auge nicht hinterher kommen. Das Programm muss noch menschenfreundlich gestaltet werden, der flinke Controller muss ausgebremst werden.

Dazu wird er zwischen zwei Änderungen des Zustandes der Diode mit irgend was Sinnlosen beschäftigt, was lange dauert. Am besten eignet sich das einfaches Zählen. Das Zauberwort heißt delay , Verzögerung. Die dazu notwendige Include-Datei haben wir schon zu Beginn des Programms geladen.

Das vollständige Hauptprogramm sieht jetzt so aus:

Der Eintrag in der 2. Zeile sagt dem Programm, dass unser Controller mit einem Takt von 16 MHz läuft. Damit wartet die _delay_ms(1)-Funktion 1 ms. Wenn man die Funktion in der Schleife 1000 aufruft, hat man eine Wartezeit von 1 Sekunde.

Wir werden später sehen, wie man den Controller in einer Warteschleife laufen lässt, ohne dass er sich sinnlos beschäftigen muss. Der Nachteil dieser Methode besteht nämlich darin, dass er während des Zählens für nicht anderes zu gebrauchen ist. Da der Controller eine eingebaute Uhr hat (Timer), können wir später auf diesen zurückgreifen.

zurück