Forum: Compiler & IDEs AVR-GCC: Variablen in EEPROM an fester Adresse mit Initialisierung


von Bernhard (Gast)


Lesenswert?

Hallo!

Wie kann man im C-Quellcode (AVR GCC) Variablen im EEPROM definieren so 
dass diese an einer definierbaren Adresse liegen und einen 
Initialisierungswert bekommen?

Die Standardwerte der Variablen (EEP Datei) möchte ich dann per avrdude 
bei der ersten Programmierung des Controllers programmieren.

Beispiel:

Adresse 0x0000 bis 0x00FF : Frei
Adresse 0x0100 bis 0x0101 : uint8_t mit vordefiniertem Wert 0x10
Adresse 0x0102 bis 0x01FF : Frei
Adresse 0x0200 bis 0x0201 : uint8_t mit vordefiniertem Wert 0x11

Wenn ich die Variablen nun so definiere, dann bekomme ich eine EEP-Datei 
mit den korrekten Initialisierungswerten, nur leider landen die 
Variablen an Adresse 0x0000 und 0x0001

uint8_t var1 EEMEM = 0x10;
uint8_t var2 EEMEM = 0x11;

Alternativ kann ich per #define die Adressen der Werte festlegen und 
dann an eeprom_read_byte übergeben.
Damit habe ich feste Adressen aber bekomme am Ende keine EEP-Datei mit 
den Initialisierungswerten.

Ich suche nach einer Kombination der beiden Varianten. Geht das?

Grüße,
Bernhard

: Verschoben durch User
von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

Geht perfekt über section und entsprechend angepasstes Linkerscript. 
Dann hast du volle Kontrolle aber auch entsprechend Aufwand. Wenns 
einfach sein soll erstellst du eine Struktur in der exakten Größe des 
eeprom und platzierst darin deine variablen mit entsprechenden 
Fullvariablen dazwischen.

Matthias

von Bernhard (Gast)


Lesenswert?

Also so z.B.:
1
// AVR EEPROM initalization values
2
uint8_t ee_data[E2END + 1] EEMEM = {
3
// 256 Bytes from 0x0000 to 0x00FF
4
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x0000 - 0x000F
5
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x0010 - 0x001F
6
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x0020 - 0x002F
7
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x0030 - 0x003F
8
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x0040 - 0x004F
9
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x0050 - 0x005F
10
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x0060 - 0x006F
11
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x0070 - 0x007F
12
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x0080 - 0x008F
13
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x0090 - 0x009F
14
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x00A0 - 0x00AF
15
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x00B0 - 0x00BF
16
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x00C0 - 0x00CF
17
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x00D0 - 0x00DF
18
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x00E0 - 0x00EF
19
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x00F0 - 0x00FF
20
};

Vom Aufwand her OK, leider kann man keine Werte direkt zuweisen, z.B.
1
#define EE_ADDR 0x10
2
#define EE_VAL 0xAB
3
4
ee_data[ADDR] = EE_VAL;

So kann man aber damit arbeiten wenn man genug Kommentare einfügt.
Vielen Dank!

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

Eher noch eine Struktur verwenden und kein simples Array. Dann wird auch 
die Initialisierung übersichtlich.

Matthias

von Eduard S. (schneehase)


Lesenswert?

Kann man auch über gcc machen.
Beim Linken folgendes hinzufügen:

-Wl,-section-start=platz1=0x810100 <- Platz1 bei 0x0100 +0x810000 fürs 
eeprom
-Wl,-section-start=platz2=0x810200 <- Platz2 bei 0x0200

Bei objcopy folgendes hinzufügen:

--change-section-lma platz1=0x100
--change-section-lma platz2=0x200

Und im Code dann bei den Variablen dann sections benutzen

uint8_t var1 EEMEM __attribute__((section("Platz1"))) = 0x10;
uint8_t var2 EEMEM __attribute__((section("Platz2"))) = 0x11;

von Eduard S. (schneehase)


Lesenswert?

Konnte man beim alten Atmel Studio 4 noch ganz einfach über die 
Projekteigenschaften unter Memory Settings einstellen :)

von Bernhard (Gast)


Lesenswert?

So funktionierts:

Im Quellcode:
1
#define EEMEM_PLATZ1     __attribute__((section(".platz1")))
2
#define EEMEM_PLATZ2     __attribute__((section(".platz2")))
3
4
const uint8_t val1 EEMEM_PLATZ1 = 0x10;
5
const uint8_t val2 EEMEM_PLATZ2 = 0x11;

