Forum: Compiler & IDEs ARM-GCC: EEPROM abbilden?


von Ralf (Gast)


Lesenswert?

Hi,

mich würde mal interessieren, wie ihr ein externes EEPROM (z.B. I2C oder 
SPI) in euren Applikationen abbildet. Mir fallen zwei Möglichkeiten ein:

1) Variablen & Adressen per #define "anlegen"
2) modifiziertes Linkerscript -> Section mit entsprechender Größe in 
einem freien Bereich des ARM-Controllers anlegen, nicht initialisieren 
lassen und Adress-Offset in den Zugriffsroutinen rausrechnen

Variante 1) erscheint mit recht umständlich, weil man erstens das 
Speichermanagement von Hand macht und dabei Fehler (z.B. Überlappungen) 
auftreten können und komfortabel dürfte es auch nicht sein.
Variante 2) könnte es erlauben, dass man ein entsprechendes struct 
anlegt, welches den EEPROM-Inhalt abbildet, ggf. mit dem Attribut 
'packed'.

Gibt's noch weitere Möglichkeiten?

Ralf

von Lothar (Gast)


Lesenswert?

Es wäre mir neu das man externen I2C/SPI Speicher in den Adressraum 
mappen kann, das geht nur bei Anbindung über den EMC (external memory 
controller) z.B. im LPC1788 Selbst bei ARMs mit internem EEPROM ist das 
nicht gemappt sondern wird über IAP Funktionen beschrieben z.B. LPC11A14

Am einfachsten ist es die Daten als struct im SRAM zu halten und eine 
Funktion zu machen, die bei einer Änderung in der struct genau diese 
Änderung findet und im EEPROM aktualisiert.

Man könnte es auch per DMA machen, aber dann würde auch bei einer 
kleinen Änderung das ganze EEPROM neu geschrieben.

von Ralf (Gast)


Lesenswert?

Hi Lothar,

> Es wäre mir neu das man externen I2C/SPI Speicher in den Adressraum
> mappen kann
Sorry, so war's auch nicht gemeint, mit "freier Bereich" meinte ich 
eben, dass man eine Section anlegt, die im Adressraum im nicht belegten 
Bereich angesiedelt ist.
Auf diese Weise erhält man eben eine automatische Adressvergabe der 
Variablen.

> Am einfachsten ist es die Daten als struct im SRAM zu halten und eine
> Funktion zu machen, die bei einer Änderung in der struct genau diese
> Änderung findet und im EEPROM aktualisiert.
Mach das mal, wenn ein 16Mbit-EEPROM dranhängt und der Controller nur 
ein paar KByte RAM hat :)

Übrigens, das von dir oben erwähnte Mappen in den Adressraum wird im 
8-Bit-Controller-Bereich von ein, zwei Compilerherstellern unterstützt, 
ein sehr nettes Feature, wie ich finde.
Kostet marginal Laufzeit, macht dafür die Programme lesbarer. Tut hier 
aber nichts zur Sache, ich bezweifle, dass der GCC um diese 
Funktionalität erweitert wird.

Ralf

von Lothar (Gast)


Lesenswert?

Ralf schrieb:
> Mach das mal, wenn ein 16Mbit-EEPROM dranhängt und der Controller nur
> ein paar KByte RAM hat :)

Genau dafür gibt es aber ARMs mit EMC wo das EEPROM 8/16/32-bit parallel 
angeschlossen wird z.B. (hier wurde gepuffertes SDRAM genommen EEPROM 
geht aber genau so):

http://www.embeddedartists.com/products/oem/lpc1788_oem.php
http://www.embeddedartists.com/products/oem/lpc4088_oem.php

> Übrigens, das von dir oben erwähnte Mappen in den Adressraum wird im
> 8-Bit-Controller-Bereich von ein, zwei Compilerherstellern unterstützt,
> ein sehr nettes Feature, wie ich finde.

Ich hatte gedacht auch dafür muss das EEPROM parallel mit Adress- und 
Datenbus angeschlossen sein z.B. beim 8051 über die entsprechenden 
Ports.

von Fabian O. (xfr)


Lesenswert?

Man könnte eine Struktur mit allen EEPROM-Daten deklarieren und nur 
einen Zeiger darauf anlegen. Dann muss man nicht von Hand die Adressen 
vergeben. Allerdings darf man natürlich nur über die eeprom-Funktionen 
auf die Daten zugreifen. Außerderm darf das EEPROM nicht größer als der 
Adressraum des Mikrocontrollers sein. Mit 16-Bit-Zeigern kann man 
schlecht ein MBit-großes EEPROM verwalten ...
1
typedef struct {
2
  uint8_t  a;
3
  uint16_t b;
4
  uint32_t c;
5
} eeprom_t __attribute__(__packed__);
6
7
eeprom_t* eeprom = (eeprom_t*) 0;
8
9
uint32_t var = 50;
10
eeprom_write(&eeprom->c, &var, sizeof(eeprom->c));

