8.   Speicherarten

8.1   Übersicht

Dieser Abschnitt beschreibt die verschiedenen Speicher des ATmegaX8. Die AVR-Architektur enthält zwei Hauptspeicher, den Datenspeicher und den Programmspeicher. Zusätzlich steht noch ein EEPROM-Speicher für die dauerhafte Sicherung von Daten zur Verfügung. Alle drei Speicherbereiche sind linear und regulär.

8.2   Programmspeicher

Der ATmegaX8 enthält 4/8/16/32 KByte Programmspeicher, der als Flash ausgeführt ist und im System programmiert werden kann. Da alle AVR-Befehle 16 oder 32 Bit lang sind, ist der Programmspeicher in 2/4/8/16 K Speicherzellen mit je 16 Bit organisiert. Aus Gründen der Softwaresicherheit ist der Programmspeicher beim ATmega88/168/328 in zwei Bereiche unterteilt, den Urlader-Bereich und dem Anwendungs-Bereich. Siehe SPMEM-Register-Beschreibung.

Der Flash hat eine Haltbarkeit von mindestens 10.000 Schreib-/Löschzyklen. Der Programmzähler (PC) ist 11/12/13/14 Bit breit. Damit können alle Speicherzellen im Programmspeicher adressiert werden. Die Funktionen des Urlader-Bereichs und den dazugehörigen Urlader-Sperrbits sind für ATmega48 sowie ATmega88/168/328 getrennt beschrieben. Die Programmierung des Programmspeichers im SPI- und Parallel-Modus ist ebenfalls in einem hinteren Kapitel beschrieben.

Datentabellen können im gesamten Programmspeicherbereich hinterlegt werden (siehe LPM-Befehl).

Die Zeitabläufe für das Auslesen und Ausführen von Befehlen sind in den Seiten zuvor abgebildet.

Bild 8-1: Programmspeicher des ATmega48
Bild 8-2: Programmspeicher des ATmega88/168/328

8.3   Datenspeicher

Bild 8-3 zeigt, wie der Datenspeicher (SRAM) aufgeteilt ist.

Dieser Controller hat mehr Peripherie als es die 64 Bytes I/O-Adressraum der AVR-Struktur zulässt. Daher gibt es Erweiterten I/O-Adressraum im Adressbereich 0x60..0xFF im SRAM, mit dem auf die Peripherie nur mit den Befehlen ST/STS/STD und LD/LDS/LDD zugegriffen werden kann.

Die 768/1280/2303 Zellen des Datenspeichers enthalten die 32 Register, den I/O-Adressraum, den Erweiterten I/O-Adressraum und den eigentlichen internen SRAM-Datenspeicher. Die ersten 32 Zellen adressieren die Register, weitere 64 den I/O-Adressraum, weitere 160 den Erweiterten I/O-Adressraum, und schließlich 512/1024/2048 Bytes Datenspeicher.

Es gibt fünf verschiedene Möglichkeiten die Speicherzellen zu adressieren: Direkt, indirekt mit Versatz, indirekt, indirekt mit vorherigem Dekrement und indirekt mit anschließendem Inkrement. Die Register R26 bis R31 bilden die Zeiger für die vier indirekten Adressierungsarten.

Mit der direkten Adressierung kann der gesamte Speicher angesprochen werden.

Mit der Methode der indirekten Adressierung mit Versatz können, ausgehend von der Basisadresse, die durch das Y- oder Z-Register vorgegeben ist, 63 Speicherzellen adressiert werden.

Wenn die Methode der indirekten Adressierung mit vorherigem Dekrement oder anschließendem Inkrement genutzt werden, bilden das X-, Y- oder Z-Register den Adresszeiger, der inkrementiert bzw. dekrementiert wird.

Die 32 Register, die 64 I/O-Speicher und die 1024 Byte des internen Datenspeichers können mit allen Adressierungsarten angesprochen werden.

Bild 8-3: Daten-Adressraum-Aufteilung
Datenspeicher
32 Register0x0000 - 0x001F
64 I/O-Register0x0020 - 0x005F
160 Erweiterte I/O-Register0x0060 - 0x00FF

Interner SRAM
512/1024/2048 × 8
 
0x0100
0x02FF/0x04FF/0x08FF

8.3.1   Zugriffszeiten

Auf den internen SRAM-Datenspeicher wird in zwei Zyklen des Systemtaktes clkCPU zugegriffen.
Bild 8-4: Taktzyklen beim Zugriff auf den internen SRAM

