|
|
AVR-Tutorial: SRAM
[Bearbeiten] SRAM - Der Speicher des ControllersNachdem in einem der vorangegangenen Kapitel eine Software-PWM vorgestellt und in einem weiteren Kapitel darüber gesprochen wurde, wie man mit Schieberegistern die Anzahl an I/O-Pins erhöhen kann, wäre es naheliegend, beides zu kombinieren und den ATmega8 mal 20 oder 30 LEDs ansteuern zu lassen. Wenn es da nicht ein Problem gäbe: die Software-PWM hält ihre Daten in Registern, so wie das praktisch alle Programme bisher machten. Während allerdings 6 PWM-Kanäle noch problemlos in den Registern untergebracht werden konnten, ist dies mit 30 oder noch mehr PWM-Kanälen nicht mehr möglich. Es gibt schlicht und ergreifend nicht genug Register. Es gibt aber einen Ausweg. Der ATmega8 verfügt über 1kByte SRAM (statisches RAM). Dieses RAM wurde bereits indirekt durch den Stack benutzt. Bei jedem Aufruf eines Unterprogrammes, sei es über einen expliziten CALL (bzw. RCALL) oder einen Interrupt, wird die Rücksprungadresse irgendwo gespeichert. Dies geschieht genau in diesem SRAM. Auch PUSH und POP operieren in diesem Speicher. Ein Programm darf Speicherzellen im SRAM direkt benutzen und dort Werte speichern bzw. von dort Werte einlesen. Es muss nur darauf geachtet werden, dass es zu keiner Kollision mit dem Stack kommt, in dem z. B. die erwähnten Rücksprungadressen für Unterprogramme gespeichert werden. Da viele Programme aber lediglich ein paar Byte SRAM brauchen, der Rücksprungstack von der oberen Grenze des SRAM nach unten wächst und der ATmega8 immerhin über 1kByte SRAM verfügt, ist dies in der Praxis kein all zu großes Problem. [Bearbeiten] Das .DSEG und .BYTEUm dem Assembler mitzuteilen, dass sich der folgende Abschnitt auf das SRAM bezieht, gibt es die Direktive .DSEG (Data Segment). Alle nach einer .DSEG Direktive folgenden Speicherreservierungen werden vom Assembler im SRAM durchgeführt. Die Direktive .BYTE stellt dabei eine derartige Speicherreservierung dar. Es ermöglicht, der Speicherreservierung einen Namen zu geben und es erlaubt auch, nicht nur 1 Byte sondern eine ganze Reihe von Bytes unter einem Namen zu reservieren.
[Bearbeiten] spezielle BefehleFür den Zugriff auf den SRAM-Speicher gibt es spezielle Befehle. Diese holen entweder den momentanen Inhalt einer Speicherzelle und legen ihn in einem Register ab oder legen den Inhalt eines Registers in einer SRAM-Speicherzelle ab. [Bearbeiten] LDSLiest die angegebene SRAM-Speicherzelle und legt den gelesenen Wert in einem Register ab.
[Bearbeiten] STSLegt den in einem Register gespeicherten Wert in einer SRAM-Speicherzelle ab.
[Bearbeiten] BeispielEine mögliche Implementierung der Software-PWM, die den PWM-Zähler sowie die einzelnen OCR-Grenzwerte im SRAM anstelle von Registern speichert, könnte z. B. so aussehen:
[Bearbeiten] Spezielle Register[Bearbeiten] Der Z-Pointer (R30 und R31)Das Registerpärchen R30 und R31 kann zu einem einzigen logischen Register zusammengefasst werden und heisst dann Z-Pointer. Diesem kann eine spezielle Aufgabe zukommen, indem er als Adressangabe fungieren kann, von welcher Speicherzelle im SRAM ein Ladevorgang (bzw. Speichervorgang) durchgeführt werden soll. Anstatt die Speicheradresse wie beim LDS bzw. STS direkt im Programmcode anzugeben, kann diese Speicheradresse zunächst in den Z-Pointer geladen werden und der Lesevorgang (Schreibvorgang) über diesen Z-Pointer abgewickelt werden. Dadurch wird aber die SRAM-Speicheradresse berechenbar, denn natürlich kann mit den Registern R30 und R31, wie mit den anderen Registern auch, Arithmetik betrieben werden. Besonders komfortabel ist dies, da im Ladebefehl noch zusätzliche Manipulationen angegeben werden können, die oft benötigte arithmetische Operationen implementieren. [Bearbeiten] LD
Lädt das Register rxx mit dem Inhalt der Speicherzelle, deren Adresse im Z-Pointer angegeben ist. Bei den Varianten mit Z+ bzw. -Z wird zusätzlich der Z-Pointer nach der Operation um 1 erhöht bzw. vor der Operation um 1 vermindert. [Bearbeiten] LDD
Hier erfolgt der Zugriff wieder über den Z-Pointer wobei vor dem Zugriff zur Adressangabe im Z-Pointer noch das Displacement q addiert wird. Enthält also der Z-Pointer die Adresse $1000 und sei q der Wert $28, so wird mit einer Ladeanweisung
der Inhalt der Speicherzellen $1000 + $28 = $1028 in das Register r18 geladen. Der Wertebereich für q erstreckt sich von 0 bis 63. [Bearbeiten] ST
Speichert den Inhalt des Register rxx in der Speicherzelle, deren Adresse im Z-Pointer angegeben ist. Bei den Varianten mit Z+ bzw. -Z wird zusätzlich der Z-Pointer nach der Operation um 1 erhöht bzw. vor der Operation um 1 vermindert. [Bearbeiten] STD
Hier erfolgt der Zugriff wieder über den Z-Pointer wobei vor dem Zugriff zur Adressangabe im Z-Pointer noch das Displacement q addiert wird. Enthält also der Z-Pointer die Adresse $1000 und sei q der Wert $28, so wird mit einer Speicheranweisung
der Inhalt des Registers r18 in der Speicherzellen $1000 + $28 = $1028 gespeichert. Der Wertebereich für q erstreckt sich von 0 bis 63. [Bearbeiten] BeispielDurch Verwendung des Z-Pointers ist es möglich die Interrupt Funktion wesentlich kürzer und vor allem ohne ständige Wiederholung von im Prinzip immer gleichem Code zu formulieren. Man stelle sich nur mal vor wie dieser Code aussehen würde, wenn anstelle von 6 PWM Stufen, deren 40 gebraucht würden. Mit dem Z-Pointer ist es möglich diesen auf das erste der OCR Bytes zu setzen und dann in einer Schleife eines nach dem anderen abzuarbeiten. Nach dem Laden des jeweiligen OCR Wertes, wird der Z-Pointer automatisch durch den LD-Befehl auf das nächste zu verarbeitende OCR Byte weitergezählt.
[Bearbeiten] X-Pointer, Y-PointerNeben dem Z-Pointer gibt es noch den X-Pointer bzw. Y-Pointer. Sie werden gebildet von den Registerpärchen
Alles über den Z-Pointer gesagte gilt sinngemäß auch für den X-Pointer bzw. Y-Pointer mit einer Ausnahme: Mit dem X-Pointer ist kein Zugriff LDD/STD mit einem Displacement möglich. [Bearbeiten] Siehe auch |