Wahrscheinlich ist es auch nicht ganz koscher gemäß C-Standard, von 
wegen Wert des Nullpointers, Zeiger vs. Adressen usw., aber in der 
Praxis dürfte es gehen ...

von Ralf (Gast)


Lesenswert?

> Genau dafür gibt es aber ARMs mit EMC wo das EEPROM 8/16/32-bit parallel
> angeschlossen wird z.B. (hier wurde gepuffertes SDRAM genommen EEPROM
> geht aber genau so):
Okay, das ist natürlich ne Möglichkeit. Mir geht's jetzt halt darum, wie 
man das dann eben abbildet, wenn man ein über I2C oder SPI 
angeschlossenes EEPROM (oder DAC oder, oder, oder...) hat mit relativ 
vielen möglichen Adressen hat.

Ich kann mir nicht vorstellen, dass EEPROM-Strukturen tatsächlich 
händisch per #define aufgebaut werden, oder?

> Ich hatte gedacht auch dafür muss das EEPROM parallel mit Adress- und
> Datenbus angeschlossen sein z.B. beim 8051 über die entsprechenden
> Ports.
Nein. Übrigens sind die Compiler auf die ich mich bezog genau für den 
8051/52-Kern ;)
Die Funktionsweise für dieses Prinzip ist relativ simpel: Gegenüber 
Standard-C muss bei C für 8051 angegeben werden, in welchem 
Speicherbereich die Variablen liegen, also DATA, IDATA, PDATA, XDATA, 
etc. sofern diese nicht einem wählbaren Standard-Speicher zugeordnet 
sind (was durch Weglassen der Bereichsangabe erreicht wird). Jeder 
Speicherbereich wird durch unterschiedliche Instruktionen angesprochen.
Speziell bei Pointern muss dann je nachdem auch eingestellt werden, auf 
welchen Speicherbereich gezeigt wird, sodass der Compiler gleich die 
passenden Befehle wählen kann. Unterlässt man dies, handelt es sich um 
einen generischen Pointer, die Zugriffsart wird zur Laufzeit bestimmt.
Der Pointer bekommt dann nicht den üblichen 8- bzw. 16-Bit breiten 
Adressbereich, sondern einen 24-Bit breiten Adressraum.
Dieses zusätzliche dritte Adressbyte gibt dann an, um welchen 
Speicherbereich es sich handelt.
Der Programmierer kann sich somit ein Segment passender Größe anlegen, 
um einen EEPROM-Speicherbereich zu erstellen. Die Compiler-internen 
Laufzeitroutinen konnten hierzu durch Assemblerfunktionen für das 
entsprechende Segment erweitert werden. Ich hab damit EEPROMs, DACs, 
etc. abgehandelt, im C-Sourcecode waren es aber immer ganz normale 
Variablen (abgesehen von der Segmentzuteilung natürlich).
Einziger Wehrmutstropfen ist wie bereits erwähnt die 
Laufzeitverzögerung, da erst durch die internen Zugriffsfunktionen 
gehangelt wurde bevor die Erweiterungen an den Start durften.

Das geht wohl leider beim GCC nicht, obwohl ich's für den 
Embedded-Bereich echt schnucklig fände :)
Gut, soll wie gesagt auch nicht das Thema hier sein, vielleicht bin ich 
eben der einzige, der's für brauchbar hält.

So, und wie bildet ihr denn nun serielle Speicher ab? :)

Ralf

von Ralf (Gast)


Lesenswert?

@Fabian:
Dein Vorschlag geht in die Nähe meiner Variante 2. Dazu müsste ja dann 
"nur" noch das Linker-Script angepasst und der entsprechende Speicher 
angelegt werden, oder? In einem Adressbereich eben, für den im 
ARM-Controller nichts zugeordnet ist und der auch nicht initialisiert 
werden darf.

Ralf

von Fabian O. (xfr)


Lesenswert?

Ralf schrieb:
> Dein Vorschlag geht in die Nähe meiner Variante 2. Dazu müsste ja dann
> "nur" noch das Linker-Script angepasst und der entsprechende Speicher
> angelegt werden, oder? In einem Adressbereich eben, für den im
> ARM-Controller nichts zugeordnet ist und der auch nicht initialisiert
> werden darf.

