Grundlagen der Mikrocontrollerprogrammierung

 

Die Programmierung von Mikrocontrollern ist großenteils das Setzen von Bits in bestimmten Registern des Mikrocontrollers. Ein Register ist ein Speicherbaustein, der einen schnellen Zugriff erlaubt, aber seinen Wert bei einem Neustart verliert. Bestimmte Register eines Mikrocontrollers sind mit der Hardware des Mikrocontrollers verbunden. Hardware Baugruppen können somit über Register per Software angesprochen werden. Die Mikrocontroller der 8-Bit ATMEGA Familie besitzen pro Speicheradresse 8 Speicherbits (8 Bit-Register), die mit 1 oder 0 belegt werden können (siehe Bild rechts).

Das C-Programm oder das Assemblerprogramm wird im Program Flash Memory gespeichert. Beim Einschalten des Mikrocontrollers wird das Programm von oben nach unten abgearbeitet. Dabei werden je nach Programm Register belegt um z.B. eine Signalleitung (Portleitung) zu aktivieren (Ausgabe eines Spannungssignals) oder Signale von außen einzulesen.

 

Merke:

Die komplette Steuerung der Hardware eines Mikrocontrollers (Ausgänge, Eingänge, Timer, A/D Wandler, ...) wird per Software über das Setzen von Bits in Registern durchgeführt. Die Register, die zur Steuerung der Hardwarebaugruppen dienen befinden sich im I/O Memory (z.B. gibt es beim ATMEGA 32 64 I/O Memory Register).

 

Zugriff auf Register mit vordefinierten Registernamen

Jedes Register ist über eine eindeutige Speicheradresse ansprechbar. Eine Speicheradresse ist nichts weiteres als der Ort (angegeben in hexadezimaler Schreibweise) einer Zeile im Speicher, die aus 8 Bits besteht, z.B. 0x03 → 4. Zeile im Speicher. Um die Programmierung und die Lesbarkeit eines Programms zu vereinfachen ist jeder Speicherzelle ein sinnvoller Name zugeordnet. Der Name richtet sich nach der Funktion der Speicherzelle, also danach, ob und welche Hardwarebausteine mit dieser Speicherzelle verknüpft sind.

Im folgenden Bild ist ein einfaches Programm dargestellt. Es wird die Header-Datei <avr/io.h> eingebunden. In dieser sind alle Registernamen als Konstanten definiert (z.B. PORTB → 8 Leitungen, die als Ein- oder Ausgänge definiert sein können). Durch die Definition mit #define werden vor der Compilierung des C-Programms alle Konstanten mit dem Wert ersetzt, der in der Header Datei angegeben wird, z.B. werden im C-Programm alle PORTB mit _SFR_IO(0x18) ersetzt. Der Wert gibt die Speicheradresse des Registers im RAM an. Somit ist das Programm deutlich lesbarer, als wenn direkt im Quellcode die entsprechende Speicherzelle angegeben werden muss.

Das Programm kann also folgendermaßen gelesen werden:

  • DDRB = 0b00000001     → setze das 1. Bit im Data Directory Register von Port B auf 1 -> die 1. Leitung des Port B ist als Ausgang definiert.
  • while(1)                         → Beginne eine Endlosschleife
  • PORTB = 0b00000001   → Gebe ein Spannungssignal auf der 1. Leitung aus
  • _delay_us(50)               → Warte 50 μs
  • PORTB = 0b00000000   → Setze das Spannungssignal auf der 1. Leitung wieder auf 0 V
  • _delay_us(50)               → Warte 50 μs
  • Beginne von vorne

Wenn an der 1. Leitung von PORTB z.B. eine LED angeschlossen ist, dann blinkt diese im Abstand von 50 μs.

 

Merke:

Die per Software ansprechbaren Registernamen können dem Datenblatt eines jeden Mikrocontrollers entnommen werden.

 

Im Folgenden ist ein Auszug aus dem Datenblatt des ATMEGA 32 Mikrocontrollers dargestellt (S. 327):

 

Grundgerüst eines C Programms für ATMEGA Mikrocontroller

Für die Programmierung eines ATMEGA Mikrocontrollers ist das unten dargestellte Programmgrundgerüst zu empfehlen. Der Programmcode, also die Funktionalität des Programms wird in die Endlosschleife geschrieben. Eine Endlosschleife ist notwendig, da sonst das Programm schon nach einem Durchlauf beendet ist und der Mikrocontroller kein Programm mehr ausführt! Konfigurationen oder Befehle, die nur einmalig ausgeführt werden sollen, müssen vor die Endlosschleife geschrieben werden (hier z.B. DDRD = 0b00000001 → 1. Leitung des Ports D ist als Ausgang konfiguriert).