8.4   EEPROM-Speicher

Der Controller enthält 256/512/1024 Byte EEPROM-Speicher, der in einem separaten Adressraum organisiert ist. Jedes Byte des EEPROM kann einzeln gelesen oder geschrieben werden. Die Haltbarkeit des EEPROM beträgt mindestens 100.000 Schreib-/Löschzyklen. Das Zusammenspiel zwischen CPU und EEPROM wird nachfolgend beschrieben. Dafür werden das EEPROM-Adressregister, das EEPROM-Datenregister und das EEPROM-Kontrollregister verwendet, die im I/O-Adressraum untergebracht sind.

Das EEPROM kann ähnlich wie der Programmspeicher auch durch die ISP oder im parallelen Programmiermodus programmiert werden, siehe Programmieren des Speichers.

Praxistipp: Beim Compiler avrgcc stehen in der Kopfdatei „avr/eeprom.h“ Routinen zum Lese- und Schreibzugriff zur Verfügung, beispielsweise eeprom_read_block(). Dabei ist es häufig günstig, im RAM eine Kopie des EEPROM-Speicherbereiches zu halten, der zur allgemeinen Konfiguration dient. Das Rückschreiben von Änderungen kann dann die Hauptschleife „nebenbei“ erledigen, indem sie Byte für Byte vergleicht (geht schnell) und bei Ungleichheit ein Byte schreibt (dauert lange) und sofort zurückkehrt. So hält die Hauptschleife RAM und EEPROM auf Dauer konsistent, ohne sich um jedes einzelne Byte kümmern zu müssen oder gar zu blockieren. (Hierfür steht leider keine avr-libc-Funktion zur Verfügung.)

8.4.1   Lese- und Schreibzugriff

Die Register für den EEPROM Zugriff liegen im I/O-Adressraum.

Die Zeit für den schreibenden Zugriff auf das EEPROM ist in nachfolgender Tabelle angegeben. Mit selbst programmierten Zeitfunktionen kann die Anwendersoftware überwachen, ob ein Schreibvorgang abgeschlossen wurde und somit das nächste Byte in den EEPROM geschrieben werden kann. Wenn das EEPROM genutzt wird, sind ein paar Vorsichtsmaßnahmen zu beachten. In stark gefilterten Spannungsversorgungen neigt UCC dazu, sehr langsam zu steigen bzw. zu fallen, wenn die Spannungsversorgung ein- bzw. ausgeschaltet wird. Das heißt, dass der Baustein für eine gewisse (verhältnismäßig lange) Zeit in einem Spannungsbereich arbeitet, der kleiner als das spezifizierte Minimum ist, in der der Takt arbeitet. Wie in solchen Fällen ein Fehlverhalten verhindert werden kann, ist unter Schutzmaßnahmen beschrieben.

Ferner ist, um das ungewollte Beschreiben des EEPROMs zu verhindern, ein Schreibvorgang nur in zwei Schritten durchführbar (siehe EEPROM-Kontrollregister).

Wenn der EEPROM gelesen wird, wird die CPU für vier Taktzyklen angehalten, bevor der nächste Befehl ausgeführt wird. Beim Schreiben des EEPROM wird die CPU für zwei Taktzyklen angehalten, bevor der nächste Befehl ausgeführt wird.

8.4.2   Verfälschten EEPROM vermeiden

In Zuständen, in denen UCC zu niedrig ist, um die CPU und das EEPROM richtig arbeiten zu lassen, können die EEPROM-Daten verfälscht werden. Dies gilt auch für externe EEPROMs. Daher sind in beiden Fällen die gleichen schaltungstechnischen Lösungen erforderlich.

Ein Verfälschen der EEPROM-Daten kann durch zwei Situationen ausgelöst werden, in denen UCC zu niedrig ist. Erstens benötigt ein regulärer Schreibvorgang eine Mindestspannung, um einen korrekten Schreibvorgang durchzuführen. Zweitens kann es dazu kommen, dass die CPU selbst in der Ausführung der Befehle unsauber arbeitet, wenn die Versorgungsspannung zu gering ist.

Das Verfälschen der EEPROM-Daten kann auf einfache Weise verhindert werden, wenn folgende Design-Empfehlungen angewendet werden.

