|
|
AdressierungMikrocontroller und -prozessoren bieten in der Regel mehrere Möglichkeiten an, um auf Daten zuzugreifen. An dieser Stelle sollen die grundlegenden Adressierungsarten der AVR-Controller mit internem SRAM behandelt werden.
[Bearbeiten] Immediate-WerteEigentlich keine Adressierungsart, aber dennoch sehr wichtig, ist die Möglichkeit direkt konstante Werte in ein Register zu schreiben. Dabei ist schon zur Entwicklungszeit bekannt, welcher Wert in welches Register geladen werden soll.
ldi steht hierbei für load immediate. Bei AVR-Mikrocontrollern ist das direkte Laden von Werten nur mit den Registern r16 bis r31 möglich. [Bearbeiten] Direkte AdressierungUm auf Daten im Speicher zuzugreifen, muss man selbstverständlich wissen, wo sich diese Daten befinden. Will man z. B. den Inhalt eines Registers in eine Speicherzelle schreiben, so muss das Mikroprogramm die Adresse der gewünschten Speicherzelle kennen. Eine einfache Möglichkeit der Adressierung ist es, dem Befehl die Adresse direkt mitzuteilen.
Die Adresse der Speicherzelle wird also schon zur Entwicklungszeit im Assembler-Befehl eingetragen, was nach sich zieht, dass so ein Befehl nur auf Speicherzellen zugreifen kann, deren Adressen schon im Vorhinein bekannt sind. Da variable in obigem Beispiel eine Adresse und somit nur eine Zahl darstellt, kann man zur Entwicklungszeit auch Konstanten addieren:
Nun steht in diesem Beispiel im ersten Byte die Zahl 17 und im zweiten Byte die Zahl 18. Dabei muss man beachten, dass die Addition im sts-Befehl bereits während der Assemblierung und nicht vom Mikrocontroller durchgeführt wird. Das ist der Fall, weil die Adresse der reservierten Speicherzelle schon zu dieser Zeit berechnet worden ist. Somit ist natürlich auch die Adresse + 1 bekannt. [Bearbeiten] Indirekte AdressierungWenn wir nur die direkte Adressierung zur Verfügung haben, stoßen wir schnell an Grenzen. Betrachten wir folgendes Beispiel: Wir sollen Code schreiben, welcher eine variable Anzahl an Zahlen addieren soll. Die Zahlen stehen bereits hintereinander im Speicher, beginnend mit der Adresse zahlen_start, und im Register r16 steht, wie viele Zahlen es sind. Man merkt leicht, dass dies mit direkter Adressierung nur schwer möglich ist, denn es ist zur Entwicklungszeit noch nicht bekannt, wie viele Zahlen es sind. Wir lösen diese Aufgabe, indem wir eine Schleife programmieren, die die Zahlen liest und aufaddiert, und das ganze so oft, wie im Register r16 steht. Da wir hier von einer Schleife reden, müssen wir bei jedem Lesen aus dem Speicher mit demselben Befehl auf eine andere Speicherzelle zugreifen. Wir brauchen also die Möglichkeit die Adresse dynamisch im Programmablauf zu ändern. Dieses bietet uns die indirekte Adressierung, bei der die Adresse der gewünschten Speicherstelle in einem Register steht. Bei AVR-Mikrocontrollern gibt es dafür drei 16 Bit breite Register, die jeweils aus zwei 8-Bit-Registern bestehen. Dies rührt daher, dass ein 8-Bit-Register nur maximal 256 verschiedene Speicherzellen adressieren könnte, was für Mikrocontroller mit mehr Speicher nicht ausreicht. Die Register (r26, r27) und (r28, r29) und (r30, r31) bilden die besagten drei 16 Bit breiten Register zur indirekten Adressierung. Da diese Register auf Daten zeigen, nennt man sie logischerweise Zeigerregister (engl. Pointer). Sie tragen die Namen X, Y und Z, wobei die einzelnen 8-Bit-Register neben ihren rxx-Namen auch mit XL, XH, YL, YH, ZL und ZH angesprochen werden können. L(low) und H(high) bedeutet hierbei dass die unteren respektive die oberen 8 Bits der 16-Bit-Adresse gemeint ist.
Wir werden beispielhalber das Z-Register für unser Problem verwenden. Dazu müssen wir zunächst die Adresse der ersten Zahl in dieses laden. Da das Z-Register 16 Bit breit ist, müssen wir ZH und ZL in zwei einzelnen ldi Operationen beschreiben. Der AVR-Assembler bietet uns hier zwei praktische Funktionen: Mit LOW(...) und HIGH(...) bekommt man die unteren respektive die oberen 8 Bit einer Speicheradresse. Das kommt uns gerade recht, da wir gerade die unteren/oberen 8 Bit der Adresse in die Register ZL/ZH schreiben wollen. Dann können wir mit dem ld-Befehl die Zahl von der Speicherstelle lesen, auf die das Z-Register verweist. Wir schreiben den Wert in das Register r17. Zum Aufsummieren wollen wir das Register r18 verwenden, welches ganz zu Anfang mit clr auf 0 gesetzt wird.
Das Programm funktioniert zwar schon, aber eine Sache ist unpraktisch: Das Z-Register muss jedes mal manuell inkrementiert werden, um im nächsten Schleifendurchlauf die nächste Zahl zu lesen. Da das sequenzielle Lesen oder Schreiben von Daten aus dem bzw. in das SRAM sehr oft in Programmen vorkommt, gibt es folgende Möglichkeiten: [Bearbeiten] PostinkrementDie beiden Zeilen
können durch folgende Zeile ersetzt werden:
Das spart Ausführungszeit und macht den Code kürzer. Zu beachten ist, dass die Inkrementierung erst nach der Ausführung des eigentlichen Befehls durchgeführt wird. [Bearbeiten] PredekrementÄquivalent zum Postinkrement gibt es auch die Möglichkeit des Dekrementierens. Hierbei wird der Wert jedoch vor der Ausführung des Befehls dekrementiert. Das Predekrement eignet sich, um rückwärts durch linear angeordnete Daten zu gehen.
|