Informationen zu den Mikrocontrollern
Der Speicher enthält wortorganisierte Befehle und konstante Daten. Ein Befehlswort besteht aus zwei Byte (16 Bit). Ein ATtiny13x mit 1 kByte FLASH kann somit maximal 512 Befehle enthalten.
Nach einem Reset wird der auf Adresse 0x0000 liegende Befehl ausgeführt, danach folgen im Flash die Adressen von Interrupts, des Befehlsbereichs und dem Konstantenbereich.
Er enthält veränderbare Byte-Daten, die während eines Programmlaufs durch Befehlsaufrufe umgeschrieben werden können.
Zum SRAM gehören
Ein nicht flüchtiger Speicher, in dem nur Daten abgelegt werden, zum Beispiel während eines Programmablaufes.
Speicherdaten zum ATtiny13x (8 Pin DIL)
Speicherdaten zum ATmega8 (28 Pin DIL)
Bei einem ATmega8 sieht die detaillierte Adressbereich der Arbeitsregister, der SFR und des SRAM wie in der folgende Abb. 2a gezeigt aus:
Speicherdaten zum ATmega16 (40 Pin DIL)
Speicherdaten zum ATmega8515 (40 Pin DIL)
Die in meinen Übungen eingesetzten Mikrocontroller verfügen - je nach Controller-Typ - über unterschiedlich viele 8-Bit breite I/O‐Anschlüsse, sogenannte PORTS oder I/O Register, als Verbindung zur Außenwelt; sie werden als PORTB, PORTC usw. bezeichnet.
Genauere Informationen findet man im Datenblatt zum jeweilig eingesetzten Controller.
Der Stack wird hauptsächlich zum Speichern
verwendet.
Er wird am oberen Ende des SRAM über zwei 8 Bit Register (SPH, SPL) implementiert und wächst nach jedem Eintrag von oben nach unten.
Werden Daten mit einem
dann wird der Stapel-Zeiger um 1 dekrementiert.
Um 2 wird er dekrementiert, wenn die
auf den Stack geschoben wird.
Umgekehrt wird der Stapelzeiger um 1 erhöht, wenn
Er wird um 2 erhöht, wenn eine Adresse bei
entnommen wird.
Das nachfolgende einfache Programm macht nichts anderes als nur den Stack zu initialisieren.
Betrachten wir als erstes die Instruktion
Im AVR Instruction Manual werden alle Instruktionen der AVR-Controller beschrieben. Die LDI Instruktion ist ein 16-Bit Opcode mit folgendem Format:
Die 16-Bit der Instruktion LDI (load immediately) sind in vier Nibble aufgeteilt.
Nibble 3 ist der sogenannte OP-Code der Instruktion, hier also 0b1110.
Nibble 0 und 2 stellen acht Bit für eine Konstante K zur Verfügung. Es lassen sich damit Werte von 0 bis 255 darstellen.
Nibble 1 beschreibt das Zielregister (dddd -> 4 Bit), in das die Konstante geladen werden soll. Mit vier Bits lassen sich maximal 16 Register benennen: R16 - R31. Zur Darstellung der Zahlen 16 bis 31 benötigt man binär eigentlich 5 Bits, die alle im höchsten Bit eine 1 stehen haben. "Schneidet" man diese 1 ab, dann entspricht der Binärzahl 0b0000 das Register R16, 0b0001 das Register R17 usw.
Der Befehl
lädt in das Register hreg1 (R16) das higher Byte von Ramend und das ist 04. Der zugehörige binäre Maschinencode lautet:
Nibble 3 ist der OP-Code für die Instruktion LDI, in Nibble 2 und Nibble 0 steht die Konstante 2 und in Nibble 2 das Zielregister R16 mit der Adresse 0b0000.
Der Befehl
lädt in das Register hreg2 (R17) das lower Byte von Ramend, das ist 5F. Der zugehörige binäre Maschinencode lautet jetzt:
Gleich zu Anfang des Programms wurde die Direktive
eingebaut. Im Project-Manager von AVR Studio im Verzeichnis OUTPUT wird nach dem Kompilierungsvorgang eine Datei stackpointer.lst erzeugt. Mit einem Doppelklick öffnen wir sie.
Ganz links befinden sich zwei Spalten. Die schauen wir uns jetzt näher an. Die Spalte ganz links beginnt bei 000000 und gibt die Adresse im Programmspeicher an. Rechts daneben steht der Maschinencode, also das, was der Compiler aus unserem Assemblerprogramm "gemacht" hat. Der Maschinencode ist für jede Instruktion einzeln ausgewiesen.
Es sind genau die Ergebnisse, die wir oben gerade besprochen haben.
Auf die gleiche Weise lassen sich die beiden anderen Maschinenbefehle aus dem Assemblerprogramm herleiten. Dazu muss nur der OP-Code der Instruktion OUT (AVR Instruction Manual) und die Adresse des Stackpointer-Registers nachgeschlagen werden.
Die OUT Instruktion ist ein 16-Bit Opcode mit folgendem Format:
Nibble 3 ist der sogenannte OP-Code der Instruktion OUT.
Nibble 0 und 2 stellen sieben Bit für eine Zieladresse im I/O Registerraum. Es lassen sich damit Werte von 0 bis 63 eingeben. Das Highbyte des Stackpointers (SPH) hat die Adresse $3E oder 0b0011 1110, das Lowbyte $3D oder 0b0011 1101. Das höchste Bit in Nibble 2 ist vom Hersteller vorgegeben eine 1 und das niedrigste Bit ist das höchste Bit (Bit 5) für das in der Instruktion angesprochene Arbeitsregister R16 (0b10000).
Nibble 1 beschreibt die restlichen vier Bits des angesprochenen Arbeitsregisters (hier: 0000), aus dem Daten an die Zieladresse übertragen werden.
Der Befehl
wird als binärer Maschinencode zu:
Der Befehl
wird als binärer Maschinencode zu:
Im Programmlisting finden wir genau diese Eintragungen.
Auf die gleiche Weise lässt sich für die abschließende RJMP-Instruktion dem Instruction Manual entnehmen:
In den Nibbeln 0 - 2 steht die Anzahl der Zeilen, die innerhalb des Programmes zurück- oder vorgesprungen werden soll. In diesem Fall ist es ein Rücksprung um eine Programmzeile, also -1 oder 0b1111 1111 1111 oder $FFF.
Zusammen mit dem OP-Code ergibt sich als Maschinencode: $CF FF.
Der Maschinencode ist im Programmspeicher abgelegt und kann mit Hilfe des Debuggers im AVR-Studio sichtbar gemacht werden.
Der Maschinencode ist in little-endian-order im Programmspeicher ab Position $0000 abgelegt. Er setzt sich aus sechs Instruktionen zusammen: zwei LDI-, zwei OUT-Instruktionen und zwei RJMP-Instruktion.
Die Map-Datei
Im Verzeichnis Output wird nach dem Compilieren eines Programms eine Tabelle *.map angelegt, in der alle benutzten Symbole mit den ihnen zugeordneten Werten zu finden sind. Für das vorliegende Beispielprogramm finden sich am Ende der Liste folgende Eintragungen:
In der Zeile EQU takt ist die im Programm angegebenen Frequenz 1000000 als Hexadezimalzahl dargestellt. Die Registerbenennungen von r16 und r17 folgen.
im Codesegment (s. Abb. 6).