Den RESET-Eingang auf Low halten, solange die Spannungsversorgung unzureichend ist. Dies kann durch einen externen Resetbaustein erfolgen oder durch die interne Unterspannungsüberwachung. Wenn der Level der internen Unterspannungsdetektors nicht ausreicht, kann auch eine externe Resetschaltung verwendet werden, die einen Spannungsabfall erkennt. Wenn ein Reset während eines laufenden Schreibvorganges auftritt, so wird dieser noch zu Ende geführt, vorausgesetzt die Versorgungsspannung ist noch ausreichend.

8.5   I/O-Adressraum

Die Belegung des I/O-Adressraum ist in dieser Zusammenfassung dargestellt.

Alle I/O-Funktionen und peripheren Einheiten sind im I/O-Speicher platziert. Auf die I/O-Speicher wird mit den IN- und OUT-Befehlen zugegriffen, die Daten zwischen den Registern und den I/O-Speicherzellen transportieren. Die I/O-Speicher im Adressbereich zwischen 0x00 (0x20) und 0x1F (0x3F) sind bitadressierbar. Das heißt, dass einzelne Bits in diesen Speichern mit den SBI- und CBI-Befehlen gesetzt oder gelöscht werden können. Der Zustand der einzelnen Bits kann mit den Befehlen SBIS und SBIC abgefragt werden.

Wenn die I/O-Speicher mit den IN- und OUT-Befehlen angesprochen werden, dann müssen die Adressen 0x00 bis 0x3F verwendet werden. Wenn die I/O-Speicher mit den Befehlen LD und ST über die Adressen des Datenspeichers angesprochen werden, so muss der Wert 0x20 zu den ursprünglichen Adressen hinzuaddiert werden (Adressen 20h bis 5Fh im Datenspeicher). Dieser Controller hat mehr Peripherie als es die 64 Bytes I/O-Adressraum der AVR-Struktur zulässt. Auf den Erweiterten I/O-Adressraum im Adressbereich 0x60..0xFF kann nur mit den Befehlen ST/STS/STD und LD/LDS/LDD zugegriffen werden kann.

Um die Kompatibilität mit zukünftigen Bausteinen zu gewährleisten, werden reservierte Bits als Null gelesen, wenn auf diese zugegriffen wird. Reservierte Adressen im I/O-Adressraum dürfen nicht beschrieben werden.

Einige Statusbits können gelöscht werden, indem sie mit einer logischen 1 beschrieben werden. Dass heißt, dass durch Zurückschreiben einer 1 in ein Bit, das zuvor als 1 gelesen wurde, dieses Bit gelöscht wird. Im Gegensatz zu anderen AVR-Controllern kann dies auch mit den Befehlen SBI und CBI geschehen.

Die Übersicht über alle I/O-Register ist gegen Ende des Dokumentes abgebildet.

8.5.1   Allgemeine I/O-Register

Der Mikrocontroller enthält drei allgemeine I/O-Register namens GPIOR0, GPIOR1 und GPIOR2. Diese können zur Speicherung irgendwelcher Daten verwendet werden. Dabei ist GPIOR0 bitadressierbar mit den Befehlen SBI, CBI, SBIS und CBIS.

6   Register-Beschreibung

8.6.1   EEAR — EEPROM-Adresse

Bit15141312111098
(0x42)------EEAR9(1)EEAR8(1)EEARH
(0x42)EEAR7EEAR6EEAR5EEAR4EEAR3EEAR2EEAR1EEAR0EEARL
76543210
ZugriffRRRRRRR/WR/W
ZugriffR/WR/WR/WR/WR/WR/WR/WR/W
Startwert000000XX
XXXXXXXX
Diese Bits sind reserviert und werden immer als 0 gelesen. In den EEPROM-Address-Registern steht die Adresse des Bytes, auf das im EEPROM zugegriffen werden soll. Die 256/512/1024 Byte sind linear im Adressraum von 0 bis 255/511/1023 verteilt. Beim Einschalten der Versorgungsspannung ist der Wert der Adress-Bits undefiniert. Es muss also auf jeden Fall erst eine Adresse in die Register geschrieben werden, bevor auf den EEPROM zugegriffen werden kann.
Hinweis:
    EEAR9 und EEAR8 sind beim ATmega48 nicht implementiert und müssen mit Null beschrieben werden.

8.6.2   EEDR — EEPROM-Daten

Bit76543210
(0x40)MSBLSBEEDR
ZugriffR/WR/WR/WR/WR/WR/WR/WR/W
Startwert00000000
Dieses Register enthält die Daten, die in das EEPROM geschrieben oder aus diesem ausgelesen werden. In beiden Fällen muss zuvor die Adresse der Speicherzelle im EEPROM, in die geschrieben bzw. aus der gelesen werden soll, in EEAR angegeben werden.

