Forum: Compiler & IDEs Ist es möglich, etwas wie EEMEM und PROGMEM für externe RAM?


von Maxim B. (max182)


Lesenswert?

Guten Abend,
ich habe folgende Frage:
Ich möchte gerne externe SPI-RAM etwa so wie EEPROM und FLASH benutzen: 
Variablen und Arrays definieren, Tabellen anlegen usw.
Gibt es in GCC dafür Mittel, um mit Hilfe von z.B. Attributen über 
Funkionen, die Zugang zu externem Speicher definieren, den Speicher 
"durchsichtig" zu verwenden?

Oder möchte ich zu viel?

Danke für Antworte im voraus.

von g457 (Gast)


Lesenswert?

> Gibt es in GCC dafür Mittel, um mit Hilfe von z.B. Attributen über
> Funkionen, die Zugang zu externem Speicher definieren, den Speicher
> "durchsichtig" zu verwenden?

Wenn die unbekannte Zielplattform adäquate Hardwareunterstützung dafür 
hat dann ja.

von Peter D. (peda)


Lesenswert?

Für den GCC ist mir nichts bekannt, aber für den Keil C51 gibt es 
generic Pointer. Man kann damit eigene Speicherbereiche definieren und 
der Compiler lenkt dann die Zugriffe auf Deine Lese- und Schreibroutinen 
um.

von Maxim B. (max182)


Lesenswert?

Danke für die Antwort!
Keil ist mir zu teuer, da ich kein Profi bin. Ich muß dann ohne dieser 
Option leben.

von (prx) A. K. (prx)


Lesenswert?

Mit C++ kannst du dem recht nahe kommen. Allerdings steckst du dann erst 
einmal einige Arbeit in das Framework, um hinterher etwas weniger Arbeit 
zu haben.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Maxim B. schrieb:
> Gibt es in GCC dafür Mittel, um mit Hilfe von z.B. Attributen über
> Funkionen, die Zugang zu externem Speicher definieren, den Speicher
> "durchsichtig" zu verwenden?

Nö.

> Oder möchte ich zu viel?

Ja.

Schreib dir ne Handvoll von IO- und Verwaltungsfunktionen.

von Maxim B. (max182)


Lesenswert?

A. K. schrieb:
> Mit C++ kannst du dem recht nahe kommen.

Es wäre schön für mich, zuerst C gut zu erlernen...

23LCV1024 habe ich auf der Platine, zusammen mit dem Speicherkondensator 
1,5F, der auch DS3234 bedient (wo auch 256 bytes RAM vorhanden. Gegen 
23LCV1024 eine Kleinigkeit, aber...). Nun überlege ich, wie ich das am 
sinnvollsten benutze. Erste Idee war, einen Puffer für SD-Card... Ich 
möchte noch FRAM ausprobieren, mit einem externen Modul. Schade, daß es 
nicht geht, diese Speicher so tief wie eigene RAM von ATMega zu 
integrieren.

: Bearbeitet durch User
von Chris S. (schris)


Lesenswert?

Geht relativ einfach.
Mach ein struct, und dann caste die ptr-diff von struct(Null) auf 
struct(Null).foo . dies ist dann dasselbe wie bei Eemem. Dies natürlich 
mittels Macro beim Zigriff, wie man es auch bei Eemem machen würde.

von leo (Gast)


Lesenswert?

Chris S. schrieb:
> Geht relativ einfach.
> Mach ein struct,

Blafasel. Du kennst ein externes SPI-RAM und die Ansteuerung?

leo

von (prx) A. K. (prx)


Lesenswert?

Wenn der interne Speicher zu klein ist, um alle kleinteiligen Daten zu 
speichern, dann wird es kompliziert. Wenn der externe Speicher aber nur 
für gepufferte Datenhaltung oder für grössere Datenmengen verwendet 
wird, dann ist ein solcher transparenter Layer ziemlich überflüssig.

Passen zumindest die kleinteiligen Daten ins interne RAM, dann ist es 
letztlich einfacher, diese Daten ein einer internen Struct zu halten und 
diese en bloc ins externe RAM zu spiegeln. Und grössere Mengen per 
Funktion zu adressieren, statt direkt als Arrayzugriff, ist oft 
akzeptabel. Wenn nicht, hat man vielleicht einen zu kleinen µC gewählt.

von Maxim B. (max182)


Lesenswert?

