26.   Flash-Selbstprogrammierung, ATmega48

26.1   Übersicht

Der ATmega84(P)A hat nicht die Möglichkeit des Lesens-beim-Schreiben des Flash-Speichers. Der SPM-Befehl kann vom gesamten Flash-Speicher aus ausgeführt werden.

Der Chip unterstützt Selbstprogrammierung zum Download und Upload von Programmkode. Dazu kann jedes verfügbare Dateninterface und -protokoll herangezogen werden.

Der Programmspeicher wird Seite für Seite beschrieben und gelöscht. Bevor eine Seite mit den Daten, die in dem temporären Seitenpuffer geladen sind, programmiert wird, muss die Seite gelöscht werden. Der temporäre Seitenpuffer wird 16-bitweise mit dem SPM-Befehl gefüllt. Der Seitenpuffer kann sowohl vor dem Seitenlösch-Kommando als auch zwischen dem Seitenlösch-Kommando und dem Schreiben der Seite gefüllt werden.

Variante 1: Füllen des Puffers vor dem Löschen:

Variante 2: Füllen des Puffers zwischen dem Löschen und Schreiben: Wenn nur ein Teil einer Seite verändert werden muss, muss der Rest der Seite vorher rückgelesen werden (z.B. im temporären Seitenpuffer), da vor dem Schreiben immer die gesamte Seite gelöscht wird. Wenn Variante 1 verwendet wird, bietet der Urloader echtes, byteweises Lesen-Verändern-Schreiben an. In dieser wird die Software zunächst die Seite lesen, dann die notwendigen Veränderungen durchführen und anschließend die modifizierten Daten zurückschreiben. Wenn Variante 2 verwendet wird, ist es nicht möglich, die alten Daten zu lesen, da die Seite bereits gelöscht wurde. Auf den temporären Seitenpuffer kann frei zugegriffen werden. Es ist wichtig, dass die Seitenadresse beim Löschen und Schreiben der Seite die gleiche Flash-Seite adressiert.

26.1.1   Seite löschen

Zum Löschen einer Seite wird die Seitenadresse im Zeigerregisterpaar Z hinterlegt, der Wert 0b00000011 in das SPMCSR geschrieben und innerhalb von 4 Taktzyklen ein SPM-Befehl ausgeführt. Die Daten in R1 und R0 werden dabei ignoriert. Die Seitenadresse muss in den Bereich PCPAGE in Z geschrieben werden, die anderen Bits von Z werden bei dieser Operation ignoriert.
Hinweis:
Wenn ein Interrupt innerhalb der zulässigen vier Taktzyklen zwischen den beiden Befehlen auftaucht, kann der Zugriff nicht korrekt erfolgen. Um atomare (unterbrechungsfreie) Aneinandereihung der Befehlsausführung zu garantieren, sollten die Interrupts vor dem Schreiben von SPMCSR gesperrt werden.

26.1.2   Zwischenpuffer füllen (Seite laden)

Zum Schreiben eines Befehlswortes wird die Adresse im Zeigerregisterpaar Z hinterlegt, die Daten in R1:R0 geschrieben, der Wert 0b00000001 in das SPMCSR geschrieben und innerhalb von vier Taktzyklen ein SPM-Befehl ausgeführt. Der PCWORD-Bereich von Z wird verwendet, um die Daten im temporären Puffer zu adressieren. Der temporäre Puffer wird automatisch nach dem Befehl Seite-Schreiben gelöscht, aber auch durch Beschreiben des RWWSRE Bits im SPMCSR. Er wird auch nach einem System-Reset gelöscht. Man beachte, dass es nicht möglich ist, mehr als einmal in eine Adresse zu schreiben, ohne dass der temporäre Puffer gelöscht wird.

Wenn der EEPROM mitten in einer Seitenlade-Operation beschrieben wird, gehen alle geladenen Daten verloren.

26.1.3   Seite schreiben

