PROGMEM mit diversen Arrays auslesen (Bilder-Ausgabe auf LED Matrix)

Hallo an Alle!

ich habe hier mal ein Problem und zwar habe ich einige Arrays in Progmem gespeichert aber weiß nicht so recht, an welcher Stelle ich genau den pgm_read Befehl ansetzen soll.

Auch welcher der pgm Befehle der richtige ist, hab ich auch so meine Probleme (da das Array ein uint_16 ist, gehe ich davon aus, dass ich den pgm_read_word_near Befehl nehme…?).

Ebenso ein kleines Rätsel ist auch die Verwendung von const und static, eigentlich reicht doch die Verwendung eines der Beiden, oder? Zumindest gabs keine Fehlermeldung wenn Beides drin steht…aber das ist nur ne Frage am Rande.

Hauptsache ich bekomm progmem ausgelesen und die Daten nacheinander auf der LED Matrix angezeigt, was er wahrscheinlich bisher nicht macht, da die Matrix schwarz bleibt. Die Arrays sind selbsterklärend fürs Forum etwas gekürzt. Pro Bild/Array sind es 2048 Pixel.

Mein Programm:

// ----------------------------
// Standard Libraries - Already Installed if you have ESP8266 set up
// ----------------------------

#include <Ticker.h>

#include <PxMatrix.h>
// The library for controlling the LED Matrix
// Needs to be manually downloaded and installed
// https://github.com/2dom/PxMatrix

Ticker display_ticker;

// Pins for LED MATRIX
#define P_LAT 16
#define P_A 5
#define P_B 4
#define P_C 15
#define P_OE 2
#define P_D 12
#define P_E 0

// PxMATRIX display(32,16,P_LAT, P_OE,P_A,P_B,P_C);
// PxMATRIX display(64,32,P_LAT, P_OE,P_A,P_B,P_C,P_D);
PxMATRIX display(64, 32, P_LAT, P_OE, P_A, P_B, P_C, P_D);

// This is a horribly inefficient way of drawing this, but meh.. :)
// Converted using the following site: http://www.rinkydinkelectronics.com/t_imageconverter565.php

const PROGMEM uint16_t bar1[] = {
0xF800, 0xF800, 0xF800, 0xF800, 0xF800, 0xF800, 0x0000, 0x0000, 0x0000, 0xF800, 0xF800, 0xF800, 0xF800, 0x0000, 0x0000, 0xF800,   // 0x0010 (16) pixels
0xF800, 0xF800, 0xF800, 0xF800, 0xF800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0020 (32) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0030 (48) pixels
0x0000, 0x6B0C, 0xFFDF, 0xAD34, 0x6B0C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0040 (64) pixels [...]

};

const PROGMEM uint16_t static bar2[] = {
0xDCC0, 0xDCC0, 0xDCC0, 0xDCC0, 0xDCC0, 0xDCC0, 0x0000, 0x0000, 0x0000, 0xDCC0, 0xDCC0, 0xDCC0, 0xDCC0, 0x0000, 0x0000, 0xDCC0,   // 0x0010 (16) pixels
0xDCC0, 0xDCC0, 0xDCC0, 0xDCC0, 0xDCC0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0020 (32) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0030 (48) pixels [...]

};

