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
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.
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
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.phphttp://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.
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
typedefstruct{
2
uint8_ta;
3
uint16_tb;
4
uint32_tc;
5
}eeprom_t__attribute__(__packed__);
6
7
eeprom_t*eeprom=(eeprom_t*)0;
8
9
uint32_tvar=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 ...
> 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
@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
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?
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
> 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.
> 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
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.
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 :-)
@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
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.
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
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
Ralf schrieb:> wie ihr ein externes EEPROM (z.B. I2C oder> SPI) in euren Applikationen abbildetRalf 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.
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