8.6.3   EECR — EEPROM-Kontrolle

Bit76543210
(0x3F)--EEPM1EEPM0EERIEEEMPEEEPEEEREEECR
ZugriffRRR/WR/WR/WR/WR/WR/W
Startwert00XX00X0
Diese Bits sind reserviert und werden immer als 0 gelesen. Diese Bits wählen aus, welche Aktion vom Bit EEPE ausgelöst wird. Es ist möglich, ein EEPROM-Byte in einer atomaren Operation zu löschen und zu schreiben oder diese beiden Funktionen aufzuteilen. Die Ausführungszeiten sind in Tabelle 8-1 aufgeführt. Solange EEPE gesetzt ist, wird der Schreibzugriff auf EEPMn ignoriert. Nach Reset sind diese Bits Null, es sei denn, der EEPROM war mit Programmieren beschäftigt.
EEPM1EEPM0ProgrammierzeitOperation
003,4 msLöschen und Schreiben in einer atomaren Operation
011,8 msnur löschen
101,8 msnur schreiben
11reserviert für später
Wenn das I-Bit im SREG-Register und das EERIE-Bit gesetzt sind, ist der EEPROM-bereit-Interrupt freigegeben. Wenn das Bit gelöscht ist, dann ist der Interrupt gesperrt. Der EEPROM-bereit-Interrupt generiert eine permanente Interruptanforderung, wenn das EEPE-Bit gelöscht ist. Dieses Bit muss gesetzt werden, wenn das EEPE-Bit zum Beschreiben des EEPROM gesetzt werden soll. Wenn EEMPE gesetzt ist, bewirkt das Setzen des EEPE-Bits, dass die Daten im EEDR-Register in die Adresse, die im EEAR-Register steht, geschrieben werden. Wenn das EEMPE-Bit gesetzt wurde, wird es durch die Hardware nach 4 Taktzyklen automatisch wieder gelöscht. Es bleibt also nur ein kleines Zeitfenster, um den eigentlichen Schreibvorgang zu starten. Wenn die Adresse und die Daten korrekt eingestellt wurden, muss das EEPE-Bit gesetzt werden, um den Schreibvorgang zu starten, mit dem die Daten in das EEPROM geschrieben werden. Bevor das EEPE-Bit gesetzt wird, muss vorher das EEMPE-Bit auf 1 gesetzt werden, da ansonsten kein Schreibvorgang stattfindet. Der nachfolgende Ablauf muss eingehalten werden, wenn das EEPROM beschrieben werden soll (Die Schritte 3 und 4 sind von der Reihenfolge her egal und können auch schon vorher ausgeführt werden.)
  1. Warten, bis das EEPE-Bit Null wird
  2. Warten, bis das SPMEN-Bit im SPMCR-Register Null wird
  3. Schreiben der EEPROM-Adresse in EEAR (bei Bedarf)
  4. Schreiben der EEPROM-Daten in EEDR (bei Bedarf)
  5. Schreiben einer 1 in das EEMPE-Bit
  6. Schreiben einer 1 in das EEPE-Bit innerhalb von 4 Taktzyklen
Der EEPROM kann nicht programmiert werden, während die CPU in den Flash-Speicher schreibt. Die Software muss also überprüfen, ob die Programmierung des Flash-Speichers abgeschlossen ist, bevor ein EEPROM-Schreibvorgang gestartet werden kann. Schritt 2 ist daher nur relevant, wenn die Software im Boot-Sektor erlaubt, den Flash-Speicher zu beschreiben. Wenn in der Software das Beschreiben des Flash-Speichers nicht vorkommen kann, dann kann Schritt 2 entfallen.

Achtung: Ein Interrupt zwischen Schritt 5 und 6 wird zu einem fehlerhaften Schreibversuch führen, da das Zeitfenster von EEMPE dann überschritten wird. Wenn eine Interruptroutine auf das EEPROM zugreift, wird dadurch ein laufender Zugriff unterbrochen, die Register EEAR und EEDR werden verändert und der durch den Interrupt unterbrochene Zugriff misslingt. Es ist daher zu empfehlen, die Interrupts während der Schritte 4 und 5 global zu sperren.