const PROGMEM uint16_t static bar3[] = {
0xF7E0, 0xF7E0, 0xF7E0, 0xF7E0, 0xF7E0, 0xF7E0, 0x0000, 0x0000, 0x0000, 0xF7E0, 0xF7E0, 0xF7E0, 0xF7E0, 0x0000, 0x0000, 0xF7E0,   // 0x0010 (16) pixels [...]
.
.
.


// ISR for display refresh
void display_updater() {
 display.display(70);
}

void setup() {
 display.begin(16);
 display.clearDisplay();
 
 display_ticker.attach(0.002, display_updater);
 yield(); 
}

void drawFrame( const uint16_t *frame) {
 display.clearDisplay();
 int imageHeight = 32;
 int imageWidth = 64;
 int counter = 0;
 for (int yy = 0; yy < imageHeight; yy++) {
   for (int xx = 0; xx < imageWidth; xx++) {
     display.drawPixel(xx, yy, frame[counter]);
     counter++;
   }
 }
 delay(100);
}

void loop() {
 drawFrame(bar1); delay(1000);
 drawFrame(bar2);
 drawFrame(bar3);

yield();

};

Kann mir da wer helfen?

schöne Grüße

Sicher ist der Strichpunkt am Ende nutzlos/falsch.
Grüße Uwe

@noiasca Stimmt, da war was ^^ dankeschön!

@Uwe nope, der Bildschirm bleibt immer noch schwarz...

wie gesagt, ich muss die arrays aus progmem noch irgendwie auslesen und d.h. den Befehl dazu in den Code unterbringen, dass wird der Grund dazu sein...

Grüße

bitte halte dich an PROGMEM - Arduino-Referenz
da du uint16_t hast, brauchst du pgm_read_word.

Mach mal einen kleinen Testsketch mit einer reinen Ausgabe auf Serial von deinem Array. So ein zerstückeltes Monster ist sinnlos. Lerne in kleinen Einheiten.

zur zweiten Frage: const char static brauchst du wenn

Der folgende Code wird funktionieren, wenn er in einer Funktion ausgeführt wird:
const static char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n"

bei dir sind die arrays in global scope, also brauchst du das static nicht angeben.

AberMakaber:
Ebenso ein kleines Rätsel ist auch die Verwendung von const und static, eigentlich reicht doch die Verwendung eines der Beiden, oder?

Das Schlüsselwort 'static' hat - je nachdem wo es eingesetzt wird - recht unterschiedliche Bedeutung.

Wird es so wie hier - im globalen Definitionsbereich - genutzt, bestimmt es die Sichtbarkeit der Variable. Ohne 'static' ist die Variable im gesamten zusammengebundenen Programm sichtbar. Also nicht nur im eigentlichen Sketch, sondern auch in allen Programmteilen, die später vom Linker noch dazugebunden werden ( Das sich dort in der Regel keiner dafür interessiert, ist eine andere Sache :wink: ).
Mit 'static' ist die Variable wirklich nur in der Übersetzungseinheit des eigentlichen Sketches sichtbar. In den allermeisten Fällen macht es keinen ( erkennbaren ) Unterschied.
Wird es innerhalb einer Funktion bei der Definition einer lokalen Variable eingesetzt, bedeutet es, dass der Variablenwert zwischen mehrfachen Aufrufen der Funktion erhalten bleibt.

'const' hat eine ganz andere Bedeutung und legt fest, dass der Wert dieser Variable nicht geändert werden darf/kann. Was dann natürlich auch bedeutet, dass der Wert bei der Defintion gesetzt werden muss.

@MicroBahner ah, alles klar, dankeschön für die super Erklärung!

@noiasca Werd ich mich gleich mal ransetzen, danke aber erstmal für die Erklärung.

Ich sollte dazu sagen, ohne progmem funktioniert das Ganze tadellos, ich weiß was auf dem Bildschirm/der LED Matrix angezeigt wird, so isses nicht. Der Grund für progmem ist einzig und Allein, um Speicher frei zu schaufeln, nur mal so nebenbei.

Wenn das alles funktioniert zeig ich euch mal zur Belohnung ein kleines Video von dem Projekt :wink:

Habe den zweiten Testsketch von Progmem genutzt mit 3 verkürzten Arrays von meinem “Monstern”, jeweils bestückt mit 16 Daten anstelle von 2048 (!)…

Allerdings spuckt er mir einige Fehler aus.

Fand das Beispiel besser, eine “Tabelle” anzulegen mit meinen 3 Arrays besser. Allerdings habe ich hier keine strings vorzuliegen und er spuckt mir einige Conversions-Fehler aus, weiß da nicht wie weiter.
Ich verstehe auch das Problem, nur weiß ich nicht, wie man es lösen kann. Die Definiton von “Pointer Access Operators” hilft mir nicht weiter.

const PROGMEM uint16_t bar1[] = {
0xF800, 0xF800, 0xF800, 0xF800, 0xF800, 0xF800, 0x0000, 0x0000, 0x0000, 0xF800, 0xF800, 0xF800, 0xF800, 0x0000, 0x0000, 0xF800,   // 0x0010 (16) pixels
};

const PROGMEM uint16_t bar2[] = {
0xDCC0, 0xDCC0, 0xDCC0, 0xDCC0, 0xDCC0, 0xDCC0, 0x0000, 0x0000, 0x0000, 0xDCC0, 0xDCC0, 0xDCC0, 0xDCC0, 0x0000, 0x0000, 0xDCC0,   // 0x0010 (16) pixels
};

const PROGMEM uint16_t bar3[] = {
0xF7E0, 0xF7E0, 0xF7E0, 0xF7E0, 0xF7E0, 0xF7E0, 0x0000, 0x0000, 0x0000, 0xF7E0, 0xF7E0, 0xF7E0, 0xF7E0, 0x0000, 0x0000, 0xF7E0,   // 0x0010 (16) pixels
};

const unsigned int* const int_table[] PROGMEM = { bar1, bar2, bar3 };

unsigned int displayInt[]; // Rückgabewert der Funktion zum Auslesen der Daten

void setup() {
  Serial.begin(115200); // Initialisiere den seriellen Port
  while (!Serial);  // Warte, bis der serielle Port verbunden ist
  Serial.println("OK");

 
}

void loop() {
  // ...
 // Setup code:
  // Lies einen 2-Byte-Int
  for (int k = 0; k < 3; k++) {
    strcpy_P(displayInt, (char*)pgm_read_word(&(int_table[k])));
    
    Serial.println(displayInt); // Gib den gelesenen Wert aus
  }
delay(500);
}

Fehlermeldung:

Arduino: 1.8.13 (Windows 10), Board: “LOLIN(WEMOS) D1 R2 & mini, 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), 4MB (FS:2MB OTA:~1019KB), v2 Lower Memory, Disabled, None, Only Sketch, 115200”

