PROGMEM/Flash mit Arrays rechnen

Hi alle zusammen,
bin momentan am verzweifeln! ich versuche da ich Speicher Probleme mit dem RAM meines Arduino Mega2560 habe einige fixe aber relativ Große Arrays auszulagern im Flash.
Wenn ich diese dann wieder einlese muss ich mit den Array Werten eine Berechnung durchführen.

um zu testen das meine Berechnung im normalenfall funktioniert hab ich hier erstmal ein beispiel Sketch ohne PROGMEM und nur den ersten ARRAY wert.

(habe die Arrays stark verkürzt für den Post im Forum)

#include <pgmspace.h>
int i;
unsigned short Vij[512]  ={33397,33530,33370,3361551,33359,33657,33343,};

unsigned short PTAT=2991.35815;
const  uint32_t   ThOffset[512]PROGMEM ={33855,34109,33949,34153,33583,33861,33723,33671,33747,33727};
const signed short  ThGrad[512] PROGMEM ={-14181,-19831,-19266,-17571,-14181,-9096,-13051,-8531,-5707};
signed short  Vij_comp[512];





  void Thermal_Offset_Vij_comp_calc() {
  
  long  b = pow(2, 17);
 

 
    
    Vij_comp[0] = (unsigned short)Vij[0] - (((int32_t)ThGrad[0] * PTAT) / b) - (unsigned short)ThOffset[0];

  

  

 
     Serial.println(Vij[0],DEC);

     Serial.println(ThGrad[0],DEC);

     Serial.println(PTAT,DEC);
 
     Serial.println(ThOffset[0],DEC);

     Serial.println(b,DEC);
 
     Serial.println(Vij_comp[0],DEC);


}
void setup() 

{
  Serial.begin(250000);

  
  Thermal_Offset_Vij_comp_calc();
}

void loop() {
  

}

wie erwartet bekomme ich -134 heraus was ich mit dem Taschenrechner überprüft habe.
Jetzt wirds kniffelig:
Ich weis das pgm_read_word() ja ein word anzeigt sprich meine Zahl soweit ich das verstehe als ASCII code ausspuckt.
Das bringt mich aber nicht weiter weil ich ja den Zahlenwert brauche um mit den Arrays meine Berechnung zu machen.

Hier der Code mit Progmem:

#include <pgmspace.h>

int i;
unsigned short Vij[512]  ={33397,33530,33370,3361551,33359,33657,33343,};

unsigned short PTAT=2991.35815;
const  uint32_t   ThOffset[512]PROGMEM ={33855,34109,33949,34153,33583,33861,33723,33671,33747,33727};
const signed short  ThGrad[512] PROGMEM ={-14181,-19831,-19266,-17571,-14181,-9096,-13051,-8531,-5707};
signed short  Vij_comp[512];





  void Thermal_Offset_Vij_comp_calc() {
  
  long  b = pow(2, 17);
 
  

    
    Vij_comp[0] = (unsigned short)Vij[0] - (((int32_t)pgm_read_byte(ThGrad) * PTAT) / b) - (unsigned short)pgm_read_byte(ThOffset);

 

  

 
     Serial.println(Vij[0],DEC);

     Serial.println(pgm_read_byte(ThGrad),DEC);

     Serial.println(PTAT,DEC);
 
     Serial.println(pgm_read_byte(ThOffset),DEC);

     Serial.println(b,DEC);
 
     Serial.println(Vij_comp[0],DEC);


}
void setup() 

{
  Serial.begin(250000);

  
  Thermal_Offset_Vij_comp_calc();
}

void loop() {
  

}

http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#ga75acaba9e781937468d0911423bc0c35

Ich weis das es auch diesen Macro gibt pgm_read_byte() jedoch kommt dann bei mir komplett andere Zahlen Heraus.

Ich schätze es liegt daran das der PROGMEM Befehl meine Array Werte alle als strings interpretiert.