Nein, ich hab die Struktur gar nicht im Speicher angelegt. Ich habe nur 
einen Zeiger auf die Struktur definiert, der den Wert 0 besitzt. Ich tue 
also so, als ob diese Variablen ab Adresse 0 im Systemspeicher liegen. 
Was dort tatsächlich liegt, ob das SRAM, irgendwelche CPU-Register oder 
sonstwas sind, spielt kein Rolle, solange man den Zeiger nicht 
dereferenziert.

Es geht also nur darum, dass der Compiler den Elementnamen in der 
Struktur eine Zahl (= Adresse im EEPROM) zuordnet.
&eeprom->a hat den Wert 0.
&eeprom->b hat den Wert 1.
&eeprom->c hat den Wert 3.

Diese Zahl wird der Funktion übergeben, die dann per SPI/I2C auf das 
EEPROM zugreift. Ich gehe davon aus, dass man auf das EEPROM byteweise 
zugreifen kann und die Adressen ab 0 beginnen.

Der Aufruf
1
eeprom_write(&eeprom->c, &var, sizeof(eeprom->c));
ist also nichts anders als
1
eeprom_write(3, &var, 4);
Sprich: Lese 4 Bytes von der RAM-Adresse &var und schreibe sie ins 
EEPROM an die Adresse 3.

Man darf nur wie gesagt nie die eeprom-Datenstruktur direkt 
dereferenzieren. Also nicht:
1
eeprom->c = var;
Das hätte böse Folgen, weil dann wie gesagt sonstwo hingeschrieben wird 
...

Deine Variante mit dem Linker-Script würde ein bisschen anders gehen. Da 
würde man die Adressen nicht bei 0 beginnen lassen, sondern einen freien 
Adressbereich dafür nehmen. Diesen Offset müsste man dann in den 
EEPROM-Funktionen wieder rausrechnen. Der Vorteil wäre, dass man die 
Daten dann nicht in einem Struct organisieren muss, sondern auch einfach 
einzelne Variablen über das Programm verteilt anlegen kann. Der Linker 
kümmert sich ja dann darum, dass sich die Adressen nicht überschneiden.

Wenn man aber sowieso nur eine Struktur mit allen Daten hat, kann man 
sich das ja sparen. Es sei denn ich übersehe etwas, was dagegen spricht?

von Ralf (Gast)


Lesenswert?

Hi Fabian,

> Nein, ich hab die Struktur gar nicht im Speicher angelegt. Ich habe nur
> einen Zeiger auf die Struktur definiert, der den Wert 0 besitzt. Ich tue
> also so, als ob diese Variablen ab Adresse 0 im Systemspeicher liegen.
oO Das geht? Das wusste ich nicht. Das könnte ich mal probieren. Ich 
dachte immer, dass ein struct etc. auch als "echte" Variable existieren 
muss, bevor ein Pointer Zugriff auf die Adressen bekommt.

> Man darf nur wie gesagt nie die eeprom-Datenstruktur direkt
> dereferenzieren.
Okay.

> Deine Variante mit dem Linker-Script würde ein bisschen anders gehen. Da
> würde man die Adressen nicht bei 0 beginnen lassen, sondern einen freien
> Adressbereich dafür nehmen. Diesen Offset müsste man dann in den
> EEPROM-Funktionen wieder rausrechnen. Der Vorteil wäre, dass man die
> Daten dann nicht in einem Struct organisieren muss, sondern auch einfach
> einzelne Variablen über das Programm verteilt anlegen kann. Der Linker
> kümmert sich ja dann darum, dass sich die Adressen nicht überschneiden.
Das allein würde ich eben schon als großen Vorteil ansehen, den Aufwand 
für's Rausrechnen der Offsetadresse halte ich für vernachlässigbar 
verglichen mit dem Vorteil.

> Wenn man aber sowieso nur eine Struktur mit allen Daten hat, kann man
> sich das ja sparen. Es sei denn ich übersehe etwas, was dagegen spricht?
Bezogen auf deine obige Warnung, dass man nicht dereferenzieren darf 
würde ich selbst dann, wenn man alles in eine Struktur packt, den 
Vorteil darin sehen, dass WENN man aus Versehen dereferenziert nicht aus 
Versehen irgendwohin geschrieben wird, wo man echten Schaden anrichten 
könnte, das aber nicht sofort auffällt. Versucht man ins Flash zu 
schreiben wird wahrscheinlich nix passieren, aber wenn's ins RAM geht 
verbeult man sich evtl. was, und das kann erst später im Programm 
Auswirkungen haben.
Die separate Memory-Section wäre dann m.E. sauberer, was meinst du?

Ralf

von Fabian O. (xfr)


Lesenswert?

