Forum: Mikrocontroller und Digitale Elektronik Strings im Flash in zentraler Datei


von Martin M. (martin69)


Lesenswert?

Hallo,

ich habe ein größeres Programm und möchte sämtliche Strings im Flash 
ablegen. Allerdings möchte ich eine gemeinsam benutze Datei mit Texten 
schreiben, auf die von verschiedenen C-Files zugegriffen werden kann.

Ich habe schon ein paar Texte im Flash und es geht auch problemlos, 
allerdings geht das Speichern der Strings im Flash anscheinend nur in 
C-Files und nicht in H-Files.

Optimal für mich wäre eine Datei mit Zuordnung zwischen Zahl (ID) und 
Text im Flash, z.B.:

1:   "ABC"
2:   "Test abc"
3:   "Test 123"
4:   "Displaybeleuchtung mit Bargraph waehlen."

Beim Aufruf einer Funktion mit übergebener ID sollte dann der Text aus 
dem Flash in einem String zurück gegeben werden. So in etwa:

// Übergabewerte: ID und Adresse für den rückzuliefernden String
void text_aus_flash_holen(unsigned int uiTextID, char* caString)
  {
  strcpy_P(caString, xxx);    // xxx müßte irgend wie die ID sein
  return;
  }

Habe aber keinen Plan, wie ich die Strings mit der dazugehörenden ID 
abspeichern muß und später abrufen kann...

von Max H. (hartl192)


Lesenswert?

Wie wärs damit:
1
const char *strings[] = // Oder wie auch immer man deinem Compiler 
2
                        // sagt etwas in den Flash zu legen
3
{
4
  "ABC",
5
  "Test abc",
6
  "Test 123",
7
  "Displaybeleuchtung mit Bargraph waehlen.",
8
};
9
10
11
char text_aus_flash_holen(unsigned int uiTextID, char* caString)
12
{
13
  uiTextID--; // weil die ID bei 1 beginnen soll
14
  if(uiTextID >= sizeof(strings)/sizeof(char*))
15
    return -1; // ID existiert nicht
16
17
  strcpy_P(caString, strings[uiTextID]); 
18
  return 0;
19
}

von Martin M. (martin69)


Lesenswert?

hab es so ausprobiert und folgenden Aufruf gemacht:

text_aus_flash_holen(2, caText_temp);

Es kommt auch ein Text, aber ein falscher ("ext"). Irgend etwas ist noch 
nicht korrekt mit dem Zeiger auf den Flash...

-------------------------
Auch die zweite Version (editiert vom Benutzer) geht nicht. Es wird 0x00 
zurück geliefert (also Text gefunden), jedoch der falsche Text aus 
irgend einer Stelle im Flash...

von BirgerT (Gast)


Lesenswert?

Wenn der Index bei 1 beginnen soll, muss wohl -1 gerechnt werden, um 
Index 0 zu erhalten..

Habe so etwas ähnliches mit RTTTL Songfiles gemacht..
1
// Stringablage im Flash
2
3
const char PROGMEM song0[] = \
4
"Mosaic:d=8,o=6,b=400:c,e,g,e,c,g,e,g,c,g,c,e,c,g,e,g,e,c,p,c5,e5,g5,e5,c5,g5,e5,g5,c5,g5,c5,e5,c5,g5,e5,g5,e5,c5";
5
6
const char PROGMEM song1[] = \
7
"GetReady:d=4,o=5,b=125:8g...";
8
9
const char PROGMEM song2[] = \
10
"Addams Family:d=4,o=5,b=160:8c,..";
11
12
const char PROGMEM song3[] = \
13
"Axel F:d=4,o=5,b=125:g,8a#.";
Stringtabelle (ebenfalls im Flash) und Leseroutine ist dann hier schon 
beschrieben:

Beitrag "Re: RTTTL Player"

Die Funktion liefert den PGM_P mit der Stringadresse[index], der dann 
der funktion strcpy_P() als Quellzeiger übergeben werden kann.

von Martin M. (martin69)


Lesenswert?

Hab es nun so gemacht, aber der Compiler meckert. Ich denke ich habe 
etwas falsch verstanden.
1
const char *strings[] = 
2
{
3
  "ABC",
4
  "Test abc",
5
  "Test 123",
6
  "Displaybeleuchtung mit Bargraph waehlen.",
7
};
8
9
10
char text_aus_flash_holen(unsigned int uiTextID, char* caString)
11
{
12
  uiTextID--; // weil die ID bei 1 beginnen soll
13
  if(uiTextID >= sizeof(strings)/sizeof(char*))
14
    return -1; // ID existiert nicht
15
16
  strcpy_P(caString, audio_start_titel_P[uiTextID]); 
17
  return 0;
18
}
19
20
21
unsigned int uiNr;
22
23
PGM_P audio_start_titel_P(unsigned int uiNr){
24
  if (uiNr >= (sizeof(strings)/sizeof(strings[0]))) uiNr =0;
25
  return (const char*)(pgm_read_word(&(strings[uiNr])));
26
}

von BirgerT (Gast)


Lesenswert?

Martin M. schrieb:
> Hab es nun so gemacht, aber der Compiler meckert. Ich denke ich
> habe etwas falsch verstanden.

Da kannste froh sein, dass ich kein Compiler bin..

Schau Dir das hier mal an..
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Strings

dort wird das Prinzip für die Stringablage im Flash erläutert.

Erstmal - die Zeilen mit Deinen Texten sind ja unterschiedlich lang;
dann - für den Compiler steht da nur ein einziger String (mit mehreren 
Zeilen) - also funktioniert das da oben so nicht.