Jetzt die Frage kann mir irgendjemand dabei Weiterhelfen wie ich es schaffe die im Flash abgelegten Array Werte nicht als Zeichen auszulesen sondern die tatsächlichen Werte.

Vielen Dank im vorraus

Gruß Beni

Ich weis das pgm_read_word() ja ein word anzeigt sprich meine Zahl soweit ich das verstehe als ASCII code ausspuckt.

Was soll das mit ASCII zu tun haben? Diese Makros liefern dir genau den Wert der im Speicher steht.

Ich weis das es auch diesen Macro gibt pgm_read_byte() jedoch kommt dann bei mir komplett andere Zahlen Heraus.

Byte liefert dir ein Byte. Word ist ein 16-Bit Integer. Für 32-Bit Werte gibt es pgm_read_dword() (double word). Du musst das Makro verwenden dass zu deinem Datentyp passt!

short ist übrigens auf den AVRs das gleiche wie int. Da gibt es erst bei ARMs einen Unterschied.

Sowas ist Unsinn:

(int32_t)pgm_read_byte

Wenn du ein Byte einliest wird daraus nicht durch Magie ein long. Du kannst z.B. mit pgm_read_dword() 32 Bit auslesen und auf float casten. Oder mit pgm_read_word() 16 Bit und das auf einen Zeiger casten. Aber das geht nur weil die Bitlänge stimmt!

"Wort" als Einheit ist heute etwas veraltetet und nicht mehr so wichtig wie z.B. in den 60ern und 70ern als es da gewaltige Unterschiede gab, aber hier als Erklärung: https://de.wikipedia.org/wiki/Datenwort

Deine Datentypen und die Initiierungswerte passen nicht immer zusammen: unsigned short Vij[512] ={33397,33530,33370,[u]3361551[/u],33359,33657,33343,}; unsigned short PTAT=[u]2991.35815[/u];

Deine Leseaufrufe müssen auch zur Länge der Datentypen passen:

     Serial.println(Vij[0],DEC);

     Serial.println( (int)pgm_read_word(ThGrad),DEC);
     Serial.println(PTAT,DEC);
     Serial.println(pgm_read_dword(ThOffset),DEC);
     Serial.println(b,DEC);
     Serial.println(Vij_comp[0],DEC);

Beim Mega2560 musst Du auch aufpassen, wo deine Werte im Speicher stehen. pgm_read_word und pgm_read_dword nutzen nur 16-Bit-Adressen. Damit kannst Du nur 64k ansprechen. Gegebenenfalls solltest Du auf die Varianten mit 32-Bit Adressierung umstellen ( pgm_read_word_far )

MicroBahner: Beim Mega2560 musst Du auch aufpassen, wo deine Werte im Speicher stehen. pgm_read_word und pgm_read_dword nutzen nur 16-Bit-Adressen. Damit kannst Du nur 64k ansprechen. Gegebenenfalls solltest Du auf die Varianten mit 32-Bit Adressierung umstellen ( pgm_read_word_far )

Davon ist er glaube ich noch weit entfernt. Außerdem ist das nicht so einfach wie nur das Auslese-Makro umzustellen. Man braucht noch ein weiteres (nicht dokumentiertes) Makro um die Adresse in eine far-Adresse umzurechnen

Serenifly: Man braucht noch ein weiteres (nicht dokumentiertes) Makro um die Adresse in eine far-Adresse umzurechnen

Das ist (inzwischen ?) dokumentiert: pgm_get_far_address

Ah. Da war nicht immer der Fall :) Das war mal nicht nur nicht dokumentiert, aber man musste es man sich sogar selbst schreiben

Ist aber wie gesagt off topic. Das Haupt-Problem ist hier dass die verwendenden Makros nicht zu den Datentypen passen. Und die Werte müssen eben auch zu den Datentypen passen. Einen float Wert in eine Integer Konstante führt zu nichts sinnvollem

schon mal vielen dank für die antworten mir ist klar das casten war dumm hab jetzt mal die wesentlichen Punkte verbessert .
@MicroBahner das der eine Wert so groß war liegt nur an nem Tippfehler ^^.