Chris S. schrieb:
> Geht relativ einfach.
> Mach ein struct, und dann caste die ptr-diff von struct(Null) auf
> struct(Null).foo . dies ist dann dasselbe wie bei Eemem.
Danke.
Könntest du bitte Beispiel schreiben?

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

A. K. schrieb:
> Und grössere Mengen per
> Funktion zu adressieren, statt direkt als Arrayzugriff

Das ist klar. Hier habe ich keine Frage.
Meine Interesse ist aber teilweise theoretischer Natur: eine Variable 
deklarieren wie in intern-RAM.

von Wilhelm M. (wimalopaan)


Lesenswert?

Maxim B. schrieb:
> A. K. schrieb:
>> Und grössere Mengen per
>> Funktion zu adressieren, statt direkt als Arrayzugriff
>
> Das ist klar. Hier habe ich keine Frage.
> Meine Interesse ist aber teilweise theoretischer Natur: eine Variable
> deklarieren wie in intern-RAM.

In C nein.
In C++ ja.

von Karl M. (Gast)


Lesenswert?

Wilhelm M. schrieb:
>> Das ist klar. Hier habe ich keine Frage.
>> Meine Interesse ist aber teilweise theoretischer Natur: eine Variable
>> deklarieren wie in intern-RAM.
>
> In C nein.
> In C++ ja.

Wie sieht das korrekt aus?

von Wilhelm M. (wimalopaan)


Lesenswert?

Karl M. schrieb:
> Wilhelm M. schrieb:
>>> Das ist klar. Hier habe ich keine Frage.
>>> Meine Interesse ist aber teilweise theoretischer Natur: eine Variable
>>> deklarieren wie in intern-RAM.
>>
>> In C nein.
>> In C++ ja.
>
> Wie sieht das korrekt aus?

Das kann ich Dir für ein konkretes externes RAM nicht sagen, denn 
erstens habe ich das noch nie benötigt, und zweitens habe ich kein 
SPI-Ram herumliegen.

Ich kann Dir nur sagen, wie die Schnittstelle aussehen könnte:
1
template<typename T, template Dev>
2
struct ExternalData {
3
     ExternalData(const T&);
4
     ExternalData(const ExternalData&) = delete;
5
6
     void operator=(const T&);
7
8
     // der Rest ad libitum
9
};

Dann könnte man es folgendermaßen verwenden:
1
using spiRam = SPI<...>;
2
3
ExternalData<uint16_t, spiRam> d{42};
4
5
d = 0815;

von Chris S. (schris)


Lesenswert?

1
EEPROM Example:
2
3
int EEMEM n = 37;
4
int n_in_RAM;
5
6
while(1) {
7
    n_in_RAM == eeprom_read_word(&n);
8
    n_in_RAM++;
9
    eeprom_write_word(&n, n_in_RAM);
10
    // and to see it:
11
    LCD_print_var(n_in_RAM);
12
}
13
14
#include <stddef.h>
15
#define RRMEM(x)  offsetof(struct ram_ext_s,x)
16
17
struct ram_ext_s {
18
  word id;
19
  unsigned int test;
20
  unsigned char foo;
21
  void* bar;
22
  long long foobar;
23
  int value;
24
};
25
26
if(ram_read_word(RRMEM(id))!=(word)UUID) { // init external RAM
27
        ram_write_dword(RRMEM(foobar),123456789UL);
28
        ram_write_word(RRMEM(bar),NULL);
29
        ram_write_word(RRMEM(test),0);
30
        ram_write_word(RRMEM(value),37);
31
        ram_write_word(RRMEM(id),UUID);
32
}
33
34
while(1) {
35
    n_in_RAM == (int)ram_read_word(RRMEM(value));
36
    n_in_RAM++;
37
    ram_write_word(RRMEM(value), n_in_RAM);
38
    // and to see it:
39
    LCD_print_var(n_in_RAM);
40
}

von Jim M. (turboj)


Lesenswert?

Maxim B. schrieb:
> Ich möchte gerne externe SPI-RAM etwa so wie EEPROM und FLASH benutzen:
> Variablen und Arrays definieren, Tabellen anlegen usw.

Auf welcher Plattform? Bei AVR: Vergiss es!

Bei ARM Cortex-M3 und höher gibt es etliche die QSPI Flash via Memory 
Controller in den Addressraum einblenden können - der wird dann wie 
"normaler" Flash angesteuert. Mann muss dann "nur" dem Compiler (und 
Linker) beibringen die Sachen auf die richtige Adresse zu packen.