> oO Das geht? Das wusste ich nicht. Das könnte ich mal probieren. Ich
> dachte immer, dass ein struct etc. auch als "echte" Variable existieren
> muss, bevor ein Pointer Zugriff auf die Adressen bekommt.

In Header-Files der Chip-Hersteller wird das oft so gemacht, um die 
Adressen von IO-Registern usw. festzulegen. Hier ist ein Beispiel, wie 
das beim Atmel XMEGA aussieht:
Beitrag "Re: Headerdatei avr/io.h"

>> Deine Variante mit dem Linker-Script würde ein bisschen anders gehen. Da
>> würde man die Adressen nicht bei 0 beginnen lassen, sondern einen freien
>> Adressbereich dafür nehmen. Diesen Offset müsste man dann in den
>> EEPROM-Funktionen wieder rausrechnen. Der Vorteil wäre, dass man die
>> Daten dann nicht in einem Struct organisieren muss, sondern auch einfach
>> einzelne Variablen über das Programm verteilt anlegen kann. Der Linker
>> kümmert sich ja dann darum, dass sich die Adressen nicht überschneiden.
> Das allein würde ich eben schon als großen Vorteil ansehen, den Aufwand
> für's Rausrechnen der Offsetadresse halte ich für vernachlässigbar
> verglichen mit dem Vorteil.

Klar, von der Rechenleistung her ist das kein Ding. Aber man braucht 
eben ein spezielles Linkerscript, muss den Variablen per Attribut die 
Section zuweisen usw.. Macht den Code also weniger portabel und vom 
Compiler/Linker abhängig. Wenn Dich das nicht stört, ists aber natürlich 
komfortabler.

> Bezogen auf deine obige Warnung, dass man nicht dereferenzieren darf
> würde ich selbst dann, wenn man alles in eine Struktur packt, den
> Vorteil darin sehen, dass WENN man aus Versehen dereferenziert nicht aus
> Versehen irgendwohin geschrieben wird, wo man echten Schaden anrichten
> könnte, das aber nicht sofort auffällt. Versucht man ins Flash zu
> schreiben wird wahrscheinlich nix passieren, aber wenn's ins RAM geht
> verbeult man sich evtl. was, und das kann erst später im Programm
> Auswirkungen haben.
> Die separate Memory-Section wäre dann m.E. sauberer, was meinst du?

Sauberer wärs schon. Allerdings wärs mir am liebsten, wenn man es sofort 
mitbekommen würde, wenn man den Fehler macht und die Adressen 
derefenziert. Das beste wäre, wenn der Controller sich dann resetten 
würde. Das würde man gleich merken. Schlecht wäre, wenn dann einfach 
nichts passiert. Dann sucht man ggf. lange nach dem Fehler. Kommt 
wahrscheinlich auf die Hardware an.

von Ralf (Gast)


Lesenswert?

> In Header-Files der Chip-Hersteller wird das oft so gemacht, um die
> Adressen von IO-Registern usw. festzulegen.
Ja, so ist das beim ARM-Cortex ja auch, wobei ich meine, dass da eben 
eine Variable des structs angelegt wird, müsst ich jetzt aber 
nachgucken.

> Aber man braucht eben ein spezielles Linkerscript, muss den Variablen per
> Attribut die Section zuweisen usw.. Macht den Code also weniger portabel
> und vom Compiler/Linker abhängig. Wenn Dich das nicht stört, ists aber
> natürlich komfortabler.
Mmmh, okay, das stimmt. Aber andersrum gesehen wird der Großteil der 
Cortex-Software wahrscheinlich mit dem GCC geschrieben, denke ich. 
Welche IDE etc. drüber liegt (Rowley, Code-Red, etc.) dürfte denke ich 
keine Auswirkungen haben.

> Sauberer wärs schon. Allerdings wärs mir am liebsten, wenn man es sofort
> mitbekommen würde, wenn man den Fehler macht und die Adressen
> derefenziert. Das beste wäre, wenn der Controller sich dann resetten
> würde. Das würde man gleich merken. Schlecht wäre, wenn dann einfach
> nichts passiert. Dann sucht man ggf. lange nach dem Fehler. Kommt
> wahrscheinlich auf die Hardware an.
Das hab ich in einem anderen Zusammenhang probiert: Ich wollte wissen, 
was genau passiert, wenn ich einen Pointerzugriff (lesend/schreibend) 
direkt hinters RAM mache. Ging in beiden Fällen direkt in den 
BusFaultHandler. Welchen Controller verwendest du?

Ralf

von Le X. (lex_91)


Lesenswert?

Ich habs schon in Automotive-Code gesehen dass das so gemacht wird, also 
die Struktur und dann nen Zeiger darauf erstellen.
Über geeignete Makros kann man das Ganze gut verpacken.

