Aray im Flash mit Adressen von Variablen

Hi,

bin mal wieder am Verzweifeln. :smiling_imp:
Ich möchte meine EEPROM-Routinen zum lesen und schreiben von verstellbaren Variablen überarbeiten da diese viel Platz benötigen.
Bisher war das ungefähr so:

  Mode=EEPROMRead();
  State=EEPROMRead();
  Effect=EEPROMRead();
  strip.updateLength(EEPROMReadWord());
  strip.PixelLeft=EEPROMReadWord();
  ....

Und das dann insgesamt 40 mal, ebenfalls so in WriteData.

Jetzt dachte ich mir ich könnte die Adressen aller zu speichernden Variablen in eine Tabelle im Flash packen

uint8_t const PROGMEM &Vars[] = {
  Mode,
  State,
  Effect,
  strip.numLEDs+1,
  strip.numLEDs,
  strip.PixelLeft+1,
  strip.PixelLeft,
  ...
  };

um das ganze in Read- und WriteData dann so abzuarbeiten:

  for(uintB n=0;n<38;n++){
    uint8_t* ptr=pgm_read_word(Vars+n);
    *ptr=EEPROMRead();
  }

Habe schon viele Varianten mit Pointer setzen durch bzw. der Groschen in Sachen Pointer in C++ ist noch nicht wirklich gefallen.
Mault der Compiler mal nicht sind die Daten falsch eingelesen, editier ich das um kommen Convertierungsfehler bzw. ein exit status declaration of 'Vars' as array of references.
Was mach ich falsch bzw. geht das überhaupt mit der Arduino-IDE?

MfG
Andi

pgm_read_...() liefert keinen Zeiger! Sondern Integer. Wenn das einen Zeiger darstellen soll kann man Casten

Ganz allgemein mit Bytes als Daten würde es so gehen:

const byte flashData[] PROGMEM = { 1, 2, 3, 1, 2, 3, 4, 5, 10, 20, 30, 40, 50 };

void setup()
{
  Serial.begin(9600);

  for (byte i = 0; i < sizeof(flashData); i++)
  {
    byte b = pgm_read_byte(flashData + i);
    Serial.println(b);
  }
}

void loop()
{
}

Wenn du das dann als Zeiger interpretieren willst, kannst du sowas machen:

byte* ptr = (byte*)pgm_read_word(flashData + i);

Hier ist dann pgm_read_word() nötig weil Zeiger 2 Byte haben

Das ist Unsinn:

uint8_t const PROGMEM &Vars[]

Es gibt keine Arrays aus Referenzen. Was dir der Compiler auch klar sagt! Wenn du ein Array aus Zeigern willst, dann lege auch ein Array aus Zeigern an:

byte* const flashData[] PROGMEM = { ... };

Darin kannst du dann mit dem "Address of"-Operator & Adressen abspeichern. Und die dann wie gesagt nach dem Auslesen auf Zeiger casten

Insgesamt dann so ähnlich:

byte value1 = 110;
byte value2 = 210;

byte* const flashData[] PROGMEM = { &value1, &value2 };
const int NUM_OF_ELEMENTS = sizeof(flashData) / sizeof(flashData[0]);

void setup()
{
  Serial.begin(9600);

  for (byte i = 0; i < NUM_OF_ELEMENTS; i++)
  {
    byte* b = (byte*)pgm_read_word(flashData + i);
    Serial.println(*b);
  }
}

void loop()
{
}
#include <EEPROM.h>
byte Mode;
byte State;
byte Effect;
struct Strip {
  int numLEDs;
  int PixelLeft;
} strip;

byte * const Vars[] PROGMEM = {
  &Mode,
  &State,
  &Effect,
  ((byte*)&strip.numLEDs),
  ((byte*)&strip.numLEDs) + 1,
  ((byte*)&strip.PixelLeft),
  ((byte*)&strip.PixelLeft) + 1,
};

void setup() {
  for (byte n = 0; n < sizeof(Vars) / sizeof(Vars[0]); n++) {
    byte* ptr = (byte*)pgm_read_word(Vars + n);
    *ptr = EEPROM.read(n);
  }
}
void loop() {}

Haribo68:
Was mach ich falsch bzw. geht das überhaupt mit der Arduino-IDE?

Flash-Speicher und EEPROM haben rein GAR NICHTS miteinander zu tun.
Außer dass beide Speicherarten in Atmega-Mikrocontrollern eingebaut sind.

Der Programmspeicher (Flash) wird nur beim Brennen eines Programms neu geschrieben und ist zur Laufzeit des Programms unveränderlich. Der Flash-Speicher kann also allenfalls zum Ablegen von KONSTANTEN verwendet werden, die sich zur Laufzeit eines Programms nicht ändern.

Der EEPROM-Speicher kann dagegen zur Laufzeit des Programms nicht nur gelesen werden, sondern (im Rahmen der begrenzten Schreibzyklen) auch ,mit neuem Inhalt beschrieben werden.

Im Endeffekt also: Alles, was zum Zeitpunkt des Kompilierens als Konstanten feststeht und sich zur Laufzeit nicht ändert, kannst Du mit dem Programm zusammen ins Flash hochladen und zur Laufzeit auslesen.
Egal ob es sich um einfache Konstanten, Arrays mit Konstanten, oder struct-Arrays handelt.
Da gibt es ÜBERHAUPT KEINE PROBLEME, solange Du nicht insgesamt mehr als 64KB Daten im Flash unterbringen möchtest. Und selbst dann gäbe es Möglichkeiten.