Du musst dem Compiler sagen - diese Zeile ist der String1, diese Zeile 
ist der String2 usw. - und ausserdem, wolltest Du die Strings nicht auch 
im Flash ablegen?!
1
const char string1[] PROGMEM = { "ABC" };
2
const char string2[] PROGMEM = { "Test abc" };
3
const char string3[] PROGMEM = { "Test 123" };
4
const char string4[] PROGMEM = { 
5
              "Displaybeleuchtung mit Bargraph waehlen."
6
};

Die Strings sind aber alle unterschiedlich lang. Die Auswahl über 
String[index] funktioniert aber nur, wenn alle Elemente / Strings die 
gleiche Länge haben. Also brauchst Du noch eine Tabelle mit konstant 
langen Elementen; hierfür bieten sich die Anfangsadressen Deiner Strings 
an.
1
const char * PROGMEM const strings[] = {
2
                 string1, string2, string3, string4
3
};

So - und um nun den String[index] aus dem Flash zu lesen, musst Du 
zuerst in der Tabelle schauen, an welcher Adresse der steht, und dann 
den String ab dieser Adresse kopieren.
1
char text_aus_flash_holen(unsigned int uiTextID, char* caString)
2
{
3
  uiTextID--; // weil die ID bei 1 beginnen soll
4
5
  // uiTextID == 0 -> 65535 -> >= Anzahl der Strings
6
  // uiTextID == 4 -> 3 -> < Anzahl der Strings
7
8
                  // ergibt die Anzahl der Elemente in der Tabelle
9
  if(uiTextID >= (sizeof(strings)/sizeof(strings[0])))
10
    return -1; // ID existiert nicht
11
12
             (1)      ---(2)-----  -----(3)----- (4) ----(5)------
13
  strcpy_P(caString, (const char*)(pgm_read_word(&(strings[uiTextID])))); 
14
  // (1) Zielstring im RAM caString[]
15
  // (2) cast gegen Compilerwarnung
16
  // (3) Lesefunktion für ein 16-Bit Irgendwas(Int,Uint,..)
17
  // (4) Adressoperator ergibt die Speicheradresse des Tabelleneintrags..
18
  // (5) ..in dem die Speicheradresse von string[uiTextID(-1)] steht..
19
  // .. und diese Speicheradresse ist der QuellPointer für strcpy_P()
20
21
  return 0;
22
}

Jetzt sollte es aber klappen..

von Roland E. (roland0815)


Lesenswert?

Alle Strings landen im "Flash". Immer, sonst würde der Controller die ja 
vergessen. Falls ein spezieller "UserFlash" gemeint ist, müssen die 
Strings mit dem entsprechenden Pragma markiert werden, damit die an die 
entsprechenden Adressen geschrieben werden.

Dei den meisten MuC funktioniert schon:

const char string[x] = "Blahblubb"

und dann einfach auf den String per Variable zugreifen. Bei der 
Konstruktion funktioniert auch sizeof().

Das "PROGMEM" Pragma brauchts nur für (einige) Atmels.

Luxuriös wirds, wenn man noch einen Zeilenumbruch (oder 0x00) dranhängt, 
um ggf eine Fehlerbehandlung für Ausgaben zu bekommen.

Aber was der OP mein ist:
Alle diese "strings" in einer zentralen '.h'+'.c'- Datei verpacken, und 
entsprechend regulär includieren. Dann können alle Module sauber per 
Variablenname darauf zugreifen.

von Rolf M. (rmagnus)


Lesenswert?

Martin M. schrieb:
> Hab es nun so gemacht, aber der Compiler meckert. Ich denke ich habe
> etwas falsch verstanden.

Das ist aber echt schade, daß der Compiler nur "mecker" ausgibt, statt 
eine Beschreibung, die auf den Fehler hinweisen könnte und eine 
Zeilennummer.

Roland E. schrieb:
> Alle Strings landen im "Flash". Immer, sonst würde der Controller die ja
> vergessen.

Naja, beim AVR landen die Strings ohne weiteres Zutun im RAM, aber der 
muss ja irgendwie intialisiert werden. Der Initialisierungswert ist also 
im Flash gespeichert, die eigentliche Stringvariable aber ist im RAM.

> Das "PROGMEM" Pragma brauchts nur für (einige) Atmels.

Konkret die AVRs, um die es offenbar geht, denn strcpy_P() gibt's meines 
Wissens auch nur dort.

> Luxuriös wirds, wenn man noch einen Zeilenumbruch (oder 0x00) dranhängt,
> um ggf eine Fehlerbehandlung für Ausgaben zu bekommen.

0x00 (bzw. '\0') wird sowieso schon automatisch angehängt. Das ist die 
"Natur" eines Stings in C. Was das mit Fehlerbehandlung zu tun hat, 
verstehe ich aber nicht.

von Roland E. (roland0815)


Lesenswert?

Wenn ich eine Variable mit

const char string[] = "Irgendwas"

anlege, ist die nicht Nullterminiert. Das ist bei C auch völlig normal. 
Das die dann aus dem Flash als Ganzes in den Ram geholt wird, ist ein 
AVR-spezifisches Problem.

Die "Fehlerbehandlung" ist durch den Programmierer zu implementieren, 
wenn der auf die Daten wie oben beschrieben per eigenen Zeiger losgeht. 
Er sieht nämlich sonst nicht, wo sein String zu Ende ist.

Zugegeben, ich hatte mich mit AVR nur am Rande beschäftigt. Es gibt 
genug andere und weniger umständliche Plattformen...

von Max H. (hartl192)


Lesenswert?

Roland E. schrieb:
> Wenn ich eine Variable mit
>
> const char string[] = "Irgendwas"
>
> anlege, ist die nicht Nullterminiert. Das ist bei C auch völlig normal.
Nein, Stringliterale sind in C automatisch nullterminiert.

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.