Jedes Softwaremodul das Zugriff aufs EEPROM benötigt inkludiert einen 
Header, der Makros und Struktur definiert.

Hat sich anscheinend bewährt, diese Methode.

von Roland H. (batchman)


Lesenswert?

Ralf schrieb:
> In einem Adressbereich eben, für den im
> ARM-Controller nichts zugeordnet ist und der auch nicht initialisiert
> werden darf.

"Den ARM" gibt es nicht. Insofern unterscheiden sich die zugeordneten 
Bereiche. Wenn es portabel sein soll, ist das etwas aufwändiger.

Fabian O. schrieb:
> Man darf nur wie gesagt nie die eeprom-Datenstruktur direkt
> dereferenzieren. Also nicht:

µC-spezifisch kann man den Zeiger schon auf einen Bereich zeigen lassen, 
welcher ungültig ist. Beim direkten Schreiben oder Lesen erzeugt das 
dann einen Fault. Das Offset muss natürlich bei den Funktionen abgezogen 
werden.

Das ganze Thema ist ziemlich heterogen. Ohne Einschränkung auf "extern" 
kämen noch die stm32-Flash-Emulation mit "page sizes" von 512 bis 16k 
hinzu. Aber auch bei externen möchte man nicht immer byte-weise 
zugreifen.

Ralf schrieb:
> im C-Sourcecode waren es aber immer ganz normale
> Variablen

Warum finden das viele so toll?
Im Allgemeinen und im speziellen Fall (wg. page size/addresses) ist das 
m. E. nicht so gut, wenn es später eben byte-weise nicht mehr geht. Das 
Portieren ist dann arg lästig.

Insofern verwende ich (mit Ausnahme von manchen normalen Variablen) 
immer setter/getter, static inline oder templates. Das kann dann auch 
jeder Compiler. Die große Ausnahme sind Sprachen (im Gegensatz zu 
Compiler), welche aus einer Zuweisung letztlich einen Funktionsaufruf 
machen (Scala). Nur in diesen Fällen kann zentral etwas ergänzt werden.

Zurück zum Thema EEPROM: Mein Favorit ist inwzischen die SD-Karte. 
Komplexer, aber im Gegensatz zum struct-Ansatz können die Daten 
dynamisch wachsen. Auslesemöglichkeit ist am PC gegeben. Selbst wenn man 
FAT weglässt, hat man immer noch eine Art SPI EEPROM mit 512 Byte page 
size, das man am PC auslesen kann.

Die Idee mit dem "linker script" ... irgendwie arg gekünstelt.

Fabian O. schrieb:
> Das beste wäre, wenn der Controller sich dann resetten
> würde.

Nun, im Falle vom ARM will man das nicht sofort, sondern erst nach dem 
"hard fault handler", welcher die Adresse ausgibt :-)

von Ralf (Gast)


Lesenswert?

@le x.:
> Jedes Softwaremodul das Zugriff aufs EEPROM benötigt inkludiert einen
> Header, der Makros und Struktur definiert.
Stimmt, das wär ne Möglichkeit.

@Roland:
> "Den ARM" gibt es nicht. Insofern unterscheiden sich die zugeordneten
> Bereiche. Wenn es portabel sein soll, ist das etwas aufwändiger.
Klar, den gibt's nicht. Selbst untereinander wird's nicht einfach so 
portabel sein, Anpassungen wird man denke ich fast immer machen müssen.

> µC-spezifisch kann man den Zeiger schon auf einen Bereich zeigen lassen,
> welcher ungültig ist. Beim direkten Schreiben oder Lesen erzeugt das
> dann einen Fault. Das Offset muss natürlich bei den Funktionen abgezogen
> werden.
Das wäre dann auch beim angepassten Linkerscript gegeben.

> Das ganze Thema ist ziemlich heterogen. Ohne Einschränkung auf "extern"
> kämen noch die stm32-Flash-Emulation mit "page sizes" von 512 bis 16k
> hinzu. Aber auch bei externen möchte man nicht immer byte-weise
> zugreifen.
Was mitunter wegen dem Paging auch gar nicht geht, zumindest beim 
Schreiben.

> Warum finden das viele so toll?
Weils den Code vereinfacht und man nicht immer ellenlange 
Funktionsaufrufe hintackern muss, nur um ein Byte zu lesen/schreiben?

> Im Allgemeinen und im speziellen Fall (wg. page size/addresses) ist das
> m. E. nicht so gut, wenn es später eben byte-weise nicht mehr geht. Das
> Portieren ist dann arg lästig.
Da stimme ich zu. Aber das trifft m.E. dann auch auf jede andere 
Möglichkeit zu, denn spätestens wenn sich das vermeintlich gleiche 
Interface bzw. dessen Register in der Ansteuerung unterscheiden, geht's 
eh los...