Um das Schreiben einer Seite durchzuführen, wird die Adresse im Zeigerregisterpaar Z hinterlegt, der Wert 0b0000101 in das SPMCSR geschrieben und innerhalb von vier Taktzyklen ein SPM-Befehl ausgeführt. Die Daten in R1:R0 werden dabei ignoriert. Die Seitenadresse muss in dem Bereich PCPAGE von Z geschrieben werden, die anderen Bits von Z müssen während dieser Operation auf Null gesetzt sein.

26.2   Adressierung beim Selbstprogrammieren

Das Zeigerregisterpaar Z wird zur Adressierung des SPM-Befehls benutzt (= impliziter Operand).

Bit15141312111098
ZH(R31)Z15Z14Z13Z12Z11Z10Z9Z8
ZL(R30)Z7Z6Z5Z4Z3Z2Z1Z0
76543210

Da der Flash-Speicher in Seiten organisiert ist (siehe Tabelle 28-11), kann man sich den Programmzähler in zwei Teile aufgeteilt vorgestellen. Die niederwertigen Bits addressieren ein 16-Bit-Wort in einer Seite, und der höherwertige Teil adressiert die Seite. Siehe Bild 26-1. Man beachte, das Seite-Löschen und Seite-Schreiben unabhängig adressiert werden. Daher muss die Software gleiche Adressen zum Seitenlöschen und Seitenschreiben sicherstellen.

Auch für den LPM-Befehl wird Z (implizit oder explizit) zur Adressierung verwendet. Da dieser Befehl byteweise arbeitet, wird hierbei auch das LSB (Bit 0) von Z verwendet.

Bild 26-1: Flash-Adressierung während SPM (1)
Hinweis:
  1. Die verschiedenen Variablen im Bild 26-1 sind in Tabelle 28-11 gelistet.

26.2.1   EEPROM schreiben behindert Schreibzugriff auf SPMCSR

Man beachte, dass das Beschreiben des EEPROM alle Programmierungen des Flash blockiert. Das Lesen der Fuses und Sperrbits durch die Software ist ebenfalls während der Beschreibens des EEPROM nicht möglich. Es wird daher empfohlen, dass der Anwender überprüft, ob das Statusbit EEWE im EECR-Register gelöscht ist, bevor in das SPMCSR geschrieben wird.

26.2.2   Fuse- und Sperrbits lesen

Es ist möglich, sowohl die Fuses als auch die Sperrbits durch die Software auszulesen. Zum Lesen der Sperrbits wird das Zeigerregisterpaar Z mit 0x0001 geladen und die Bits BLBSET und SPMEN im SPMCSR gesetzt. Wenn innerhalb der nächsten drei Taktzyklen nach dem Setzen der beiden Bits ein LPM-Befehl ausgeführt wird, werden die Werte der Sperrbits in das LPM-Zielregister geladen. Die beiden Bits werden nach dem Ausführen des Befehls automatisch gelöscht oder dann, wenn innerhalb der Zeit kein LPM-Befehl ausgeführt wird. Wenn BLBSET und SPMEN gelöscht sind, arbeitet der LPM-Befehl wieder normal, so wie in der Befehlsübersicht beschrieben.
Bit76543210
Rd------LB2LB1

Das Auslesen der Low-Fuse ist mit dem Lesen der Sperrbits vergleichbar. Zum Lesen der Low-Fuse (Fuse-Low-Bits = FLB) wird das Zeigerregisterpaar Z mit 0x0000 geladen und die Bits BLBSET und SPMEN im SPMCSR gesetzt. Wenn innerhalb der nächsten drei Taktzyklen nach dem Setzen der beiden Bits ein LPM-Befehl ausgeführt wird, werden die Werte der Low-Fuse in das LPM-Zielregister geladen, wie nachfolgend dargestellt. Siehe Tabelle 28-9 zur Zuordnung der Bits der Low-Fuse.