Ich erinnere mich an jemanden, der wollte hier schon vor Jahren Bilder in der Größe 57*1024 Pixel in 24-Bit RGB im PROGMEM ablegen, und auf seinem Fahrrad-Gepäckträger einen Streifen mit 57 Stück WS2812 LEDs montieren, und dann beim Durch-die-Gegend-Fahren Dinosaurierbilder als Persistence of VisionBilder mit einer Auflösung von 57x1024 Pixel in die Umgebung projizieren. Wohl hauptsächlich für seine Foto-Langzeitaufnahmen. Auch das ging im Endeffekt, mit einem Atmega2560 und reichlich Tüfelei von meiner Seite aus. Aber das einzige Problem dabei war das Überwinden der 64KB Speichergrenze, die es zumindest in den IDE-Versionen von 1.0.0 bis 1.0.5 gab. Keine Ahnung, ob man heute auf Controllern mit ausreichend Flash-Speicher problemlos mehr als 64KB Daten im PROGMEM Flash unterbringt, das habe ich seit damals nicht wieder probiert.

jurs:
Aber das einzige Problem dabei war das Überwinden der 64KB Speichergrenze, die es zumindest in den IDE-Versionen von 1.0.0 bis 1.0.5 gab. Keine Ahnung, ob man heute auf Controllern mit ausreichend Flash-Speicher problemlos mehr als 64KB Daten im PROGMEM Flash unterbringt, das habe ich seit damals nicht wieder probiert.

Es gibt keinen direkten Weg zur Übersetzungszeit lange Adresskonstanten zu erzeugen,
das macht Tabellen mit Adressen in PROGMEM nahezu unmöglich.

Der Effekt wird auch das F()-Macro aushebeln, wenn seine Konstanten oberhalb 64k landen.

Davon abgesehen kann man genauso (un-)bequem auf den Flash zugreifen wie auf kleineren Arduinos,
es gibt zu den _near Varianten auch _far Varianten der PROGMEM Zugriffsroutinen
und eine Funktion die eine lange Addresse ermittelt (aber wie gesagt, nur zur Laufzeit).

Das ist mal wieder typisch Whandal: Zeigt ohne Worte und man ist baff ^^
Vielan Dank, nun klappt es.
Im Array muss man die Zeiger (&) zu Variablen mit 16 Bit mit (Byte*) konvertieren.
Die Änderung über eine Tabelle der Variablen-Adressen erspart mir wieder ca. 440 Bytes.

MfG
Andi

Im Array muss man die Zeiger (&) zu Variablen mit 16 Bit mit (Byte*) konvertieren.

& hat mehrere Bedeutungen die du verwechselst. In Zusammenhang mit Zeigern musst du das als "Adresse von" lesen. Du hast ein Array aus Zeigern und das wird mit den Adressen der Variablen initialisiert.
Es wird aber auch für Referenzen verwendet. Die sind ein wenig mit Zeigern verwandt aber du kannst keine Arrays aus Referenzen haben.

Hier wird die Adresse einer Variablen (welche alles mögliche sein könnte) auf einen Zeiger auf Byte zurechtgebogen:

((byte*)&strip.numLEDs),

Da ein int* (Zeiger auf int) ein anderer Datentyp als byte* (Zeiger auf byte) ist. Aber an der Stelle ist egal was dahinter genau steht. Nur die Adresse selbst ist von Interesse

Das kannst du lesen als "Die Adresse von strip.numLEDs wird auf einen Zeiger auf byte gecastet"

Das ist mir auch klar was Zeiger sind. In C++ ist mir das leider noch nicht so klar wie Zeiger definiert werden und wie man diese handhabt.
Jede (globale) Variable ist in einer Speicherzelle und vielleicht jede lokale wenn diese nicht in CPU-Registern temporär sind. Wird ge-pusht dann sind sie sowieso im Stack des RAMs.
In ASM war es früher so einfach das Z-Register auf die Adresse einer Marke zu setzen und davon zu lesen (LD Rn,Z+ etc.) und in C++ ist es, als Anfänger, schon ziemlich schwierig einen Zeiger zu definieren.
Da fragt man sich manchmal wirklich was die da früher ger..... haben ^^.
Aber danke vielmals, werde mir das mit den Pointern noch mal durchgehen.

MfG
Andi

Wenn Du Erfahrung mit ASM hast, dann dürften doch Pointer für Dich überhaupt kein Problem sein. Wie Du schon schreibst, das ist das was ins Adressregister geschrieben wrd und dann kann man lesen.
Das ist auch hier nicht anders, es wird nur einiges vor Dir versteckt, was Du in ASM selbst machen musst, und es wird anders geschrieben. Also einfach auf die alten Kenntnisse bauen. Die haben immer noch Gültigkeit fürs Verständnis.

Gruß Tommy

auf die alten Kenntnisse bauen. Die haben immer noch Gültigkeit fürs Verständnis.

Die wenigsten Prozessoren und damit deren Assembler kennen übrigens ein Z-Register :wink:
Das ist der Vorteil von C: man ist hardware-unabhängig. Das war jedenfalls der Plan 8)
Dafür muss man sich mit * , & und -> rumschlagen.

Pointer (Zeiger) sind übrigens in C unc C++ das gleiche.

Bei C++ sind zusätzlich noch Referenzen dazugekommen. Damit kann man nicht ganz soviel Unsinn machen. Und sie sehen aus wie Variable, sind aber "eigentlich" nur deren Referenzen, also doch sowas wie Zeiger. Ob das nun gut ist oder schlecht. Werden hier bei Arduino Libraries wenig verwendet, kann man also meist ignorieren.