Trotz allem komme ich nicht ganz klar irgendwie fehlt mir das verständnis.

Hier nochmal mein Code ohne PROGMEM der den gewollten Wert auch liefert:

#include <pgmspace.h>

int i;
const uint16_t Vij[512] ={33397,33530,33370,33615};

const uint16_t PTAT=2991;
const  uint16_t   ThOffset[512] ={33855,34109,33949,34153};
const  int16_t    ThGrad[512]   ={-14181,-19831,-19266,-17571};
int16_t   Vij_comp[512];





  void Thermal_Offset_Vij_comp_calc() {
  
  long  b = pow(2, 17);
 
  

 for(i=0; i < 4; i++){ 
    
    Vij_comp[i] = Vij[i] - (((int32_t)ThGrad[i] * PTAT) / b) - ThOffset[i];

  
 
  }

 
     Serial.println(Vij[0],DEC);

     Serial.println(ThGrad[0],DEC);

     Serial.println(PTAT,DEC);
 
     Serial.println(ThOffset[0],DEC);

     Serial.println(b,DEC);
 
     Serial.println(Vij_comp[0],DEC);



  }
void setup() 

{
  Serial.begin(250000);

  
  Thermal_Offset_Vij_comp_calc();
}

void loop() {
  

}

das die Rechnung funktioniert liegt anscheinend an dem typcast (int32_t)ThGrad nur so bekomme ich die mathematisch richtigen werte heraus.
Warum genau kann ich nur vermuten vllt liegt es an der nachfolgenden Multiplikation die den int16_t Bereich sprengt nur wie übertrage ich dies funktionierend auf das PROGMEM Beispiel
???
ohne den Cast bekomme ich -458 statt den Korrekten -135.
```
*#include <pgmspace.h>

int i;
const uint16_t Vij[512] PROGMEM ={33397,33530,33370,33615};
const uint16_t PTAT=2991;
const  uint16_t  ThOffset[512]PROGMEM ={33855,34109,33949,34153};
const  int16_t    ThGrad[512]  PROGMEM ={-14181,-19831,-19266,-17571};
signed long  Vij_comp[512];

void Thermal_Offset_Vij_comp_calc() {
 
  long  b = pow(2, 17);

for(i=0; i < 4; i++){
   
    Vij_comp[i] = pgm_read_word(Vij+i) - ((pgm_read_word(ThGrad+i) * PTAT) / b) - pgm_read_word(ThOffset+i);

}

Serial.println(pgm_read_word(Vij),DEC);

Serial.println((int)pgm_read_word(ThGrad),DEC);

Serial.println(PTAT,DEC);

Serial.println(pgm_read_word(ThOffset),DEC);

Serial.println(b,DEC);

Serial.println(Vij_comp[0],DEC);

}
void setup()

{
  Serial.begin(250000);

Thermal_Offset_Vij_comp_calc();
}

void loop() {

}*
```
Jetzt die Frage wie gehe ich vor kann jemand die Rechnung vllt mal ausprobieren?
hab jetzt schon ziemlich viel ausprobiert auch pgm_read_dword kam aber nicht einmal auf das Ergebnis vom oberen Code ohne PROGMEM.
Vielen Dank für jede Unterstützung!!!
Gruß Beni

Nur das ihr mich richtig versteht ausgeben mit Serial funktioniert einwandfrei daher dachte ich mal grundsätzlich nicht an ein Adressierungsfehler.

ich bin der Meinung es steht und fällt mit dem ((int32_t)ThGrad * PTAT) da ich ohne den cast auf 32 bit an dieser stelle auch die falschen - 458 heraus bekomme.

   Vij_comp[i] = pgm_read_word(Vij+i) - (((uint32_t)pgm_read_word(ThGrad+i) * PTAT) / b) - pgm_read_word(ThOffset+i);