Das Makro "EEMEM" darf hier nicht verwendet werden, sonst gibts einen 
Konflikt.

Im Makefile:

1. Linkerflags ergänzen:
1
LDFLAGS += -Wl,--section-start=.platz1=0x81000,--section-start=.platz2=0x81100

2. avr-objcopy ergänzen:
1
%.eep: %.elf
2
        @echo
3
        @echo $(MSG_EEPROM) $@
4
        -$(OBJCOPY) -j .eeprom -j .platz1 -j .platz2 --set-section-flags=.eeprom="alloc,load"  \
5
        --change-section-lma .eeprom=0 --change-section-lma .platz1=0 --change-section-lma .platz2=0x100 -O $(FORMAT) $< $@

Schließlich bekommt man ein kleines EEP-File mit nur den vordefinierten 
Werten an der richtigen Adresse.

Das Auslesen der Werte ist auch einfach und der Compiler nutzt auch die 
korrekten Adressen:
1
value = eeprom_read_byte(&val2);

- &val2 ist 0x100
- Nach dem Funktionsaufruf ist value dann 0x11

von Bernhard (Gast)


Lesenswert?

Nachtrag:

Im Makefile noch bei der Generierung der HEX Datei die zusätzlichen 
Sections ausnehmen, sonst zeigt z.B. avrdude einen Fehler an
1
%.hex: %.elf
2
        @echo
3
        @echo $(MSG_FLASH) $@
4
        $(OBJCOPY) -O $(FORMAT) -R .eeprom -R .platz1 -R .platz2 $< $@

von Le X. (lex_91)


Lesenswert?

Im Prinzip hast du 3 Möglichkeiten:

1) EMEM verwenden.
Du bekommst eine .eep Datei, die zu deiner SW passt. Aber wenn du die SW 
änderst kann es sein dass deine Variablen woanders liegen => der alte 
EEPROM-Inhalt ist nutzlos

2) der obige Ansatz mit sections. Wär mir persönlich bisl zuviel Aufwand 
:-)

3) Das EEPROM als Struktur abbilden. So mach ichs momentan. Du kriegst 
leider erstmal kein .eep-File, aber das werd ich per sections noch 
hinkriegen.
Nachteil: Wenn eine Softwarekomponente eine zusätzliche eep-Variable 
braucht musst du sie ans Ende der Struktur pfriemeln. Kann also 
irgendwann bisl durcheinander kommen. Ausser du hälst dir für jedes 
Modul Reserveblöcke vor, für zukünftige Erweiterungen.

von Bernhard (Gast)


Lesenswert?

Das EEPROM zu spalten macht dann Sinn wenn man zwei quasi unabhängige 
Applikations-Firmwares hat z.B. Bootloader und Hauptprogramm.

Jeder Teil soll was im EEPROM speichern können ohne den anderen zu 
beeinträchtigen.
Ein automatisch generiertes EEP File mit Standardwerten macht Sinn bei 
der Programmierung von > 1 Stück. Außerdem ist so in der Datei nur das 
drin was ich brauche, keine unnötigen Füll-Arrays o.ä. die nur die 
Programmierung verlangsamen.

Ich habe mir jetzt mit zwei Sections geholfen. Eine für den Bootloader 
und eine für das Hauptprogramm.
Das schöne dabei: In dem Quellcode muss ich nur die Standardwerte 
setzen.
Im Makefile setze ich nur die jeweiligen Startadressen der Sections.

Am Anfang des EEPROM lasse ich etwas Platz frei, dann der erste und 
zweite Block.

In jeder Section ist dann wiederum ein struct mit den Variablen.
Der Zugriff erfolgt einfach per
1
eeprom_read_byte(&config_struct.variable)

Wenn ich in Zukunft noch mehr Variablen ablegen möchte, dann hab ich 
innerhalb der Sections noch genug Platz. Die Section für den Bootloader 
ist recht klein, z.B. 112 Bytes (128 Bytes - 16 Bytes vom Anfang), 
Beginn der Section für das Hauptprogramm dann ab 0x80
Wenn man dann mal einen größeren Controller mit mehr EEPROM einsetzt 
kann das dann auch genutzt werden (oder ist in gewissen Grenzen auch 
abwärtskompatibel)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.