Maxim B. schrieb:
> Keil ist mir zu teuer, da ich kein Profi bin. Ich muß dann ohne dieser
> Option leben.

Keil C51 ist umsonst, wenn man sich Silabs Simplicity Studio saugt. 
BTW:Produziert noch jemand vernünftige 8051ger in 2019?

Beim 8051 hätte man externen XRAM, der aber jede Menge Port Pins frisst.

von Maxim B. (max182)


Lesenswert?

Jim M. schrieb:
> Keil C51 ist umsonst

Ach 51... Für C51 brauch man kein C, nicht einmal Assembler ist 
unbedingt notwendig. Ich habe Anfang 90-er C51 in Maschinencode ohne 
Computer gemacht: so bequem für Menschen ist Befehlssatz. Byte für 
Operation, Byte oder zwei für Daten oder Adressen. Mit AVR kann das 
schon kaum mehr gehen: Bits von Befehl und Daten sind eigenartig 
durcheinander. Ohne Flasche Schnaps versteht man hier mit Code kaum 
etwas.

Jim M. schrieb:
> Beim 8051 hätte man externen XRAM
Das gibt es auch bei manchen AVR. Wie bei ATMega8515 (wenn man nur 512 
bytes RAM hat, dann ist externe RAM schon eine Notwendigkeit!), so auch 
bei ATmega128A und ATMega2560.

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Maxim B. schrieb:
> Für C51 brauch man kein C, nicht einmal Assembler ist
> unbedingt notwendig.

Du hast einen C-Compiler (C51) benutzt, ohne C oder Assembler?

Achtung: C51 ist ein C-Compiler, 8051 ist die CPU-Architektur.

Zur Ursprungsfrage:

Du kannst die Zugriffe in Makros verpacken, dann sieht dein SPIMEM im 
Code ungefähr so aus wie EEMEM oder PROGMEM. Praktisch wird das aber 
sehr unhandlich, weil die Makros dann mehrfachen Code erzeugen und so 
weiter.

Vermutlich wäre es sinnvoller, wenn du eine gemeinsame Schnittstelle für 
PROGMEM, EEMEM und SPIMEM definierst, also dir Funktionen schreibst. Die 
kannst du dann überall benutzen.

Oder du lebst damit, dass AVR-interne Dinge anders funktionieren als 
Chips, die du ranklebst.

von Sebastian S. (amateur)


Lesenswert?

Wie die vorherigen Beiträge zeigen ist es vor allem unter C++ recht 
einfach, aber...

Überlege Dir gut, welche Daten Du "auslagerst", da der Zugriff nur noch 
im Schneckentempo erfolgt.

Auch wenn es später im Code nicht direkt ersichtlich ist, erfolgt der 
Zugriff immer nur auf "Umwegen".

von g457 (Gast)


Lesenswert?

Leute, warum so kompliziert? Wenn Hardwareunterstützung da ist dann 
langen da ein Linkerattribut und eine passende Section im Linkerskript.

von S. R. (svenska)


Lesenswert?

g457 schrieb:
> Leute, warum so kompliziert? Wenn Hardwareunterstützung da
> ist dann langen da ein Linkerattribut und eine passende Section
> im Linkerskript.

Maxim redet immer von AVRs und da gibt es keine Hardwareunterstützung.

von g457 (Gast)


Lesenswert?

..und ein angepasstes Startupfile</Ingrid>

von Maxim B. (max182)


Lesenswert?

S. R. schrieb:
> Vermutlich wäre es sinnvoller, wenn du eine gemeinsame Schnittstelle für
> PROGMEM, EEMEM und SPIMEM definierst, also dir Funktionen schreibst. Die
> kannst du dann überall benutzen.

Das hilft leider wenig. So kann ich in jedem Fall machen, dafür brauche 
ich keine Frage zu stellen...
Ich möchte lediglich:
1
__Spiram int a = 0;
2
for(char i=0;i<10;i++){
3
   PORTA = (char)a++;
4
}

Sebastian S. schrieb:
> da der Zugriff nur noch
> im Schneckentempo erfolgt.

