Kampis Elektroecke

XMega – Lesen und Schreiben der User Signature Row

XMega Blockdiagramm

Bei der User Signature Row handelt es sich um ein seperates Datenfeld von der Größe einer einzelnen Flash-Seite. Dieses Feld ist für statische Anwendungsparameter gedacht und kann dem entsprechend von dem Mikrocontroller oder einem Programmiergerät gelesen und beschrieben werden. Im Gegensatz zum EEPROM kann die User Signature Row nicht durch einen Chip-Erase-Befehl des Programmiergerätes, sondern nur durch ein spezielles Kommando gelöscht werden.

Beschreiben der User Signature Row:

Der Zugriff auf die User Signature Row erfolgt auch hier über den internen Puffer des NVM-Controllers. Allerdings werden in diesem Fall keine einzelnen Datenbytes, sondern Datenworte (also zwei Bytes) geschrieben. Um ein Datenwort in die User Signature Row zu schreiben, muss der folgende Ablauf eingehalten werden:

  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. WRITE_USER_SIG_ROW-Kommando in das CMD-Register schreiben
  3. 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.
  4. Warten bis die Operation abgeschlossen ist.

Da beim Schreiben (und auch beim Lesen) der User Signature die Parameter in festgelegten Registern stehen müssen und die Ausführung des Befehls zudem an den SPM-Befehl gekoppelt ist, soll der Code für diesen Anwendungsfall direkt in Assembler programmiert werden. Eine Besonderheit ergibt sich bei der Nutzung des SPM-Befehls. Dieser Befehl kann nur aus dem Bootloaderbereich heraus ausgeführt werden, was zur Folge hat, dass Funktionen, die den SPM-Befehl nutzen, im Bootloaderbereich platziert werden müssen.

Die Startadresse des Bootloaderbereichs ist abhängig vom verwendeten Mikrocontroller und kann dem Datenblatt des Mikrocontrollers entnommen werden. Bei einem XMega384C3, der für dieses Beispiel verwendet wurde, beginnt der Bootloaderbereich bei der Wortadresse 0x30000.

Bevor es an die Umsetzung für den Code zum Beschreiben der User Signature geht, soll die Funktion zum Ausführen des SPM-Befehls programmiert werden. Diese Funktion muss im Bootloaderbereich des Mikrocontrollers platziert werden. Daher habe ich für Code, der im Bootloaderbereich platziert werden soll, eine eigene Sektion namens boot angelegt. 

Über die Flags “a” und “x” wird der Speicherbereich als allocatable (a) und executable (x) markiert, sprich er wird im Programmcode eingefügt und kann ausgeführt werden.

In den Projekteinstellungen muss nun dem Linker noch mitgeteilt werden, dass es eine neue Sektion namens boot gibt und das diese Sektion in die Bootloadersektion des Mikrocontrollers kopiert werden soll. Dazu wird in den Memory Settings des Linkers in den Projekteinstellungen die Startadresse des Bootloaderbereichs und ein Verweis auf die Sektion eingefügt.


Hinweis:

Wird die Kommandozeile genutzt, so muss der Linkeraufruf folgendermaßen ergänzt werden.

Hierbei ist zu beachten, dass beim Linker eine Byteadresse und keine Wortadresse angegeben wird!


Nun geht es an den Programmcode um den SPM-Befehl auszuführen. Der Befehl ist folgendermaßen definiert:

Mnemonics Operands Description Operation Flags #Clocks
SPM   Store Program Memory (RAMPZ:Z) ← R1:R0 None

Der Befehl verwendet die Register R1, R0, RAMPZ, R31 und R30. Bei der Ausführung wird das Datenwort, welches in R1 und R0 gespeichert ist, unter der Adresse, die über RAMPZ und dem Z-Pointer, also die Register R31 und R30, definiert ist, gespeichert.

Für den Aufruf der SPM-Funktion sollen folgende Übergabeparameter gelten:

Damit der SPM-Befehl genutzt werden kann, muss eine bestimmte Signatur in das CCP-Register geschrieben werden. Sobald diese Signatur in das Register kopiert worden ist, kann die Anwendung vier Taktzyklen lang den SPM und LPM-Befehl ausführen.

Es ergibt sich damit die folgende Funktion:

Die Funktion kopiert zuerst das übergebene NVM-Kommando in das CMD-Register und schaltet anschließend den SPM-Befehl frei, der direkt nach dem Freischalten ausgeführt wird.

Weil der Assemblercode zusammen mit C-Code genutzt werden soll, muss anschließend das Register R1 auf 0 gesetzt werden, da dieses Register für AVRGCC reserviert ist und beim Verlassen einer Funktion immer den Wert 0 führen muss (das sogenannte __zero_reg__). Abschließend wird noch das CMD-Register des NVM-Controllers gelöscht (also der Befehl NO_OPERATION eingefügt) und das Register R19 vom Stack geholt.

Jetzt können wir uns dem Beschreiben des Flash-Puffers widmen. Da diese Funktionen aus der Applikation heraus verwenden werden soll, müssen die Register entsprechend des AVRGCC-ABI genutzt werden. Bevor die Daten allerdings in die Signatur Row geschrieben werden können, müssen diese erst in den Puffer des Flash-Speichers geschrieben werden (siehe den weiter oben vorgestellten Ablauf). Dies soll eine Funktion namens NVM_FlashWriteWord, die folgendermaßen definiert ist, übernehmen.

Die Funktion erwartet zwei 16-Bit Übergabeparameter, die, wie im AVRGCC-ABI beschrieben, in den Registern R25 und R24 für den Parameter Address und in den Registern R23 und R22 für den Parameter Data übergeben werden.

Das LSB wird dabei immer in der geraden Registeradresse gespeichert. Gemäß des Ablaufs müssen die Daten aus den Registern R25 und R24 im Z-Register gespeichert werden und die Daten aus den Registern R23 und R22 in den Registern R1 und R0.


Hinweis:

Da die User Signature Row nur eine Flash-Seite lang ist (also 512 Byte), reichen zwei Register für die Adressierung. Andernfalls muss das RAMPZ-Register mit genutzt und der Übergabeparameter für die Funktion auf einen uint32_t geändert werden.


Die Funktion sichert zudem das RAMPZ-Register und löscht es anschließend. Dadurch wird verhindert, dass ein noch vorhandener Wert im RAMPZ-Register fälschlicherweise für die Zieladresse genutzt wird.

Da es sich bei dieser Funktion um eine globale Funktion handelt und diese von außerhalb aufgerufen werden soll, muss der Funktionsname nach außen sichtbar gemacht werden. Dies geschieht durch den Zusatz .global. Zudem soll diese Funktion im Programmspeicher abgelegt werden. Daher wird für diese Funktion die Speichersektion .text, also die Sektion für den Programmcode, genutzt.

Im nächsten Schritt wird das LOAD_FLASH_BUFFER-Kommando in das Register R26 kopiert und der SPM-Befehl ausgeführt.

Abschließend wird das RAMPZ-Register wiederhergestellt, der Inhalt des verwendete Registers R18 vom Stack geholt und die Funktion verlassen.

Für große Datenmengen ist es natürlich praktischer, wenn direkt eine komplette Seite in den Buffer kopiert werden kann. Daher soll eine weitere Funktion namens NVM_FlashWritePage angelegt werden.

Die Funktion zum Beschreiben einer kompletten Seite sieht folgendermaßen aus:

Da die Funktion den SPM-Befehl benutzt, muss auf die richtige Codesektion geachtet werden. Auch diese Funktion muss in den Bootloaderbereich kopiert werden.

Zu Anfang wird das Z– und das RAMPZ-Register gelöscht, damit die Startadresse auf den Anfang der Seite gelegt wird. Dann wird die Adresse des Buffers in das Y-Register und die Größe einer Flash-Seite kopiert und anschließend wird so lange über den Puffer iteriert, bis alle Datenbytes ausgelesen und mit Hilfe des SPM-Kommandos in den Flash-Puffer kopiert worden sind.

Jetzt stehen die Daten im Puffer für den Flash-Speicher und können mit dem WRITE_USER_SIG_ROW-Kommando in die User Signature Row kopiert werden. Dazu muss das Kommando in das CMD-Register kopiert und erneut ein SPM-Befehl ausgeführt werden. Auch hierfür wird eine Funktion angelegt:

Der Code für diese Funktion ist recht übersichtlich:

Der Zustand des NVM-Controllers kann über das BUSY-Bit im STATUS-Register abgefragt werden. So lange wie das Bit gesetzt ist, ist der NVM-Controller beschäftigt und kann somit keine neue Operation ausführen.

Damit sind alle Komponenten zusammen und müssen nur noch nacheinander aufgerufen werden:

Die User Signature Row kann nach dem Ausführen des Programms mittels Debugger ausgelesen werden um die Funktion des Programms zu überprüfen.

Da das Schreiben somit funktioniert, wollen wir uns im nächsten Schritt anschauen, wie die Signatur wieder gelöscht werden kann.

Löschen der User Signature Row:

Für eine Löschung der User Signature Row sind folgende Schritte notwendig:

  1. Das NVM-Kommando ERASE_USER_SIG_ROW in das CMD-Register schreiben.
  2. SPM-Befehl ausführen
  3. Warten bis die Operation abgeschlossen ist

Die Funktion dafür sieht recht übersichtlich aus, da nur das Kommando geladen und in die SPM-Funktion gesprungen werden muss.


Hinweis:

Um fehlerhafte Daten zu vermeiden ist es sinnvoll, wenn die User Signature Seite vor dem Schreiben komplett gelöscht wird. Andernfalls kann es zu fehlerhaften Daten kommen, wie dieses Beispiel zeigt:

In diesem Fall habe ich versucht die geschriebenen Werte 0x1001 und 0x0110 mit den Werten 0x2020 und 0x3030 zu überschreiben, ohne vorher die User Signature Seite gelöscht zu haben.


Zu guter Letzt wollen wir uns noch anschauen, wie Daten von der User Signature Row gelesen werden können.

Auslesen der User Signature Row:

Damit die Datenwörter wieder ausgelesen werden können, muss folgendermaßen vorgegangen werden:

NVM-Kommando LOAD_FLASH_BUFFER in das CMD-Register schreiben.

  1. Z-Register mit der Leseadresse laden
  2. READ_USER_SIG_ROW-Kommando in das CMD-Register schreiben
  3. LPM-Befehl ausführen

Für den LPM-Befehl gilt die folgende Definition:

Mnemonics Operands Description Operation Flags #Clocks
LPM Rd, Z+ Load Program Memory and Post-Increment Rd ← (Z)
Z ← Z + 1
None 3

Der Befehl lädt das Datenbyte, welches durch das Z-Register addressiert wird, in das Register Rd. Daher muss vor der Ausführung des Befehls das Z-Register mit der Leseadresse beschrieben werden. Als Zielregister soll das Register R24 genutzt werden. Zudem wird empfohlen Interrupts global zu deaktivieren, bevor der LMP-Befehl ausgeführt wird, weswegen vor der Ausführung das SREG gesichtert, das GIE-Bit gelöscht und das Register anschließend wiederhergestellt wird.

Fehlt nur noch die Funktion um ein Datenwort auszulesen. Die Funktion soll eine 16-Bit Adresse als Übergabeparameter annehmen und das Datenwort als Rückgabewert zurückgeben.

Gemäß des Ablaufplans ergibt sich damit der folgende Funktionscode:

Natürlich kann auch hier wieder eine komplette Seite auf einmal ausgelesen werden. Dazu wird das Kommando geladen und, ähnlich wie bei dem seitenweisen Schreiben, der LPM-Befehl in jedem Schleifendurchlauf ausgeführt.

Und schon kann die programmierte User Signature Row über die Applikation ausgelesen werden.

Zurück

Last Updated on

Schreibe einen Kommentar

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