Wenn der Schreibvorgang beendet ist, wird das EEPE-Bit durch die Hardware automatisch gelöscht. Der Zustand des Bits kann also durch die Software permanent abgefragt werden, um das Ende des Schreibvorganges zu erkennen. Wenn das EEPE-Bit mit einer 1 gesetzt wurde, wird die CPU für zwei Takte angehalten, bevor der nächste Befehl ausgeführt wird.

Wenn die Adresse korrekt eingestellt wurde, muss das EERE-Bit gesetzt werden, um den Lesevorgang zu starten. Das Bit wird durch die Hardware gelöscht, wenn die Daten ausgelesen und im EEDR abgelegt wurden. Wenn das EERE-Bit gesetzt wurde (also das Auslesen beginnt) wartet die CPU 4 Taktzyklen lang bevor der nächste Befehl ausgeführt wird.

Der Anwender sollte das EEPE-Bit abfragen, bevor ein Lesevorgang gestartet wird. Während eines laufenden Schreibvorganges ist es nicht möglich, den EEPROM zu lesen oder das EEPROM-Daten-Register zu ändern.

Nachfolgende Tabelle zeigt die typischen Programmierzeiten des EEPROMs, die zeitliche Steuerung erfolgt durch den kalibrierten RC-Oszillator.

Tabelle 8-2: EEPROM-Programmierzeit
ParameterAnzahl der RC-Oszillator-TakteTypische Programmierzeit
EEPROM schreiben (von CPU)26.3683,3 ms
Im Gegensatz zu älteren AVR-Mikrocontrollern mit knapp 10 ms EEPROM-Programmierzeit ist hier die Schreibzeit deutlich kürzer.
Die folgenden Programmbeispiele zeigen den Schreibvorgang für das EEPROM. Die Beispiele unterstellen, dass keine Interrupts auftreten können und der Flash nicht durch ein Programm im Urlader-Bereich beschrieben wird. Falls doch muss die EEPROM-Schreibroutine auf das Ende einer SPM-Schreiboperation warten.
Beispiel in Assembler
EEPROM_write:
	sbic	EECR,EEPE	; Warte bis vorheriges Schreiben fertig
	 rjmp	EEPROM_write    
	out	EEARH,r18	; Lade Adresse (r18:r17)
	out	EEARL,r17
	out	EEDR,r16	; Lade Datenbyte (r16)
	sbi	EECR,EEMPE	; Setze EEMPE-Bit
	sbi	EECR,EEPE	; EEPROM schreiben starten durch Setzen des EEPE-Bits
	ret
Beispiel in C
void EEPROM_write(unsigned uiAddress, unsigned char ucData) {
  while (EECR & (1<<EEPE));	/* Warte bis vorheriges Schreiben fertig */
  EEAR = uiAddress;			/* Lade Adresse und Datenbyte */
  EEDR = ucData;
  EECR |= (1<<EEMPE);	/* Setze EEMPE-Bit */
  EECR |= (1<<EEPE);	/* EEPROM schreiben starten durch Setzen des EEPE-Bits */
}
Das nächste Beispiel zeigt den Lesevorgang des EEPROMs. Dabei ist wieder unterstellt, dass keine Interrupts auftreten können.
Beispiel in Assembler
EEPROM_read:
	sbic	EECR,EEPE	; Warte bis vorheriges Schreiben fertig
	 rjmp	EEPROM_read
	out	EEARH,r18	; Lade Adresse (r18:r17)
	out	EEARL,r17
	sbi	EECR,EERE	; Starte EEPROM lesen
	in	r16,EEDR	; Lese Datenbyte
	ret
Beispiel in C
unsigned char EEPROM_read(unsigned uiAddress) {
  while (EECR & (1<<EEPE));	/* Warte bis vorheriges Schreiben fertig */
  EEAR = uiAddress;		/* Lade Adresse */
  EECR |= (1<<EERE);		/* Starte EEPROM lesen */
  return EEDR;			/* Lese Datenbyte */
}

8.6.4   GPIOR2 — Freies I/O-Register 2

Bit76543210
(0x4B)MSBLSBGPIOR2
ZugriffR/WR/WR/WR/WR/WR/WR/WR/W
Startwert00000000

8.6.5   GPIOR1 — Freies I/O-Register 1

Bit76543210
(0x4A)MSBLSBGPIOR1
ZugriffR/WR/WR/WR/WR/WR/WR/WR/W
Startwert00000000

8.6.6   GPIOR0 — Freies I/O-Register 0

Bit76543210
(0x3E)MSBLSBGPIOR0
ZugriffR/WR/WR/WR/WR/WR/WR/WR/W
Startwert00000000