Bit76543210
RdFLB7FLB6FLB5FLB4FLB3FLB2FLB1FLB0

Vergleichbar wird zum Lesen der High-Fuse (Fuse-High-Bits = FHB) das Zeigerregisterpaar Z mit 0x0003 geladen und die Bits BLBSET und SPMEN im SPMCSR gesetzt. Wenn innerhalb der nächsten drei Taktzyklen nach dem Setzen der beiden Bits ein LPM-Befehl ausgeführt wird, werden die Werte der High-Fuse in das LPM-Zielregister geladen, wie nachfolgend dargestellt. Siehe Tabelle 28-7 zur Zuordnung der Bits der High-Fuse.

Bit76543210
RdFHB7FHB6FHB5FHB4FHB3FHB2FHB1FHB0

Vergleichbar wird zum Lesen der Extended-Fuse (Extended-Fuse-Bits = EFB) das Zeigerregisterpaar Z mit 0x0002 geladen und die Bits BLBSET und SPMEN im SPMCSR gesetzt. Wenn innerhalb der nächsten drei Taktzyklen nach dem Setzen der beiden Bits ein LPM-Befehl ausgeführt wird, werden die Werte der Extended-Fuse in das LPM-Zielregister geladen, wie nachfolgend dargestellt. Siehe Tabelle 28-4 zur Zuordnung der Bits der Extended-Fuse.

Bit76543210
Rd-------EFB0

Programmierte Fuse- und Sperrbits werden als Null gelesen. Unprogrammierte Fuse- und Sperrbits werden als Eins gelesen.

26.2.3   Schreibfehler vermeiden

Wenn UCC zu niedrig ist, kann das Programmieren des Flash verderben, weil die Versorgungsspannung zu niedrig für ein korrektes Arbeiten der CPU und des Flash ist. Das gilt für alle Systeme mit Flash-Speicher.

Ein Verfälschen des Flash-Programmierens kann in zwei Situationen, in denen die Versorgungsspannung zu gering ist, begründet sein. Erstens benötigt ein regulärer Schreibvorgang eine Mindestspannung, um korrekt zu arbeiten. Zweitens kann die CPU selbst Befehle falsch ausführen, wenn die Versorgungsspannung zu gering ist.

Flash-Fehler können verhindert werden, wenn folgende Designregeln beachtet werden (eine genügt):

  1. Halten des AVR-Resets auf aktiv Low in Zeiten unzureichender Versorgungsspannung. Dies kann durch Freigabe des internen Unterspannungs-Detektors geschehen. Andernfalls kann eine externe Spannungsüberwachung eingesetzt werden. Wenn ein Reset auftritt, während ein Schreibvorgang läuft, so wird dieser Vorgang noch bis zum Ende ausgeführt, vorausgesetzt, die Spannungsversorgung ist noch ausreichend.
  2. Versetzen des AVR-Kerns in den Power-Down Sleep-Modus wenn UCC zu gering ist. Das schützt die CPU davor, Befehle zu dekodieren und auszuführen, woduch auch das SPMCSR geschützt wird und somit auch der Flash.

26.2.4   Programmierzeit für Flash-Speicher mittels SPM

Der kalibrierte RC-Oszillator regelt die Zeiten beim Flash-Zugriff. Nachfolgende Tabelle zeigt die typischen Programmierzeiten des Flash durch die CPU.
Tabelle 26-1: SPM-Programmierzeit(1)
AktionMinimale ProgrammierzeitMaximale Programmierzeit
Flash schreiben (Seiten löschen, Seite schreiben, Sperrbits mittels SPM setzen)3,7 ms4,5 ms
Hinweis:
  1. Minimale und maximale Programmierzeit gelten für jede einzelne Operation.

26.2.5   Einfaches Assembler-Beispiel für einen Urlader