> Zurück zum Thema EEPROM: Mein Favorit ist inwzischen die SD-Karte.
> Komplexer, aber im Gegensatz zum struct-Ansatz können die Daten
> dynamisch wachsen.
Aber das kann's doch bei nem normalen EEPROM bzw. Flash auch?

> Auslesemöglichkeit ist am PC gegeben. Selbst wenn man FAT weglässt, hat
> man immer noch eine Art SPI EEPROM mit 512 Byte page size, das man am PC
> auslesen kann.
Okay, das ist "kompatibel" zu meinem 16MBit Teilchen, mir war's konkret 
zuviel da ins Layout noch passende Kartenhalter etc. reinzupfriemeln. 
Abgesehen davon hab ich gelesen, dass bzgl. Ansteuerung bzw. Timing 
nicht immer alles auf Anhieb läuft...

> Die Idee mit dem "linker script" ... irgendwie arg gekünstelt.
Wie anscheinend jede andere Möglichkeit auch. Offenbar ist es nicht so 
einfach, im embedded-Bereich beliebige embedded-Komponenten miteinander 
zu verknüpfen :)

> Nun, im Falle vom ARM will man das nicht sofort, sondern erst nach dem
> "hard fault handler", welcher die Adresse ausgibt :-)
Genau das meinte ich weiter oben :)

Ralf

von Roland H. (batchman)


Lesenswert?

Ralf schrieb:
> Weils den Code vereinfacht und man nicht immer ellenlange
> Funktionsaufrufe hintackern muss, nur um ein Byte zu lesen/schreiben?

Schon. Es ist aber nicht mehr ersichtlich, dass hinter einer simplen 
Zuweisung evtl. doch eine aufwändige Operation steht. Eine IDE hilft 
auch bei der Expansion.

Ralf schrieb:
> Aber das trifft m.E. dann auch auf jede andere
> Möglichkeit zu, denn spätestens wenn sich das vermeintlich gleiche
> Interface bzw. dessen Register in der Ansteuerung unterscheiden, geht's
> eh los...

Dann war es aber keine Schnittstelle auf etwas höherem 
Abstraktionsniveau.
Wenn die Schnittstelle lautet "schreibe ein Byte/Wort/Doppelwort ins 
EEPROM, egal wie performant das ist", dann ist der dezentrale Part nicht 
betroffen.
Die andere Schnittstelle wäre dann "schreibe einen Puffer maximal 
schnell, und ich akzeptiere, dass die Adressen nur bestimmte Werte haben 
dürfen".

Diese Semantik geht aus einem " = " nicht hervor.

Oder "GPIO Richtung input/output": Andere verwenden die Zuweisung z. B. 
auch beim Setzen der Richtung eines GPIO-Pins. Geht gut, solange das 
exakt ein Bit ist. Geht dann nicht mehr gut, wenn wie beim stm32 eben 
mehrere Bits involviert sind. Muss nicht gut gehen, wenn die Zuweisungen 
auf PIC24 atomar sind, auf einem PIC32 aber nicht. Das fällt dann erst 
später auf.

Ralf schrieb:
> Aber das kann's doch bei nem normalen EEPROM bzw. Flash auch?

Schon. Aber mit welcher Datenstruktur? FAT kann es einfach :-)

Vielleicht gibt es eine Art FAT-Implementierung für EEPROMs.

Ralf schrieb:
> Okay, das ist "kompatibel" zu meinem 16MBit Teilchen, mir war's konkret
> zuviel da ins Layout noch passende Kartenhalter etc. reinzupfriemeln.

Klar. Es hat in der Tat Nachteile. Deshalb schrieb ich auch "Favorit" 
:-)

Ralf schrieb:
> Abgesehen davon hab ich gelesen, dass bzgl. Ansteuerung bzw. Timing
> nicht immer alles auf Anhieb läuft...

Das kann ich nicht bestätigen. LCDs sind da viel viel schlimmer.
Es ist eher ein Massenprodukt, insofern gibt es vermutlich weniger 
Probleme. Man soll nur Marken-SDs verwenden, daran habe ich mich 
gehalten.

Andererseits habe ich schon Probleme damit gehabt, dass auch bei I2C 
EEPROMs die Timings beim Schreiben exakt eingehalten werden müssen.

Ralf schrieb:
> Offenbar ist es nicht so
> einfach, im embedded-Bereich beliebige embedded-Komponenten miteinander
> zu verknüpfen :)

Warum nicht? SPI, CAN, RF, SD/FAT, ... eigentlich alles OK, EEPROM ist 
irgendwie ein Sonderfall.

