Kampis Elektroecke

XMega – Beschreiben des Flash-Speichers

XMega Blockdiagramm

AVR Mikrocontroller verfügen über die Möglichkeit den Programmcode in ihrem Flash-Speicher softwareseitig zu ändern oder neuen Programmcode in den Speicher zu schreiben. Dies wird u. a. bei Bootloadern benutzt, wo das eigentliche Programm in irgendeiner Form (z. B. per USB oder RS232) an den Mikrocontroller übertragen und dann durch die Bootloadersoftware in den Flash-Speicher geschrieben wird. Das Beschreiben des Flash-Speichers ist weitestgehend mit dem Beschreiben der User Signature Row identisch und unterscheidet. Daher können viele Funktionen des vorangegangenen Tutorials wiederverwendet werden.

Der Zugriff auf den Flash geschieht seitenweise, sprich es wird entweder eine komplette Seite gelesen oder geschrieben. Ein Schreibzugriff erfolgt, wie bei der User Signature Row, über den Flash-Puffer des NVM-Controllers. In diesen Puffer werden die Daten für eine Seite kopiert und anschließend wird der komplette Puffer auf eine festgelegte Seite des Flash-Speichers kopiert. Der Aufbau des Flash-Speichers lässt sich dem Datenblatt des verwendeten Mikrocontrollers entnehmen und sieht für einen XMega384C3 folgendermaßen aus:

Der Flash-Speicher dieses Mikrocontrollers ist also in 768 Seiten für den Anwendungscode und 16 Seiten für den Bootloadercode aufgeteilt. Jede Seite ist 256 Worte (512 Byte) groß.

Die Adressierung des Flash-Speichers erfolgt über das Z-Register (inkl. des RAMPZ-Registers). Dazu werden das komplette Z-Register als ein 24-Bit Register betrachtet und die Bits 1 bis 8 adressieren das Datenwort der jeweiligen Seite und die Bits 9 bis 19 die entsprechende Seite. Das Bit 0 ist ausschließlich für den lesenden Zugriff mittels des (e)lpm Befehls wichtig und regelt ob das niederwertigste Byte (0) oder das höherwertigste Byte (1) des Datenwortes gelesen wird.

Für den kompletten Schreibzugriff auf den Flash-Speicher ergibt sich der folgende Ablauf:

  1. Flash-Puffer mit Daten füllen
    1. NVM-Kommando LOAD_FLASH_BUFFER in das CMD-Register schreiben.
    2. Z-Register mit der Wortadresse für das Datenwort beschreiben.
    3. Datenwort in die Register R1:R0 schreiben.
    4. spm Befehl ausführen.
  2. Z-Register mit der Seitenadresse füllen
  3. WRITE_FLASH-PAGE-Kommando in das CMD-Register schreiben
  4. spm Befehl ausführen
    • Der Befehl wird jetzt durch das CCP-Register geschützt, weshalb zuerst die korrekte Signatur in das CCP-Register geschrieben werden muss.
  5. Warten bis die Operation abgeschlossen ist.

Schauen wir uns das ganze mal an einem praktischen Beispiel an. Dazu möchte ich die letzte Seite der Anwendungssektion im Flash-Speicher beschreiben. Der Inhalt der Seite wird durch ein Array dargestellt, wo sowohl das erste, als auch das letzte Datenwort beschrieben werden.

uint16_t AppCode[APP_SECTION_PAGE_SIZE / 2];
AppCode[0] = 0x01;
AppCode[(APP_SECTION_PAGE_SIZE / 2) - 1] = 0x02;

Dieses Array wird anschließend über die Funktion NVM_FlashWritePage aus dem vorangegangenen Tutorial in den Flash-Puffer kopiert.

NVM_FlashWritePage(AppCode);

Anschließend wird der Inhalt des Flash-Puffers in den Flash geschrieben. Dies soll durch eine Funktion namens NVM_FlushFlash, welche die Seitenadresse als Übergabeparameter erwartet, realisiert werden.

;--
;	Input:
;		r25:r24				Page address
;
;	Return:
;		-
;--
.section .text
.global NVM_FlushFlash
NVM_FlushFlash:

	in	r18, RAMPZ

	mov	r19, r25
	mov	ZH, r24

	lsl	ZH
	rol	r19

	sts	RAMPZ, r19

	ldi	r26, NVM_CMD_ERASE_WRITE_FLASH_PAGE_gc
	call	NVM_ExecuteSPM

	call	NVM_WaitBusy

	out	RAMPZ, r18

	ret

Die Funktion füllt als erstes das Z-Register mit der Seitenadresse. Die Seitenadresse muss in das ZH– und das RAMPZ-Register kopiert werden, wobei das ZH-Register die Bits 0 bis 7 und das RAMPZ-Register die Bits 8 bis 10 der Adresse enthalten muss. Die Bits müssen an die Bitpositionen 1 bis 7 für das ZH-Register und an die Stellen 0 bis 3 im RAMPZ-Register kopiert werden

Dazu wird als erstes das obere Byte der Adresse in das Register R19 und das untere Byte der Adresse in das ZH-Register kopiert. Danach wird der Inhalt des ZH-Registers mit dem lsl Befehl um ein Bit nach links geschoben. Dabei wird die letzte Stelle des Datenbytes auf das Carry-Flag geschoben, sodass das Carry-Flag den Zustand des höchsten Bits im ZH-Register repräsentiert. Damit enthält das ZH-Register die unterstehn 7 Adressbits an den Bitpositionen 1 bis 7. Über den rol Befehl wird das Register R19 um eine Position nach links gerollt und dabei das Carry-Flag in das Register R19 geladen. Als Ergebnis erhält man die um ein Bit nach links geschobene Seitenadresse, aufgeteilt in zwei Registern. Der Inhalt des Registers R19 wird abschließend noch in das RAMPZ-Register kopiert.

mov r19, r25 
mov ZH, r24 

lsl ZH 
rol r19 

sts RAMPZ, r19

Danach wird das Register R26 mit dem NVM-Befehl geladen. Hierbei ist es wichtig, dass der bereits vorhandene Inhalt der Seite vor dem Schreiben gelöscht wird, da es ansonsten passieren kann, dass bereits vorhandene Daten nicht korrekt überschrieben werden. Über das nachfolgende spm Kommando wird die Ausführung des NVM-Befehls gestartet.

ldi r26, NVM_CMD_ERASE_WRITE_FLASH_PAGE_gc 
call NVM_ExecuteSPM

Die fertige Funktion kann jetzt im Hauptprogramm genutzt werden:

NVM_FlushFlash(767);
NVM_WaitBusy();

Sobald die Operation durchgeführt abgeschlossen worden ist, kann das Ergebnis mittels Debugger betrachtet werden.

Der Flash-Speicher wurde erfolgreich beschrieben. Mit den hier gezeigten Funktionen können eigene Bootloader, welche zum flexiblen Programmieren der Mikrocontroller genutzt werden können, erstellt werden.

Zurück

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert