C AVR -> EEPROMS -> Feste Adressen

Janiiix3

Aktives Mitglied
28. Sep. 2013
1.333
10
38
Hannover
Sprachen
  1. ANSI C
  2. C#
Moin,

wie sieht es eigentlich mit der Adressvergabe in Bezug auf "C und EEPROM" aus.
Habe jetzt vermehrt gelesen das es sinnvoller ist ( gerade bei irgendwelchen Updates etc. ) sich auf feste Speicheradressen festzulegen?

In C wird das ja einem schon alles abgenommen. Wie sieht es jetzt aber aus, wenn ich mir die Adressen selber festlege?

  • Wie genau muss ich vorgehen?
  • Was ist zu beachten?
  • Wie berechne ich die Adressen ( gibt es Adressen wo man nicht schreiben darf? )

Die Daten sollten wohl erst mal im RAM gesammelt werden ( struct? ( wenn ja was für Variablen währen sinnvoll? ) ) und später oder wenn alle beisammen sind, geschrieben werden..?
 
???
Um was für'n Eeprom geht's denn? Irgend'n externer, oder AVR-interner - und wenn interner, um welchen Controller?

Zu C kann ich nichts sagen, aber der AVR-interne Eeprom ist in Bytes organisiert, ein ATtiny13 verfügt dann zB über 64 Bytes Eeprom, ein ATmega2560 über 4096 Bytes (4kBytes).
Entsprechend muß der Adress-Zeiger beim Tiny13 sechs Bytes breit sein, beim Mega2560 zwölf.

Dieser Adress-Zeiger ist in Form eines I/O-Registers realisiert, dem Eeprom Address Register (EEAR). Beim Tiny13 genügt ein 8-Bit-Register, beim Mega2560 sind logischerweise zwei Register nötig: EEARH und EEARL. C wird die aber sicher wie ein Word behandeln...

Habe jetzt vermehrt gelesen das es sinnvoller ist ( gerade bei irgendwelchen Updates etc. ) sich auf feste Speicheradressen festzulegen?
Wenn das neue Programm dieselben konstanten oder variablen Daten an denselben Adressen erwartet, wie das alte, muß beim flashen des neuen Programmes nicht der Eeprom mitgeflasht werden. Insbesondere sinnreich, wenn im Eeprom zur Laufzeit irgendwelche Daten (zB Konfiguration durch den Benutzer) abgelegt wurden, die zur Compile-Zeit noch gar nicht bekannt waren. Ein etwaiges Firmware-Update kann dann aufgespielt werden, ohne daß diese Konfigurationsdaten ausgelesen, zwischengespeichert und zurückgeschrieben - oder gar ganz neu angelegt werden müssen. Beispiel: Du hast irgend'n Regler implementiert, der … zB 'ne Temperatur messen soll, und aus einer Tabelle (im Eeprom) 'nen dazugehörenden PWM-Wert ausgeben soll. Der Benutzer kann diese Tabelle selbst im Eeprom anlegen (Kalibrationsdaten).
Jetzt fügst Du Deinem Programm irgend'ne tolle neue Funktion hinzu, und stellst das Hexfile Deinen zichtausend Benutzern zur Verfügung. Die wollen sich nicht alle mit der erneuten Kalibration herumschlagen, oder die Daten zwischenspeichern. Wenn die Neue Firmware also dieselben Adressen verwendet, muß nur sichergestellt werden, daß der Eeprom beim Flashen nicht angetastet wird...

Eeprom Lesen:
  • warten, bis eine eventuelle Eeprom-Schreib-Operation abgeschlossen ist (EEPE in EECR =0)
  • Adresse nach EEAR schreiben
  • Read-Strobe (EERE in EECR setzen)
  • Byte aus dem EEDR lesen
Das Schreiben ist etwas komplexer, grundsätzlich muß das Ziel-Byte im Eeprom erstmal gelöscht werden, bevor es beschrieben werden kann. Das geht in einer "atomaren" Variante in einem Rutsch, aber man kann die auch auftrennen, also nur löschen, und dann nur schreiben.
Wozu?
Der kombinierte Lösch-Schreib-Zugriff dauert etwa 3,4ms. Pro Byte. Wenn Du Daten schneller wegschreiben mußt, kannst Du aber vorher den benötigten Platz löschen, und benötigst dann zum wegschreiben der Daten (ohne nochmaliges Löschen) je 1,8ms pro Byte.

Eeprom Schreiben:
  • warten, bis eine eventuelle Eeprom-Schreib-Operation abgeschlossen ist (EEPE in EECR =0)
  • Programming Mode festlegen (erase, write oder atomic erase&write - EEPM[1:0] in EECR)
  • Adresse nach EEAR schreiben
  • Daten nach EEDR schreiben
  • EEMPE in EECR setzen (unlock EEPE)
  • innerhalb von vier Takten EEPE setzen (Write Strobe)
 
Hi LotadaC,

Zu C kann ich nichts sagen, aber der AVR-interne Eeprom ist in Bytes organisiert, ein ATtiny13 verfügt dann zB über 64 Bytes Eeprom, ein ATmega2560 über 4096 Bytes (4kBytes).
Entsprechend muß der Adress-Zeiger beim Tiny13 sechs Bytes breit sein, beim Mega2560 zwölf.

da meinst du wohl Bits und nicht Bytes ;)

Gruß
Dino
 
EEProm darfst du überall schreiben, ist halt nur langsam ;)
Speicheradressen berechnen, da gibt es nicht viel zu rechnen. Ich gehe um das Beispiel kurz zu halten davon aus dass du nur 8 Bytes EEProm hast.
Jetzt hast du Version 1 deiner Software, die speichert Kalibrierbyte, ein INT16, ein Statusbyte und ein weiteres INT16.
Macht: █ █ █ █ □ □
Frei von Anfang weg nummeriert (also nichts angegeben). 2 Bytes stehen dir noch zur Verfügung. Wie die Position berechnet wird sollte jetzt auch klar sein, du kennst ja idR. die Datenbreite der Werte die du speicherst (Vorsicht, Boolean ist häufig CPU Bitbreite, also beim AVR 8 Bit. Für mehrere boolische Werte: Selber basteln).
Jetzt kommt aber Version deiner Software, und du kommst auf den Gedanken, es wäre ja schön zu wissen auf welcher Hardware Version die Software läuft. Kann man machen. Wieder fortlaufend nummeriert
Macht: █ █ █ █
Es wird natürlich funktionieren, vorausgesetzt der EEProm ist mit Version 2 beschrieben. Aber was ist wenn du nun das neue Programm auf einem Controller laufen lässt wo noch die Daten von Version 1 im EEProm sind? Du siehst, die ganzen Speicheradressen haben sich verschoben, du bist also weder ab- noch aufwärtskompatibel. Daher gibt man gerne feste Werte vor, die sich auch später nie ändern. OSCCAL wäre ein Beispiel (Feintuning der AVR-internen Clock). Dieser Wert wird typischerweise an Adresse 0x000 gespeichert, also das erste zur Verfügung stehende Byte. Das Programm (oder Bootloader) erwartet es dort auch und schreibt es dann bei Programmstart in das entsprechende Register. Nur ein Beispiel.
Vom AVR Studio, zumindest die alte Version und mit ASM, kenne ich das so dass der auch eine Memory Map erstellt wo drin steht welche Speicheradresse wofür verwendet wird.
Grob überflogen scheint es nicht allzu leicht zu sein in C die Speicheradressen festzulegen. Aber niemand hindert dich daran etwas zu tricksen. Beispiel:
0x000 - 0x007: Hardware Einstellungen (8 Bytes)
0x008 - 0x00F: Software Einstellungen (8 Bytes)
0x010 - 0x...: Log Datei (Rest)
Jetzt brauchst du aber vielleicht nur 2 statt 8 Bytes für die Hardware Einstellungen. Deklariere einfach ein Array (6 Bytes) und nenne es Reserved - oder wie auch immer du willst, ich denke mal du siehst die Idee dahinter. Der Platz ist halt belegt (aber noch ungenutzt), also geht es bei 0x008 weiter. Willst du später doch 3 Bytes für die Hardware nutzen fügst du den nächsten Wert halt vor Reserved ein und machst das Array 1 kleiner. Somit bist du trotzdem auf- und Abwärtskompatibel, zumindest so lange die 8 Bytes ausreichen. Daher etwas in die Zukunft denken und lieber etwas mehr reservieren als man braucht, wenn möglich.
 
Jan geht es sicherlich um folgendes:

Man überlässt dem Compiler die Verwaltung der Speicheradressen (das wird der Normalfall sein). Das heißt, man kennt die absolute Adresse eines bestimmten Bytes gar nicht. Man muss hierbei weder wissen, dass beim AVR das EEprom 1 Byte Speicherstellen hat, oder welche Register in welcher Reihenfolge oder in welchem Timing beschrieben werden müssen. Man muss nicht mal wissen, dass es sich um einen AVR Mikrocontroller handelt. Man kann 1 Byte speichern, word, integer .... ganze Arrays aus vorher gennanten Datentypen, ganze Strukturen aus unterschiedlichen Datentypen bestehend. Alles kein Problem, wenn das Programm A die Daten erstellt, speichert und liest. Hat man ein Programm B, könnte es passieren, dass sich beim erneuten Kompilieren die Adresszordnungen (welche man nicht kennt) ändert. Zum Beispiel dann, wenn man eine weitere Variable im EEprom-Bereich hinzugefügt hat. Falls nun das neue Programm B die vom alten Programm A erstellten EEprom-Einträge lesen soll, passen u.U. die Adressen nicht mehr überein. Erstellt Programm B alle Eepromdaten neu, ist alles in Ordnung.

Abhilfe: Man lässt den Compiler nicht die Adresszurodnung machen, sondern gibt diese vor. Frage nun: Wie macht man das in C.
 
da meinst du wohl Bits und nicht Bytes
Ich hatte erst 1 bzw 2 Bytes da zu stehen (Die jetzt im Absatz drunterstehen). Das hab ich dann näher erklären wollen, und eben die Bits dazugeschrieben. Nur auf Bits hab ich nicht abgeändert...

Äh... das war natürlich absichtlich als Herausforderung für den aufmerksamen Leser gedacht:vroam:

@TommyB : Um mal etwas Komplexität hinzuzufügen: Bei den X-Core-Tinies gibt's übrigens zusätzlich zum Eeprom den "User ROW". Das sind quasi 32 weitere Eeprom-Bytes, die im Non Volatile Memory liegen. Da gibt's ja bereits unveränderliche Bytes wie Clock-Calibrationsdaten, Temperatur-Kalibrationsdaten, die Signature Bytes, ggf die CipID usw. Dann, zur Laufzeit unveränderliche, aber sonst programmierbare Fusebytes.
Zusätzlich gibt's da auch 32 weitere frei verfügbare nichtflüchtige Register, die wie Eeprom behandelt werden, aber zB nicht vom Chip Erase betroffen sind.
UPDI kann sie außerdem auch bei gesetzten Lockbits beschreiben.
 
Jan geht es sicherlich um folgendes:

Man überlässt dem Compiler die Verwaltung der Speicheradressen (das wird der Normalfall sein). Das heißt, man kennt die absolute Adresse eines bestimmten Bytes gar nicht. Man muss hierbei weder wissen, dass beim AVR das EEprom 1 Byte Speicherstellen hat, oder welche Register in welcher Reihenfolge oder in welchem Timing beschrieben werden müssen. Man muss nicht mal wissen, dass es sich um einen AVR Mikrocontroller handelt. Man kann 1 Byte speichern, word, integer .... ganze Arrays aus vorher gennanten Datentypen, ganze Strukturen aus unterschiedlichen Datentypen bestehend. Alles kein Problem, wenn das Programm A die Daten erstellt, speichert und liest. Hat man ein Programm B, könnte es passieren, dass sich beim erneuten Kompilieren die Adresszordnungen (welche man nicht kennt) ändert. Zum Beispiel dann, wenn man eine weitere Variable im EEprom-Bereich hinzugefügt hat. Falls nun das neue Programm B die vom alten Programm A erstellten EEprom-Einträge lesen soll, passen u.U. die Adressen nicht mehr überein. Erstellt Programm B alle Eepromdaten neu, ist alles in Ordnung.

Abhilfe: Man lässt den Compiler nicht die Adresszurodnung machen, sondern gibt diese vor. Frage nun: Wie macht man das in C.
Mit dem formulieren habe ich es nicht so. Genau das wollte ich damit eigentlich sagen. Danke @Dirk!
 
Ok, die Frage ist also:
Wie bekomme ich es hin, daß die (Hoch-)Spracheninterne Variablen-Adress-Verwaltung einen bestimmten Bereich nicht antastet, aber sonst normal verwendet werden kann. Insbesondere bezogen auf den Eeprom.

Nun, was ist eine Variable eigentlich?
Ein "Griff", den Du an eine Speicherzelle schraubst. Du, oder eben die Sprache, der Compiler. Und wenn ich eine größere Variable (word, qword, String,...) nehme?
Dann wird der Griff an die erste Zelle geschraubt.

Die Variablenverwaltung sollte sich insbesondere darum kümmern, daß es dabei zu keinen Kollisionen kommt.

Die AVR besitzen (bis zu) drei interne Speicherbereiche: Den Programmspeicher (Program Flash), den volatilen Arbeitspeicher (SRAM), und den nonvolatilen Eeprom.
Sinnigerweise sollte eine Sprache (ein Compiler) also für jeden dieser Berteiche getrennt die verwendeten Adressen (Griffe an Variablen) verwalten.

Zu C kann ich nichts sagen...
Unter Assembler wählt man den entsprechenden Bereich mit einer Direktive aus:
Alles was hinter .cseg steht, landet im Flash.
Für alles, was hinter .dseg steht werden Adressen im SRAM erzeugt.
Alles was hinter .eseg steht, landet im Eeprom.