Die Verknüpfung ist nicht das Problem, es ist die dahinterliegende 
Heterogenität der jeweiligen EEPROM-Technik oder Emulation.

von Ralf (Gast)


Lesenswert?

Hi Roland,

> Schon. Es ist aber nicht mehr ersichtlich, dass hinter einer simplen
> Zuweisung evtl. doch eine aufwändige Operation steht. Eine IDE hilft
> auch bei der Expansion.
Das stimmt zwar, ist aber eine Sache der Sichtweise. "Eigentlich" 
handelt es sich ja (im Falle eines Speichers) am Ende doch wieder um 
eine Variable, die daraus gelesen bzw. in die geschrieben werden soll. 
Also ist die "simple Zuweisung" aus reiner Programmierer-Sicht die 
richtige(re).

> Dann war es aber keine Schnittstelle auf etwas höherem
> Abstraktionsniveau.
> Wenn die Schnittstelle lautet "schreibe ein Byte/Wort/Doppelwort ins
> EEPROM, egal wie performant das ist", dann ist der dezentrale Part nicht
> betroffen.
Du meinst, wenn beispielsweise erst eine komplette Page gelöscht werden 
muss, nur um eine Byte-Position zu ändern?

> Die andere Schnittstelle wäre dann "schreibe einen Puffer maximal
> schnell, und ich akzeptiere, dass die Adressen nur bestimmte Werte haben
> dürfen".
Das versteh ich nicht (bezogen auf die Speicher).

> Diese Semantik geht aus einem " = " nicht hervor.
Muss sie das denn (wirklich)?

> Oder "GPIO Richtung input/output": Andere verwenden die Zuweisung z. B.
> auch beim Setzen der Richtung eines GPIO-Pins. Geht gut, solange das
> exakt ein Bit ist. Geht dann nicht mehr gut, wenn wie beim stm32 eben
> mehrere Bits involviert sind. Muss nicht gut gehen, wenn die Zuweisungen
> auf PIC24 atomar sind, auf einem PIC32 aber nicht. Das fällt dann erst
> später auf.
Dazu kenn ich leider die Portstrukturen der genannten Controller nicht.
Beim Standard-8051 ist die Richtung implizit aus dem Portlatch 
abgeleitet (weil open-drain mit Pull-Up), bei nem SiLabs-8051 
beispielsweise gibt's noch ein Register um den Port in den 
Push-Pull-Betrieb zu versetzen und eines um die Ausgangstreiber zu 
deaktivieren und den analogen Betrieb zu ermöglichen.
Wie das beim STM32(Cortex-M3, vermute ich) bzw. PIC32 gelöst ist weiss 
ich nicht. Der Cortex-M3 von SiLabs hat mindestens die o.g. 
Pineigenschaften des Silabs-8051.

> Schon. Aber mit welcher Datenstruktur? FAT kann es einfach :-)
> Vielleicht gibt es eine Art FAT-Implementierung für EEPROMs.
Ja, FAT ist eine Datenstruktur, aber du hattest es oben auf die SD-Karte 
bezogen, und die ist ohne Datenstruktur auch nur ne Sammlung von Pages 
:)

> Klar. Es hat in der Tat Nachteile. Deshalb schrieb ich auch "Favorit"
> :-)
Beim nächsten Design schau ich mir halt mal an, welche Kartenhalter 
leicht zu bekommen sind, und spiel dann mal damit rum. Es dürfte sich 
m.E. nicht wirklich anders verhalten als das serielle 16MBit-Flash, 
welches ich momentan einsetzen möchte, abgesehen von der Initialisierung 
vielleicht (soviel zur Theorie :).

> Das kann ich nicht bestätigen. LCDs sind da viel viel schlimmer.
> Es ist eher ein Massenprodukt, insofern gibt es vermutlich weniger
> Probleme. Man soll nur Marken-SDs verwenden, daran habe ich mich
> gehalten.
Ich hab's ja auch nur gelesen, ob's stimmt, weiss ich nicht. Kommt 
wahrscheinlich auch drauf an, woher der Treiber stammt, 
selbstgeschrieben oder aus'm Netz gesaugt.

> Andererseits habe ich schon Probleme damit gehabt, dass auch bei I2C
> EEPROMs die Timings beim Schreiben exakt eingehalten werden müssen.
Kommt drauf an, wenn ein Minimum-Wert angegeben ist, halte ich mich an 
den genauso wie ans Maximum.