@Whandall Danke für die Rückmeldung aber mit der Rechenzeile komme ich auf für Vij_comp auf -1629 kommt bei dir wirklich dann -135 heraus für den ersten Wert von Vij_comp ???

Woher soll ich das wissen?

Ich gehe aber davon aus, ich habe ja nur den Overflow verhindert.

ja des hatte ich bereits ja auch schon so probiert aber es kommt halt einfach das falsche Ergebniss raus das ist ja mein Gordischer Knoten den ich habe :'( . Hab es oben im Quelltext auch schon mit int32_t versucht aber als resultat für den ersten Array wert kommt nicht die richtige Lösung heraus !(-135)

Das könnte daran liegen, dass -135 nicht die richtige Lösung ist.

const uint16_t Vij[512] PROGMEM = { 33397, 33530, 33370, 33615, };
const uint16_t PTAT = 2991;
const uint16_t ThOffset[512]PROGMEM = { 33855, 34109, 33949, 34153, };
const int16_t  ThGrad[512]  PROGMEM = { -14181, -19831, -19266, -17571, };
signed long   Vij_comp[10];


void Thermal_Offset_Vij_comp_calc() {
  long  b = 1L << 17;
  for (byte i = 0; i < 4; i++) {
    Vij_comp[i] = pgm_read_word(Vij + i) - (((uint32_t)pgm_read_word(ThGrad + i) * PTAT) / b) - pgm_read_word(ThOffset + i);
    Serial.print(Vij_comp[i]);
    Serial.print(F(" = "));
    Serial.print(pgm_read_word(Vij + i));
    Serial.print(F(" - ("));
    Serial.print(pgm_read_word(ThGrad + i));
    Serial.print(F(" * "));
    Serial.print(PTAT);
    Serial.print(F(") / "));
    Serial.print(b);
    Serial.print(F(" - "));
    Serial.print(pgm_read_word(ThOffset + i));
    Serial.println(F(""));
  }
}

void setup() {
  Serial.begin(250000);
  Thermal_Offset_Vij_comp_calc();
}

void loop() {}
-1629 = 33397 - (51355 * 2991) / 131072 - 33855
-1621 = 33530 - (45705 * 2991) / 131072 - 34109
-1634 = 33370 - (46270 * 2991) / 131072 - 33949
-1632 = 33615 - (47965 * 2991) / 131072 - 34153

Die Ergebnisse sind nicht gerundet sondern abgeschnitten, sonst aber in Ordnung.

Und keine Spur von -165.

Ich habe den leeren Array im RAM verkürzt, damit der Sketch sich auf dem Nano übersetzen ließ.

Vielleicht habe ich nicht explizit erwähnt das in der Rechnung mein ThGrad ein signed Value sein muss. sprich ich muss auch mit dem Vorzeichen Rechnen daher ist uint32_t zu casten falsch das Vorzeichen darf nicht wegfallen.

const uint16_t Vij[512] PROGMEM = { 33397, 33530, 33370, 33615, };
const uint16_t PTAT = 2991;
const uint16_t ThOffset[512]PROGMEM = { 33855, 34109, 33949, 34153, };
const int16_t  ThGrad[512]  PROGMEM = { -14181, -19831, -19266, -17571, };
signed long   Vij_comp[10];


void Thermal_Offset_Vij_comp_calc() {
  long  b = 1L << 17;
  for (byte i = 0; i < 4; i++) {
    Vij_comp[i] = pgm_read_word(Vij + i) - (((int32_t)((int)pgm_read_word(ThGrad + i)) * PTAT) / b) - pgm_read_word(ThOffset + i);
    Serial.print(Vij_comp[i]);
    Serial.print(F(" = "));
    Serial.print(pgm_read_word(Vij + i));
    Serial.print(F(" - ("));
    Serial.print((int)pgm_read_word(ThGrad + i));
    Serial.print(F(" * "));
    Serial.print(PTAT);
    Serial.print(F(") / "));
    Serial.print(b);
    Serial.print(F(" - "));
    Serial.print(pgm_read_word(ThOffset + i));
    Serial.println(F(""));
  }
}

