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.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.
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 ...
> 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 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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.