> Warum nicht? SPI, CAN, RF, SD/FAT, ... eigentlich alles OK, EEPROM ist
> irgendwie ein Sonderfall.
Das ist jetzt aber ein Mix aus Schnittstellen und Speichern, deswegen 
sehe ich EEPROM etc. nicht als Sonderfall an.
Das (ursprüngliche) Problem ist eher die Tatsache, dass sich selbst in 
Toolchains und Werkzeugen, welche den embedded-Bereich bedienen nicht 
alle embedded-Komponenten (ideal) abbilden lassen (was auf alle anderen 
Bereiche sicherlich auch zutrifft), aber machbar wäre es eigentlich, und 
vom Aufwand her wahrscheinlich nicht wirklich schwer zu implementieren.

> Die Verknüpfung ist nicht das Problem, es ist die dahinterliegende
> Heterogenität der jeweiligen EEPROM-Technik oder Emulation.
Doch, wenn man das Entwicklungswerkzeug als Verknüpfungspunkt nimmt, 
dann schon :)

Ralf

von Ralf (Gast)


Lesenswert?

Hi,

wenn ich jetzt abschließend (oder nur zwischendurch?) ein Resúme aus dem 
ganzen ziehe, dann gibt's wohl keine allgemeingültige und echt saubere 
Lösung.

Structs zu verwenden ist bei kleineren Speichern vorteilhaft, optional 
mit zusätzlichem Segment in nicht verwendetem Adressbereich.
Bei größeren Speichern sind Structs nicht mehr einfach zu pflegen, zumal 
dann evtl. erschwerend hinzukommt, dass die Daten ja auch dynamisch sein 
können, was evtl. für ein Filesystem spricht.

Das reicht mir aber als Ansatzanpunkt für eine erste Implementierung.

Vielen Dank an alle!

Ralf

von W.S. (Gast)


Lesenswert?

Ralf schrieb:
> wie ihr ein externes EEPROM (z.B. I2C oder
> SPI) in euren Applikationen abbildet

Ralf schrieb:
> wenn ich jetzt abschließend (oder nur zwischendurch?) ein Resúme aus dem
> ganzen ziehe, dann gibt's wohl keine allgemeingültige und echt saubere
> Lösung.

Aber ja doch: zwei Funktionen sind völlig ausreichend für eine 
allgemeine und echt saubere Lösung:

bool EE_Write(long addr, char content);
char EE_Read(long addr);

das reicht auch noch in 100 Jahren (bei long adressen..).

Ansonsten sind bei allen EEPROM's (wir reden hier über I2C oder SPI 
Devices, ja?) keine Möglichkeiten vorhanden, deren Inhalte über 
irgendwelche Hardware direkt in den Adreßraum einzublenden.

Verschärft gilt dies für serielle Flashroms, bei denen man ein Procedere 
fahren muß, um damit was anfangen zu können: Bereich in internen ram 
laden, diesen auslesen, sektorweise löschen, ram laden, ram 
zurückschreiben und so weiter.


W.S.

von Ralf (Gast)


Lesenswert?

Hallo W.S.,

> Aber ja doch: zwei Funktionen sind völlig ausreichend für eine
> allgemeine und echt saubere Lösung:
Das sehe ich ein, ich frag mich aber, wie dann die Adressen "sauber" 
generiert werden, siehe oben (über structs, #define oder Segment in 
unbenutzem Speicherbereich).

> Ansonsten sind bei allen EEPROM's (wir reden hier über I2C oder SPI
> Devices, ja?) keine Möglichkeiten vorhanden, deren Inhalte über
> irgendwelche Hardware direkt in den Adreßraum einzublenden.
Darum ging's mir eben, ob es dafür eine nicht hardware-basierte saubere 
Lösung gibt. Es macht keinen Sinn das EMIF zu aktivieren und dann einen 
"intelligenten" Parallel-Seriell-Wandler zwischenzuschalten, dann könnte 
man auch gleich parallelen Speicher verwenden.

> wir reden hier über I2C oder SPI Devices, ja?
Allgemein ja. EEPROMs haben ja den "Vorteil", dass sie byteweise 
beschrieben werden könnten.
Wird größerer Speicher benötigt, geht's eben Richtung Flash (s.u.)

> Verschärft gilt dies für serielle Flashroms, bei denen man ein Procedere
> fahren muß, um damit was anfangen zu können: Bereich in internen ram
> laden, diesen auslesen, sektorweise löschen, ram laden, ram
> zurückschreiben und so weiter.
In meinem Fall hab ich ein SPI-Flash dran, welches zumindest das Löschen 
von Pages erlaubt, die meisten erlauben ja nur das Löschen kompletter 
Sektoren. Das tut der eigentlichen Frage aber keinen Abbruch, ich würde 
die "Zwischenschicht" mit Auslesen einer Page etc. eben mit rein nehmen.

Ralf

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.