Programmspeicher Adresse von Byte-Arrays einer Funktion übergeben

Hallo,

ich möchte die Speicheradresse von einem Byte-Array das im Flash abgelegt ist einer Funktion
übergeben, die dann die Daten des Arrays aus dem Speicher auslesen soll.
Das funktioniert auch, nur der Compiler wirft mir folgende Meldung vor die Füße:

————————————————————————
warning: invalid conversion from 'const byte ()[10] {aka const unsigned char ()[10]}' to 'word {aka unsigned int}' [-fpermissive]
Wr_Img(&Grafik);
^~~~~~~

initializing argument 1 of 'void Wr_Img(word)'
void Wr_Img(word ADDR) {
^~~~~~

————————————————————————

Hier der Code:

const  byte Gafik[] PROGMEM = {
   0x0B, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x8C, 0xFA, 0x01, 0x01, 0x02, 0x04,
}; 


void Wr_Img(word ADDR) {
  for (byte i = 1; i < (pgm_read_byte(ADDR)); i++) {
    Serial.print(pgm_read_byte(ADDR+i));
    }
}

void loop() {
   Wr_Img(&Grafik);
   while(true) {
   }
}

Irgend etwas mache ich da falsch. Kann mir hier im Forum jemand helfen.

Vielen Dank und Gruß

schnaggenhals

Vorschlag:


const  byte grafik[] PROGMEM =
{
  0x0B, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x8C, 0xFA, 0x01, 0x01, 0x02, 0x04,
};


template<typename ArrayType,size_t N>
void wrImg(ArrayType (&arrayImFlash)[N])
{
  Serial.print("Anzahl ArrayZellen: "); Serial.println(N);
  for (const byte &zelle:arrayImFlash)
  {
    Serial.println(pgm_read_byte(&zelle),HEX);
  }
}

void setup()
{
  Serial.begin(9600);
  wrImg(grafik);
}

void loop()
{
}

Der Code ist nicht vollständig.
Zumindest fehlt das setup() ... Wie sonst kommt da was auf dem SerMon raus?

Hallo my_xy_projekt,

,das setup() habe ich bewußt in dem Eintrag weggelassen.
Wollte nur in der Schleife etwas Code hineinschreiben.

Gruß

schnaggenhals

Evtl. solltest du diese Haltung nochmal überdenken.
Denn Testcode sollte auch testbar sein.

Die Adresse eines Byte Arrays hat nunmal nicht den Datentyp word (unsigned int) auch wenn sie zwei Bytes groß ist. Weil die Größe passt geht das im Prinzip, aber produziert eine Warnung.

Hallo DokuVorleserVerweigerer,

vielen Dank für Deine Antwort und Hinweise.
Das posten von lauffähigen Testcode werde ich in Zukunft beherzigen.

Gruß

schnaggenhals

Hier wären eigentlich auch Code Tags (zumindest Anführungszeichen) notwendig damit die Sternchen nicht als Umschaltung auf kursiv interpretiert werden.

Hallo combie,

ich habe Deinen Vorschlag mal benutzt. Funktioniert soweit. Danke dafür.
Bisher habe ich in Assembler und mit LunaAvr meine Projekte umgesetzt.
Zum großen Bedauern ist der Entwickler von LunaAvr verstorben und es geht an dieser Stelle
nicht weiter.
Jetzt habe ich mich entschieden mit C und der Arduino IDE weiter zu arbeiten.
Mein fertiges Projekt (eigene Platine mit OLED - Display, ATmega328, AD-Wandler MCP3426)
möchte ich nun nach C portieren.
Da ich keine aufwendige Grafik benötige habe ich einen eigenen Display-Treiber programmiert
der ohne Framebuffer auskommt. Für ein paar Grafikelemente habe ich dann die Funktion
Wr_Img() geschrieben und habe damit das zuvor beschrieben Problem.
Ich stehe bei C noch am Anfang und ich denke man kann das alles noch effektiver umsetzen.
Aber irgendwo muss man anfangen.
Deinen Vorschlag verstehe ich im Detail ganz und gar nicht. Daher hier noch einmal ein Testcode.
Die Behandlung der I2C-Schnittstelle sowie die Initialisierung des Displays habe ich im Testcode
auskommentiert. Ich hoffe das ist OK.
In der Funktion Wr_Img(); ist aber ersichtlich was ich vorhabe.
Das Byte-Array beinhaltet die Anzahl der Bytes die an das Display übertragen werden sollen,
Option wie die Grafik-Daten behandelt werden sowie die absolute Position (x,y) des ersten
Grafikbytes.

Gruß

schnaggenhals

const  byte Line_128[] PROGMEM = { 
   128,   // Länge
     2,   // 0 = Daten unterschiedlich  1 = Daten gleich 2 = Daten alternieren
     0,   // Pos. X [0 ... 127]
     2,   // Pos. Y [0 ...   7]
  0x08, 0x00,
};

const  byte Life_0[] PROGMEM = { 
     6,   // Länge
     0,   // 0 = Daten unterschiedlich  1 = Daten gleich 2 = Daten alternieren
   120,   // Pos. X [0 ... 127]
     7,   // Pos. Y [0 ...   7]
  0x21, 0x20, 0xF9, 0x20, 0x21, 0x00,
};


void Wr_Img(word ADDR) {
  // goto_xy(pgm_read_byte(ADDR+2),pgm_read_byte(ADDR+3));  // Cursor setzen
  // i2c_start((SSD1306_I2C_ADDRESS << 1) | 0);
  // i2c_byte(0x40);                                        // 0x00 for command, 0x40 for data
  
  switch (pgm_read_byte(ADDR+1)) {
  case 0:
    for (byte i = 4; i < (pgm_read_byte(ADDR)+4); i++){
      // i2c_byte(pgm_read_byte(ADDR+i));
    }
    break;

  case 1:
    for (byte i = 0; i < (pgm_read_byte(ADDR)); i++){
      // i2c_byte(pgm_read_byte(ADDR+4));
    }
    break;

  case 2:
    for (byte i = 0; i < (pgm_read_byte(ADDR)); i=i+2){
      for (byte j = 4; j < 6; j++) {
        // i2c_byte(pgm_read_byte(ADDR+j));
      }
    }
    break;
  }
  //i2c_stop();
}


void setup() {
  /*
  i2c_init();             // init TWI/I2C 
  Init_SSD1306();         // Init OLED Disply SSD1306 Display ini
  Clr_Scr();              // SSD1306 löschen
  */
  Wr_Img(&Line_128);
  Wr_Img(&Life_0);
}

void loop() {
}

Fein!
Nur, dass Arduino meist C++ ist.

Ich sehe, dass du da "verschiedenartige" Dinge in ein Array stopfst.
Das scheint mir recht seltsam zu sein......

Warum seltsam,
ich möchte doch nur eine Adresse (Pointer) an eine Funktion übertragen ohne viel Tamtam.
Da die Grafik an festen Positionen stehen wird, brauche ich doch nicht PosX und PosY
im Funktionsaufruf übertragen.

"Fein!" naja, ich denke mir mein Teil.

"seltsam"
Das ist der Syntax von C oder C++ auch.
Noch besser, habe setup() und loop() schon eliminiert damit man den unnötigen Kram
wie delay() und dessen Interrupt-Routine los wird.
Behandlung von Interrupts mache ich lieber in Assembler in einer *.S Datei.

Alles in allem komme ich mit Deinen Aussagen nicht weiter.

Danke dafür.

Gruß

schnaggenhals

Das mag sein......

Sagt was ähnliches....

Mein Grundsatz lautet, zumindest einer davon:

Gleiches in ein Array, verschiedenes in eine Struktur.

Und für mich sind Grafikdaten ganz was anderes, wie x-y Positionen.
Etwas Konzeptionell anderes.

In C++ verwendet man bevorzugt Referenzen.
Pointer haben ein Geschmäckle.

Das habe ich weder gesagt, noch gemeint.
Nicht mal im Ansatz.

Hier mal, wie das in etwa bei mir aussehen würde, mit der Gefahr, dass du auch damit wenig anfangen kannst.

#include <Streaming.h> // die Lib findest du selber ;-)
Print &cout = Serial; // cout Emulation für "Arme"

/*
const  byte Life_0[] PROGMEM = { 
     6,   // Länge
     0,   // 0 = Daten unterschiedlich  1 = Daten gleich 2 = Daten alternieren
   120,   // Pos. X [0 ... 127]
     7,   // Pos. Y [0 ...   7]
  0x21, 0x20, 0xF9, 0x20, 0x21, 0x00,
};
*/

enum class GMode:byte{unterschiedlich,gleich,alternierend};

struct Position
{
  byte x;
  byte y;
};

struct Grafik
{
  byte dataSize;
  GMode mode;
  Position pos;
  byte data[];
};

void ausgabe(const Grafik &grafik)
{
  cout << F("dataSize: ") << pgm_read_byte(&grafik.dataSize) << endl;
  
  cout << F("mode: ");
  switch(GMode(pgm_read_byte(&grafik.mode)))
  {
    case GMode::unterschiedlich : cout << "unterschiedlich"; break;
    case GMode::gleich :          cout << "gleich";          break;
    case GMode::alternierend :    cout << "alternierend";    break;
  }
  cout << endl;
  
  cout << F("x: ")        << pgm_read_byte(&grafik.pos.x)        << endl;
  cout << F("y: ")        << pgm_read_byte(&grafik.pos.y)        << endl;

  byte size = pgm_read_byte(&grafik.dataSize);
  for(unsigned i = 0; i<size; i++) cout << _HEX(pgm_read_byte(&grafik.data[i])) << endl;
  cout  << endl;
}

// Kurzschreibweise
const Grafik Live_0 PROGMEM {6,GMode(0),{120,7},{0x21, 0x20, 0xF9, 0x20, 0x21, 0x00}};

//alternative Schreibweise
const Grafik Live_1 PROGMEM {
                              dataSize: 7,
                              mode:     GMode::unterschiedlich,
                              pos:      Position{ x: 120, y: 7},
                              data:     {0x21, 0x20, 0xF9, 0x20, 0x21, 0x00, 0x42}
                            };

void setup() 
{
  Serial.begin(9600);
  cout << F("Start: ") << F(__FILE__) << endl;
  
  ausgabe(Live_0);
  ausgabe(Live_1);
}
  
void loop() 
{

}

Hallo combie,

danke für Deine letzte Antwort. Nun ich konnte damit etwas anfangen.
Dein letzter Vorschlag lebt schon im Zielsystem.

Strukturen und deren Aufbau kenne ich. Im Prinzip hatte ich das schon in Form
des Bytes-Arrays.
Mein Fehler war, dass ich versucht habe das Problem unter C++ (C) so anzugehen
wie ich es unter Assembler lösen würde.

Nun es funktioniert mit Deiner Hilfe und ich habe zudem noch etwas gelernt.

Danke und Gruß

schnaggenhals

Hallo,

@combie
Kompiliert das bei dir? Bei mir nicht. Egal ob mit Standard Toolchain oder aktueller. Streaming Lib ist aktuell 6.0.10. Er mag bei mir das _HEX Makro einfach nicht. Nehme ich das raus ist alles i.O.
Keines der Makros bspw. _DEC, _BIN, _HEX, _OCT wird aktzeptiert. Serial.print ( , HEX) funktioniert. Interessant.

In file included from C:\Users\Worker\AppData\Local\Temp\arduino_modified_sketch_740435\sketch_may15a.ino:1:0:
C:\Users\Worker\AppData\Local\Temp\arduino_modified_sketch_740435\sketch_may15a.ino: In function 'void ausgabe(const Grafik&)':
sketch_may15a:47:49: error: statement-expressions are not allowed outside functions nor in template-argument lists
   for(unsigned i = 0; i<size; i++) cout << _HEX(pgm_read_byte(&grafik.data[i])) << endl;
                                                 ^
C:\Arduino IDE Portable\Mega2560\arduino-1.8.19\portable\sketchbook\libraries\Streaming\src/Streaming.h:70:30: note: in definition of macro 'typeof'
 #define typeof(x) __typeof__(x)
                              ^
C:\Users\Worker\AppData\Local\Temp\arduino_modified_sketch_740435\sketch_may15a.ino:47:44: note: in expansion of macro '_HEX'
   for(unsigned i = 0; i<size; i++) cout << _HEX(pgm_read_byte(&grafik.data[i])) << endl;
                                            ^~~~
C:\Arduino IDE Portable\Mega2560\arduino-1.8.19\portable\sketchbook\libraries\Streaming\src/Streaming.h:139:37: error: template argument 1 is invalid
 #define _HEX(a)     _BASED<typeof(a)>(a, HEX)
                                     ^
C:\Users\Worker\AppData\Local\Temp\arduino_modified_sketch_740435\sketch_may15a.ino:47:44: note: in expansion of macro '_HEX'
   for(unsigned i = 0; i<size; i++) cout << _HEX(pgm_read_byte(&grafik.data[i])) << endl;
                                            ^~~~
Bibliothek Streaming in Version 6.0.10 im Ordner: C:\Arduino IDE Portable\Mega2560\arduino-1.8.19\portable\sketchbook\libraries\Streaming  wird verwendet
exit status 1
statement-expressions are not allowed outside functions nor in template-argument lists

Tuts!

Bei mir:
#define STREAMING_LIBRARY_VERSION 5

Streaming.zip (2,7 KB)

Auch nicht wenn Du normale Variablen übergibst? Vielleicht liegt es ja beim Controller, ob Zugriffe auf RAM und Flash wirklich gleichartig compilieren. Ein Unterprogramm muß sich dann entscheiden, welche Sorte Speicher es akzeptieren möchte.

Hallo,

also mit der obigen v5 klappt das.
Ich habe dann mit meiner Lib das hier probiert, funktioniert. Ohne die extra Zeile aber mit expliziten byte cast funktioniert es nicht.

for(unsigned i = 0; i<size; i++) 
{
  const byte var = pgm_read_byte(&grafik.data[i]);
  cout << _HEX(var) << endl;
}
cout  << endl;

Meine Lib ist die hier. Sollte eigentlich eine Weiterentwickelte sein. Die Makros darin sehen leicht verschieden aus. Das Schlüsselwort typeof ist neu darin. Ich werde wohl Meldung erstatten müssen. Das geht so nicht. :wink: Scheinbar kann das mit dem typeof nicht aufgelöst werden ohne einen klaren Datentyp wo auch ein expliziter cast nicht hilft? Kopfkratz.

Das habe ich ausprobiert, und das geht.
Aber dabei habe ich mir den generierten Code angesehen.
:japanese_ogre:(hätte ich vielleicht nicht machen sollen) :japanese_ogre:

Hier die Quelle, wobei test eine einfache byte Variable im Flash ist.

  typeof(pgm_read_byte(&test)) t = pgm_read_byte(&test);

Und hier dann der generierte Code nach dem Preprozessor:

  typeof((__extension__({ uint16_t __addr16 = (uint16_t)((uint16_t)( &::test )); uint8_t __result; __asm__ __volatile__ ( "lpm %0, Z" "\n\t" : "=r" (__result) : "z" (__addr16) ); __result; })) ) t = (__extension__({ uint16_t __addr16 = (uint16_t)((uint16_t)( &test)); uint8_t __result; __asm__ __volatile__ ( "lpm %0, Z" "\n\t" : "=r" (__result) : "z" (__addr16) ); __result; }));

pgm_read_byte() ist also selber ein Makro, mit recht komplexem Inhalt, wie z.B. casts und Inline Assembler.
Eine richtige Wurst!

Hiermit wird ja direkt und ausschließlich aus PROGMEM gelesen. Das Ergebnis im RAM sollte dann keinen Einschränkungen mehr unterliegen.

Hallo,

@ combie:
"meine" v6.0.10 Lib funktioniert bei dir? Das wäre noch verrückter.
Ja das sind alles "nur" Makros und manche verschachtelt mit anderen. Link

@ Dr.:
ja schon. Theoretisch sollte das eigentlich egal sein, der Compiler könnte den Ausdruck auch direkt einsetzen. Macht er bzw. kann er aber nicht, weil das erst zur Laufzeit ermittelt wird. constexpr funktioniert erst gar nicht, lehnt er gleich ab.

Ist schon amüsant wie unterschiedlich sich der Code verhalten kann.
Ich habe es auch ohne die libStdCpp probiert, kompiliert auch nicht, weil bei meiner Lib ganz unten auf der ersten Seite steht man darf eine libStdCpp nicht verwenden. Macht bei mir wie gesagt keinen Unterschied. Verrückte Welt.