Für jeden dieser drei Bereiche hat der Compiler 'nen eigenen … Zeiger auf die nächste frei Stelle.
Legst Du im SRAM also eine Word-Variable an (.dw) verpaßt Du der derzeitigen freien SRAM-Adresse 'nen Griff (mittels Label) - der SRAM-Zeiger auf die nächste freie Stelle wandert automatisch zwei Zellen weiter.
Legst Du im Eeprom ein Byte ab (.db) und verpaßt der Zelle 'nen Griff, wandert der Eeprom-Zeiger eins weiter.

Diese Zeiger kannst Du aber in jedem Speicherbereich beliebig umplatzieren (.org) - setzt Du also den Eeprom-Zeiger auf 0x10, werden die ersten sechzehn Adressen (Bytes) nicht verwendet, es sei denn Du setzt den Zeiger manuell wieder in diesen Bereich.
Kommt es durch das Umplatzieren zu einer Kollision, wirft das Studio 'ne entsprechende Fehlermeldung.

In Anlehnung an Thomas Beitrag #4 reservieren wir einfach mal sechzehn Bytes. Dazu reicht es, vor allen(!) anderen "Eeprom-Variablendeklarationen"

CodeBox Assembler
.eseg
.org 0x10
.cseg
zu schreiben.
Flexibler wird es, wenn man das Ende des reservierten Bereiches als Konstante hinterlegt:.equ reservedEepromSpaceEnd=0x10
Sollen an irgendwelche dieser Zellen bereits "Griffe" geschraubt werden, kann man das auch schon machen:


CodeBox Assembler
 .equ reservedEepromSpaceEnd=0x10
.eseg
.org 0x00
HardwareVersion:
.org 0x01
KaliByte:
.org 0x02
GreenInteger:
.org 0x04
Status:
.org 0x5
OrangeInteger:
.org  reservedEepromSpaceEnd
.cseg

Das ganze erzeugt noch keinen Code.
Es kann in Form einer externen Datei inkludiert werden.
Lese- und Schreibzugriffe aus dem Code heraus sind über die "Griffe" (Label) möglich.
Soll jetzt bereits beim Compilieren zB die Hardwareversion auf... "7" gesetzt werden, kannst Du das auch danach tun:

CodeBox Assembler
.eseg
.org HardwareVersion
.db 7
.org reservedEepromSpaceEnd
.cseg

Das würde keinen Code im Flash erzeugen, aber ein Eeprom-File (wo eben genau die erste Adresse mit "7" beschrieben wird).
Auch das muß natürlich vor allen anderen Eeprom-Variablen stehen.
 
Unter BASCOM sieht das ähnlich aus - Hier werden Variablen mit Dim angelegt.
Sollen sie im Eeprom liegen, ist das extra anzugeben: Dim varName as Eram varType
Eine gezielte Adresse kann durch At vorgegeben werden: Dim varName as Eram varType At location
Naheliegenderweise versucht man also:

CodeBox BascomAVR
$regfile = "m8adef.dat"
$crystal = 8000000
$hwstack=40
$swstack = 16
$framesize = 32
Dim Hardwareversion As Eram Byte
Dim Kalibyte As Eram Byte
Dim Greeninteger As Eram Integer
Dim Status As Eram Byte
Dim Orangeinteger As Eram Integer

Dim Irgendwas As Eram Byte At &H10
Dim Nochwas As Eram Byte   

Die Reservierten Bytes stimmen, "Irgendwas" landet logischerweise auch genau in Adresse 0x10, aber "Nochwas" wird nicht in Adresse 0x11 abgelegt, sondern landet in 0x07 (also direkt hinter "Orangeinteger") - BASCOM füllt die Lücken selbst auf.
Um das zu umgehen, muß also der Bereich tatsächlich "belegt" werden. Wir definieren also ein Array mit sechzehn Bytes: Dim Reservedeepromspace(16) As Eram Byte
Weitere ERAM-Variablen werden dann zwingend dahinter angelegt. Muß ich dann immer über das Array auf den reservierten Speicher zugreifen?
Nein, ich kann an irgendwelche Speicherzellen ja weitere Griffe dranschrauben (zusätzlich zu eventuell bereits vorhandenen). Bei Bascom mit At Adresse Overlay:

CodeBox BascomAVR
$regfile = "m8adef.dat"
$crystal = 8000000
$hwstack = 40
$swstack = 16
$framesize = 32

Dim Reservedeepromspace(16) As Eram Byte

Dim Hardwareversion As Eram Byte At Reservedeepromspace(1) Overlay
Dim Kalibyte As Eram Byte At Reservedeepromspace(2) Overlay
Dim Greeninteger As Eram Integer At Reservedeepromspace(3) Overlay
Dim Status As Eram Byte At Reservedeepromspace(5) Overlay
Dim Orangeinteger As Eram Integer At Reservedeepromspace(6) Overlay


Wie man sowas unter C umsetzt, muß einer von den C-Profis sagen... Ihr seid dran...
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)