void setup() {
  Serial.begin(250000);
  Thermal_Offset_Vij_comp_calc();
}

void loop() {}
-135 = 33397 - (-14181 * 2991) / 131072 - 33855
-127 = 33530 - (-19831 * 2991) / 131072 - 34109
-140 = 33370 - (-19266 * 2991) / 131072 - 33949
-138 = 33615 - (-17571 * 2991) / 131072 - 34153

Hi whandall Weiß nicht ob mein Post von gerade eben ankam da ich unterwegs bin. Kannst du mir erklären wie man anhand meines Codes jetzt auf die -135 kommt und wie man die macros Casten muss ? Was hat der Code den du als Letztes geschickt hast damit genau zu tun ? Ich hänge an dem Problem schon lange und hoffe du kannst mir weiter helfen vielen Dank

Sorry, da habe ich versehentlich den falschen Sketch genommen.

   Vij_comp[i] = pgm_read_word(Vij + i) - (((int32_t)((int)pgm_read_word(ThGrad + i)) * PTAT) / b) - pgm_read_word(ThOffset + i);
const uint16_t Vij[512] PROGMEM = { 33397, 33530, 33370, 33615, };
const uint16_t PTAT = 2991;
const uint16_t ThOffset[512]PROGMEM = { 33855, 34109, 33949, 34153, };
const int16_t  ThGrad[512]  PROGMEM = { -14181, -19831, -19266, -17571, };
signed long   Vij_comp[10];


void Thermal_Offset_Vij_comp_calc() {
  long  b = 1L << 17;
  for (byte i = 0; i < 4; i++) {
    Vij_comp[i] = pgm_read_word(Vij + i) - (((int32_t)((int)pgm_read_word(ThGrad + i)) * PTAT) / b) - pgm_read_word(ThOffset + i);
    Serial.print(Vij_comp[i]);
    Serial.print(F(" = "));
    Serial.print(pgm_read_word(Vij + i));
    Serial.print(F(" - ("));
    Serial.print((int)pgm_read_word(ThGrad + i));
    Serial.print(F(" * "));
    Serial.print(PTAT);
    Serial.print(F(") / "));
    Serial.print(b);
    Serial.print(F(" - "));
    Serial.print(pgm_read_word(ThOffset + i));
    Serial.println(F(""));
  }
}

void setup() {
  Serial.begin(250000);
  Thermal_Offset_Vij_comp_calc();
}

void loop() {}

Vielen Dank Whandall, du hast mir echt weiter geholfen!!! Kannst du mir vllt noch erklären warum das doppelt gecastet wird und warum einfaches int32_t nicht langt ???

Gruß beni

Weil pgm_read_word() einen unsigned int liefert, der bei nur einem Cast falsch expandiert würde.

Vielen Dank klappt alles einwandfrei eine letzte Frage hätte ich interesse halber.

Es geht darum warum Funktioniert Serial.println auch ohne pgm_read_word

const uint16_t Vij[512] PROGMEM = { 33397, 33530, 33370, 33615, };
const uint16_t PTAT = 2991;
const uint16_t ThOffset[512]PROGMEM = { 33855, 34109, 33949, 34153, };
const int16_t  ThGrad[512]  PROGMEM = { -14181, -19831, -19266, -17571, };
signed long   Vij_comp[10];
unsigned long a;

void Thermal_Offset_Vij_comp_calc() {
  Serial.println(ThOffset[0],DEC);
  Serial.println(pgm_read_word(ThOffset),DEC);
  a=ThOffset[0]/2;
  Serial.println(a,DEC);
  }


void setup() {
  Serial.begin(250000);
  Thermal_Offset_Vij_comp_calc();
}

void loop() {}

sprich wie greift Serial.println(ThOffset[0],DEC); überhaupt auf die Daten zu und warum kann man auch mit ThOffset[0] dann rechnen siehe a.

macht es sich dann auch eine RAM kopie oder wie muss man das verstehen ?