Beachte, dass das RWWSB-Bit im ATmega48 stets Null liest. Nichtsdestotrotz wird empfohlen, das Bit wie in den Kode-Beispielen zu überprüfen, damit die Software auch bei den größeren oder künftigen Chips mit Lesen-beim-Schreiben funktioniert.
	;- Diese Routine schreibt eine Seite vom RAM zum Flash.
	;   Die erste RAM-Speicherzelle wird durch Y adressiert.
	;   Die erste Flash-Speicherzelle wird durch Z adressiert.
	;- Fehlerbehandlung ist nicht dabei.
	;- Verwendete Register: R0, R1, temp1 (R16), temp2 (R17), loop (R18), spmcrval (R19)
	;  Retten und Siederherstellen (Push+Pop) ist hier nicht eingebaut.
	;  Registerverbrauch kann gegen Kodegröße eingespart werden.
	;- Es wird PAGESIZEB ≤ 256 für 8-Bit-Schleifenzähler vorausgesetzt.
.equ	PAGESIZEB = PAGESIZE*2	;PAGESIZEB = Seitengröße in BYTE, nicht in WORD
.org SMALLBOOTSTART
Write_page:
	; Seite löschen
	ldi	spmcrval, (1<<PGERS) | (1<<SPMEN)
	rcall	Do_spm
	; RWW-Bereich wiederherstellen (unnötig für ATmega48)
	ldi	spmcrval, (1<<RWWSRE) | (1<<SPMEN)
	rcall	Do_spm
	; Daten vom RAM zum Flash-Seitenpuffer schaufeln
	ldi	loop, PAGESIZE		;Schleifenzähler initialisieren
Wrloop:
	ld	r0, Y+
	ld	r1, Y+
	ldi	spmcrval, (1<<SPMEN)
	rcall	Do_spm
	subi	ZL, -2			;= +2, ohne Überlauf zum High-Teil
	dec	loop
	brnz	Wrloop
	; execute Page Write
	subi	ZL, PAGESIZEB		;Zeiger wiederherstellen
	ldi	spmcrval, (1<<PGWRT) | (1<<SPMEN)
	rcall	Do_spm
	; re-enable the RWW section
	ldi	spmcrval, (1<<RWWSRE) | (1<<SPMEN)
	rcall	Do_spm
	; read back and check, optional
	ldi	loop, PAGESIZEB		;Schleifenzähler initialisieren
	subi	YL, PAGESIZEB		;Zeiger wiederherstellen
	sbci	YH, 0
Rdloop:
	lpm	r0, Z+
	ld	r1, Y+
	cpse	r0, r1
	rjmp	Error			;Nicht gezeigte Fehlerbehandlung
	dec	loop
	brnz	Rdloop
	; Rückkehr zum RWW-Bereich
Return:	; Warten bis RWW-Bereich bereit zum Lesen ist (unsinnig für ATmega48)
	in	temp1, SPMCSR
	sbrs	temp1, RWWSB
	ret
	; RWW-Bereich wiederherstellen (unsinnig für ATmega48)
	ldi	spmcrval, (1<<RWWSRE) | (1<<SPMEN)
	rcall	Do_spm
	rjmp	Return

Do_spm:
	; Warten bis vorheriges SPM fertig (unsinnig für ATmega48)
Wait_spm:
	in	temp1, SPMCSR
	sbrc	temp1, SPMEN
	 rjmp	Wait_spm
	; Warten bis evtl. EEPROM-Schreibzugriff fertig ist (Interrupts okay)
Wait_ee:
	sbic	EECR, EEPE
	 rjmp	Wait_ee
	; Eingabe: spmcrval legt SPM-Aktion fest
	; Interrupts sperren, falls freigegeben, Zustand retten
	in	temp2, SREG
	cli
	; Spezielle zeitabhängige SPM-Sequenz
	out	SPMCSR, spmcrval
	spm
	; SREG wiederherstellen (Interrupts freigeben wenn vorher freigegeben)
	out	SREG, temp2
	ret

26.3   Register-Beschreibung

26.3.1   SPMCSR — SPM-Kontroll- und Statusregister

