Hallo AVR C++

Die erste Übung in jedem Programmierkurs ist das berühmte „Hallo Welt“. Damit wird versucht dem Lernenden ein motivierendes „AHA-Erlebnis“ zu vermitteln. OK, mal sehen ob wir das auch hin bekommen. Bei der Programmierung von eingebetteten Systemen besteht oft das Problem, dass kein Bildschirm oder Display zur Textausgabe angeschlossen ist. Dann stehen für das sich bemerkbar machen dem System nur LEDs zur Verfügung. Also leuchten und blinken eingebettete Systeme somit ihre Botschaft in die Welt.

Die erste Übung soll das typische LED einschalten sein. Dazu verbinden Sie bitte vom Port-B das Bit-0 mit der roten LED, indem Sie eines der kleinen Patchkabel benutzen.

Auch auf die Gefahr hin Sand in die Wüste zu tragen hier eine kurze Wiederholung zur Bedeutung der GPIO (General Purpose Input/Output) Register des AVR. Die GPIO-Ports sind beim AVR mit Buchstaben gekennzeichnet. Der ATmega8 verfügt über die Ports B, C und D. Zu jedem dieser Ports gehören die folgenden drei Register:

  • DDRx, das ist keine Ostalgie sondern bedeutet Data Direction Register, damit legt man die Portfunktion Input oder Output fest
  • PORTx, Port finde ich eigentlich nicht so sexy, POUTx für Port Output gefällt mir besser, aber nach mir geht es ja nicht
  • PINx, PIN steht für Port Input Regsiter, damit kann man also Eingaben realisieren

Für das Einschalten unser LED ist es jetzt erforderlich im entsprechenden Daten-Richtungs-Register auf das betreffende Bit eine 1 zu schreiben. In myAVR C++ können wir das mit einer recht modernen Schreibweise tun. Dabei ist zu beachten, dass zur Unterscheidung gegenüber der klassischen Großschreibweise der Port-Register die neue Schreibweise mit kleinen Buchstaben beginnt. Der Zugriff auf die einzelnen Bits erfolgt über den Punktoperator und den Bitnamen. Das befreit uns an dieser Stelle in einem gewissen Umfang von dem bisher üblichen Bitgeschubse (&|^=~1«0).

   ddrB.bit0 = 1;  // Port B Data Direction Register Bit 0 = Output
   ddrD.bit2 = 0;  // Port D Data Direction Register Bit 2 = Input (Standard nach RESET)

Die eigentliche Ausgabe erfolgt über das dafür zuständige Register. Wir verwenden dabei wiederum die objektorientierte Schreibweise.

   portB.bit0 = 1;  // Port B Output Bit 0 = High

Legen Sie ein neues kleines Programm an, überprüfen Sie die Einstellung für die Sprache AVR C++ und den Controller ATmega8 mit 3,6864 MHz. Laden Sie die Vorlage für die Grundstruktur einer AVR C++ Anwendung. Das gesamte Programm sollte wie unten dargestellt ergänzt werden. Dabei erfolgt die Konfiguration des Ports in der Operation onStart und die Ausgabe in der Operation onWork. Zunächst machen wir uns einen Plan von dem, was zu tun ist. Diesen notieren wir als Kommentar an die betreffenden Stellen in der Vorlage.

//////////////////////////////////////////////////////////
// Hallo myAVR C++ ENTWURF
//////////////////////////////////////////////////////////
class Application : public Controller
{
	public: void onStart()
	{
		// Port B Bit 0 auf Ausgang konfigurieren
	}
 
	public: void onWork()
	{
		// Port B Bit 0 Ausgang auf High schalten
	}
} app;
//////////////////////////////////////////////////////////

Es ist gut, sich den Plan noch mal durch den Kopf gehen zu lassen oder, wenn man sich unsicher ist diesen mit jemandem, der sich auskennt zu besprechen. Erst jetzt sollte nach den geeigneten Befehlen gesucht werden den Plan umzusetzen. Schreiben Sie zu den Kommentaren die entsprechenden Befehle.

//////////////////////////////////////////////////////////
// Hallo myAVR C++
//////////////////////////////////////////////////////////
class Application : public Controller
{
	public: void onStart()
	{
		// Port-B Bit-0 auf Ausgang konfigurieren
		ddrB.bit0 = 1;
	}
 
	public: void onWork()
	{
		// Port-B Bit-0 Ausgang auf High schalten
		portB.bit0 = 1;
	}
} app;
//////////////////////////////////////////////////////////

Kompilieren, linken und brennen Sie das Programm. Die rote LED sollte jetzt leuchten.

Und weil es so schön war, variieren wir unser Hallo-Welt-Programm etwas und lassen die LED blinken. Dazu muss sich gedanklich klar gemacht werden wie ein Blinken funktioniert.

Um die LED blinken zu lassen, muss also die folgende Sequenz ständig wiederholt werden:

  1. LED an
  2. warte einige Millisekunden
  3. LED aus
  4. warte noch mal

Damit diese Befehlsfolge immer wieder ausgeführt wird, programmieren wir diese einfach in die Operation onWork, da diese ja vom Framework zyklisch aufgerufen wird. Die Wartezeit können wir als Attribut der Applikation programmieren, welches wir in der Operation onStart initialisieren. Für das einfache Warten gibt es in myAVR C++ die Funktionen waitMs(Millisekunden) und waitUs(Mikrosekunden). Da das Auge sehr träge ist, benötigen wir Wartezeiten von einigen Millisekunden, um das Blinken wahrzunehmen.

Zuerst der Plan!

//////////////////////////////////////////////////////////
// Blinklicht 1 ENTWURF
//////////////////////////////////////////////////////////
class Application : public Controller
{
	// eine Variable für die Wartezeit, 16 Bit da Wert größer 255 sein kann
 
	public: void onStart()		// hier die Initalisierungen
	{
		// Port B Bit 0 auf Ausgang konfigurieren
		// Wartezeit auf eine halbe Sekunde setzen
	}
 
	public: void onWork()		// führe zyklisch aus:
	{
		// 1. LED an
                // 2. warte einige Millisekunden
		// 3. LED aus
                // 4. warte nochmal
	}
 
} app;
//////////////////////////////////////////////////////////

Dann die Umsetzung.

//////////////////////////////////////////////////////////
// Blinklicht 1
//////////////////////////////////////////////////////////
class Application : public Controller
{
	protected: uint16 wartezeit;	// 16 Bit Wert da größer 255
 
	public: void onStart()		// hier die Initalisierungen
	{
		ddrB.bit0 = 1;		// Port B Bit 0 auf Ausgang konfigurieren
		wartezeit = 500;	// Wartezeit auf eine halbe Sekunde setzen
	}
 
	public: void onWork()		// führe zyklisch aus:
	{
		portB.bit0 = 1;		// 1. LED an
                waitMs(wartezeit);	// 2. warte einige Millisekunden
		portB.bit0 = 0;		// 3. LED aus
                waitMs(wartezeit);	// 4. warte nochmal
	}
 
} app;
//////////////////////////////////////////////////////////

Kompilieren, linken und brennen Sie das Programm. Die rote LED sollte jetzt blinken. Sie können die Blinkgeschwindigkeit über das Attribut wartezeit variieren.

Zusammenfassung als Video

Nächstes Thema