Immer noch schneller, besonders blockweise, als mit einem graphischen 
128x64 über MCP23S17 ganze Bildschirm Punkt zu Punkt zu invertieren...
Ich überlege nun: eine von vernünftigen Anwendungen wäre vielleicht eine 
Kopie von Bildschirm...

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Maxim B. schrieb:
>> Vermutlich wäre es sinnvoller, wenn du eine gemeinsame
>> Schnittstelle für PROGMEM, EEMEM und SPIMEM definierst,
>> also dir Funktionen schreibst. Die kannst du dann
>> überall benutzen.
>
> Das hilft leider wenig. So kann ich in jedem Fall
> machen, dafür brauche ich keine Frage zu stellen...

Du darfst die Funktionsaufrufe auch in Makros verpacken, wenn dir das 
lieber ist. Mein Vorschlag bezog sich nur auf etwas, was im Rahmen 
deiner Möglichkeiten liegt.

Denn sowas:

> Ich möchte lediglich:__Spiram int a = 0;
> for(char i=0;i<10;i++){
>    PORTA = (char)a++;
> }

ist nicht möglich. Du kannst mit C++ recht nah da rankommen, aber ohne 
eine gemeinsame Abstraktion (die genannten Funktionen) unterscheidet 
sich dein SPIRAM trotzdem von allen anderen Speicherformen. Und es wären 
trotzdem keine normalen Variablen.

Soweit ich dich einschätze, sollten die Funktionen und Abstraktionen 
kein Problem für dich sein. Das drum herum... schon.

von Raoul D. (raoul_d219)


Lesenswert?

S. R. schrieb:
> Maxim B. schrieb:

> Denn sowas:
>
>> Ich möchte lediglich:__Spiram int a = 0;
>> for(char i=0;i<10;i++){
>>    PORTA = (char)a++;
>> }
>
> ist nicht möglich.

Ich sehe da gar kein Problem, das so in C++ zu abstrahieren.

Und ich finde, das der TO hier absolut genau die richtige Frage stellt: 
er möchte einen DT, der einen Zugriff auf SPI-Ram kapselt. Und 
andererseits einen Typumwandlungs-ctor, einen Kopierzuweisung-Op, einen 
Post-Inkrement-Op, ... zur Verfügung stellt.

Zwar scheint der TO in C++ noch(!) nicht die nötigen Fähigkeiten zu 
haben, doch nachdem er RAII kennengelernt hat, sollte er dazu in der 
Lage zu sein, einen ersten Versuch zu starten. Jedenfalls denkt er wohl 
abstrakt genug ...

von S. R. (svenska)


Lesenswert?

Raoul D. schrieb:
> Ich sehe da gar kein Problem, das so in C++ zu abstrahieren.

>>> __Spiram int a = 0;

Das geht nicht so einfach, zumindest wüsste ich nicht, wie man das mit 
der Syntax hinbekommen soll. Etwas in der Art "SPIRAM<int> a = 0;" 
ginge.

Also ja, grundsätzlich kann man sowas in C++ machen und das 
funktioniert auch sehr gut. Die Frage bezog sich aber auf C und da geht 
das nicht.

Außerdem hat prx den wesentlichen Punkt bereits genannt: Man muss 
erstmal sehr viel Arbeit investieren, bis es in C++ effizient läuft 
und das tut, was man will. Insbesondere als Neuling.

von Wilhelm M. (wimalopaan)


Lesenswert?

S. R. schrieb:
> Raoul D. schrieb:
>> Ich sehe da gar kein Problem, das so in C++ zu abstrahieren.
>
>>>> __Spiram int a = 0;
>
> Das geht nicht so einfach, zumindest wüsste ich nicht, wie man das mit
> der Syntax hinbekommen soll. Etwas in der Art "SPIRAM<int> a = 0;"
> ginge.

Das hatte ich doch alles schon oben dargestellt:

Beitrag "Re: Ist es möglich, etwas wie EEMEM und PROGMEM für externe RAM?"

> Außerdem hat prx den wesentlichen Punkt bereits genannt: Man muss
> erstmal sehr viel Arbeit investieren, bis es in C++ effizient läuft

Effizienz ist weniger das Problem

> und das tut, was man will. Insbesondere als Neuling.

Das schon eher ;-) Aber es wäre m.E. ein lohnendes Ziel.

von S. R. (svenska)


Lesenswert?

Wilhelm M. schrieb:
> Das hatte ich doch alles schon oben dargestellt:

Mit der vorgegebenen Syntax geht es also nicht.
Gut, wäre das geklärt.

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.