Das SPMCSR enthält Steuerbits, die benötigt werden, um die Urlader-Funktionen zu steuern.
Bit76543210
(0x57)SPMIERWWSBSIGRDRWWSREBLBSETPGWRTPGERSSPMENSPMCSR
ZugriffR/WRR/WR/WR/WR/WR/WR/W
Startwert00000000
Wenn das SPMIE-Bit auf 1 gesetzt ist und das I-Bit im Statusregister gesetzt ist, wird der SPM-Fertig-Interrupt freigegeben. Der SPM-Fertig-Interrupt wird ausgeführt, sobald das SPMEN-Bit im SPMCSR (durch die Hardware) gelöscht ist.
Beim ATmega48 ist dieser Interrupt wegen der angehaltenen CPU recht zweckfrei.
Beim ATmega48 ist dieses Bit stets Null. Mit diesem Bit kann man die Chip-Signatur (Herstellerkennung) sowie das RC-Oszillator-Werkskalibrierbyte auslesen. Siehe Signaturbytes lesen. Das Beschreiben mittels SPM ist wirkungslos und sollte wegen künftiger Erweiterungen unterlassen werden. Da der ATmega48 keine RWW- und NRWW-Bereiche hat, verbleibt mit diesem Bit nur eine Restfunktionalität: Wird dieses Bit während des Füllvorgangs gesetzt, wird der Puffer gelöscht. Beim ATmega48 können damit die Fuse- und Sperrbits gelesen werden; er hat keine Urlader-Sperrbits. Siehe oben. Wenn dieses Bit gleichzeitig mit SPMEN auf Eins gesetzt wird, wird der nächste SPM-Befehl innerhalb von vier Taktzyklen das Beschreiben einer Seite mit den Daten, die im temporären Puffer gespeichert sind, starten. Die Seitenadresse wird aus dem oberen Teil des Registerpaars Z entnommen. Die Daten in R1 und R0 werden ignoriert. Das PGWRT-Bit wird, nachdem die Seite geschrieben wurde, automatisch gelöscht oder dann, wenn der SPM-Befehl nicht innerhalb der vier Taktzyklen ausgeführt wurde. Die CPU wird während des Schreibvorgangs angehalten. Wenn dieses Bit gleichzeitig mit SPMEN auf Eins gesetzt wird, wird der nächste SPM-Befehl innerhalb von vier Taktzyklen das Löschen einer Seite starten. Die Seitenadresse wird aus dem oberen Teil des Registerpaars Z entnommen. Die Daten in R1 und R0 werden ignoriert. Das PGERS-Bit wird automatisch gelöscht, wenn die Seite gelöscht wurde oder wenn innerhalb der nächsten vier Taktzyklen kein SPM-Befehl ausgeführt wurde. Die CPU wird während des Löschvorgangs angehalten. Dieses Bit gibt die Ausführung eines SPM-Befehls innerhalb der nächsten vier Taktzyklen frei. Wenn es zusammen mit einem der Bits RWWSRE, BLBSET, PGWRT oder PGERS gesetzt wird, hat der folgende SPM-Befehl eine besondere Bedeutung, wie oben beschrieben. Wenn nur SPMEN allein auf Eins besetzt wird, wird der folgende SPM-Befehl die Werte, die in R1:R0 stehen, in den Seitenpuffer speichern, der durch den Registerpaar Z adressiert ist. Dabei wird das LSB von Z ignoriert. Das SPMEN-Bit wird automatisch gelöscht, wenn ein SPM-Befehl ausgeführt wurde, oder spätestens nach vier Taktzyklen. Während des Löschens und Schreibens einer Seite verbleibt das SPMEN-Bit so lange High, bis die Operation vollständig abgeschlossen ist.

Das Schreiben anderer Werte als 0b10001, 0b01001, 0b00101, 0b00011 oder 0b00001 in die unteren fünf Bits hat keine Auswirkungen.