Test:14:68: error: cannot convert ‘const uint16_t* {aka const short unsigned int*}’ to ‘const unsigned int* const’ in initialization

const unsigned int* const int_table PROGMEM = { bar1, bar2, bar3 };

^

Test:14:68: error: cannot convert ‘const uint16_t* {aka const short unsigned int*}’ to ‘const unsigned int* const’ in initialization

Test:14:68: error: cannot convert ‘const uint16_t* {aka const short unsigned int*}’ to ‘const unsigned int* const’ in initialization

Test:16:25: error: storage size of ‘displayInt’ isn’t known

unsigned int displayInt; // Rückgabewert der Funktion zum Auslesen der Daten

^

exit status 1

cannot convert ‘const uint16_t* {aka const short unsigned int*}’ to ‘const unsigned int* const’ in initialization

Ein unsigned int* ist eben kein uint16_t*. Jedenfalls nicht überall.

Serial.println(displayInt); geht auch nicht mit dem Typ von displayInt.

Und strcpy kopiert bis zu einer Ende-0, das passt nicht zu einem int, weder mit 16 noch mit 32 Bit.

@michael_x ah, okay. Hab int_table den gleichen Datentyp uint16_t zugewiesen, damit gehts. Allerdings finde ich für "Serial.println(displayInt);" und für strcpy_P keine Alternativen, was müsste ich denn für einen Befehl nehmen,damit es funktiniert?

Mit dem ersten pgm_read_word liest du einen Zeiger auf dein uint16_t Array. Daraus liest du (bei Bedarf aufs richtige Array-Element zeigend mit einem weiteren pgm_read_word deinen uint16_t Wert.

Hab im Urlaub leider keine IDE dabei, und bin auch nicht der einzige, der das könnte.
Viel Erfolg. :slight_smile:

https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html

Das sieht man dass es eine _P Variante von memcpy() gibt

Oder einfach Wert für Wert per Hand auslesen. Dadurch kann man in manchen Anwendungen auch darauf verzichten das ganze Array ins RAM zu kopieren

@all

Danke für die guten Tipps, habs inzwischen hinbekommen, auch dank einem Bekannten :-*

// ISR for display refresh
void display_updater() {
  display.display(70);
}

void setup() {
  Serial.begin(115200); // Initialisiere den seriellen Port
  while (!Serial);  // Warte, bis der serielle Port verbunden ist
  Serial.println("OK");
  
  display.begin(16);
  display.clearDisplay();
  
  display_ticker.attach(0.002, display_updater);
  yield(); 
}

uint16_t displayInt[2048]; // Rückgabewert der Funktion zum Auslesen der Daten


void drawFrame( const uint16_t  *frame) {
  memcpy_P(displayInt, frame, sizeof(displayInt));
  display.clearDisplay();
  int imageHeight = 32;
  int imageWidth = 64;
  int counter = 0;
  for (int yy = 0; yy < imageHeight; yy++) {
    for (int xx = 0; xx < imageWidth; xx++) {
      display.drawPixel(xx, yy, displayInt[counter]);
      counter++;
    }
  }
  delay(100);
}

void loop() {
    Serial.println("loop");
  drawFrame(bar1); delay(1000);
  drawFrame(bar2);
  drawFrame(bar3);
  drawFrame(bar4);
  drawFrame(bar5);
  drawFrame(bar6);
  drawFrame(bar7);
  drawFrame(bar8);

  drawFrame(bar9); delay(1000);
  drawFrame(bar8);
  drawFrame(bar7);
  drawFrame(bar6); 
  drawFrame(bar5);
  drawFrame(bar4);
  drawFrame(bar3);  
  drawFrame(bar2);

  
  yield();
}

Die Lösung wollte ich natürlich nicht vorenthalten, hier mal ein Video vom Ganzen, hab ich ja versprochen ^^

VID_20201003_213619.mp4