Forum: Mikrocontroller und Digitale Elektronik Strings für LCD im Flash ablegen


von Thomas S. (thomass)


Lesenswert?

@all,

ich wollte eine Displaysteuerung machen das hat soweit auch alles 
funktioniert jedoch habe ich alle STRINGs im RAM abgelegt.Versuche nun 
schon den ganzen Sonntag diese Strings im Flash abzulegen und von dort 
auch wieder zu lesen jedoch ohne nennenswerten Erfolg.
Ich habe das ganze so aufgebaut, in einer Struktur ist der 
status,String(Pointer) und die dazugehörige Funktion abgelegt.
1
typedef struct
2
{
3
  unsigned int state;
4
  const char *ptext_01;
5
  const char *ptext_02;
6
  int (*pfunction)(char input);
7
}MENU_STATE;
Nur weis ich nicht ob hier noch PROGMEM fehlt was in den Beispielen beim 
Arrayaufbau verwendet wird

Die Strings sind wie folgt definiert
1
const char MT01_001[]    PROGMEM ="123456";
2
const char MT02_001[]    PROGMEM ="234567";
3
const char MT01_002[]    PROGMEM ="345678";
4
const char MT02_002[]    PROGMEM ="456789";
5
const char MT01_003[]    PROGMEM ="567890";
6
const char MT02_003[]    PROGMEM ="678901";
Nun rufe ich aus den Programm wie folgt auf:
1
const char *pstrflash01;
2
const char *pstrflash02;
3
char *LCD_01_StatusText;
4
char *LCD_02_StatusText;
5
int (*pStateFunc)(char);
6
/**********************************************************************/
7
for(uint=0; menu_state[uint].state ; uint++)
8
{
9
  zahl = menu_state[uint].state;
10
  if ( menu_state[uint].state == LCD_Status )
11
  {
12
    pstrflash01 = (const char *) (pgm_read_word(&(menu_state[uint].ptext_01)));
13
    strcpy_P(M01_String, pstrflash01);
14
    LCD_01_StatusText =  M01_String;
15
//  LCD_01_StatusText =  menu_state[uint].ptext_01;
16
//  LCD_02_StatusText =  menu_state[uint].ptext_02;
17
    break;
18
  }
19
}
20
/**********************************************************************/
21
lcd_string(LCD_01_StatusText,1);
22
lcd_string(LCD_02_StatusText,2);
Im Simulator(mit Hapsim) wollte ich es simulieren jedoch wird die 
Simulation abgebrochen mit der Meldung
AVR Simulator: Infalid opcode 0xffff at adress 0x00ffff
Was mache ich hier falsch?

Danke für Eure Hilfe

Thomas

von Thomas S. (thomass)


Lesenswert?

Hallo,

wollte mal den Beitrag nochmal nach vorne holen.
Hat keiner eine Idee oder Tip an was es liegt bzw. wo der Fehler liegt.

Danke

Thomas

von Karl H. (kbuchegg)


Lesenswert?

Deine Frage ist so nicht beantwortbar, da es auch von
der Definition der Variablen menu_state abhängt, ob der
Code gültig ist oder nicht.

von Thomas S. (thomass)


Lesenswert?

@Karl-Heinz,

genau das ist das Problem ich weis nicht so recht wie das funktioniert.
Dieses Struct steht in einem H-File und wird in Main.c mit include 
eingebunden.
Solange das Struct im RAM gespeichert war hat das auch wunderbar 
funktioniert.

Kannst Du mir mal genauer erklären was Du meinst?

DANKE

Thomas

von Karl H. (kbuchegg)


Lesenswert?

Thomas S. wrote:

> Kannst Du mir mal genauer erklären was Du meinst?

Wie sieht die Definition von menu_state aus. Hast du die
Variable ebenfalls mittels PROG_MEM ins Flash verschoben
oder nicht.

Wenn du nur hast

MENU_STATE menu_state[] = {
  { LCD_SATATE, ... , ... , ... },
  { ..... }
};

dann liegt das Array menu_state selbst nicht im Flash.
In deinem Code deutet einiges darauf hin, dass dem so ist.

Dann kann aber das hier
1
    pstrflash01 = (const char *) (pgm_read_word(&(menu_state[uint].ptext_01)));

nicht stimmen, denn um an den Textpointer zu kommen, genügt es
menu_state_ptext_01 einfach auszulesen, da diese Variable ja
im SRAM liegt. Lediglich der Pointerwert, den man von dort bekommt
ist ein Pointer der ins Flash zeigt.
1
  zahl = menu_state[uint].state;
2
  if ( menu_state[uint].state == LCD_Status )
3
  {
4
    strcpy_P(M01_String, menu_state[uint].ptext_01);
5
    LCD_01_StatusText =  M01_String;
6
7
//  LCD_01_StatusText =  menu_state[uint].ptext_01;
8
//  LCD_02_StatusText =  menu_state[uint].ptext_02;
9
    break;

M01_String ist als Array hoffentlich gross genug dimensioniert
um so einen Text aufnehmen zu können.

von Thomas S. (thomass)


Lesenswert?

Karl heinz Buchegger wrote:
>
> Wenn du nur hast
>
> MENU_STATE menu_state[] = {
>   { LCD_SATATE, ... , ... , ... },
>   { ..... }
> };
>
Ja genau das habe ich weil es ja vorher alles im RAM gespeichert war
Wie muss ich das nun abändern.
1
PROGMEM MENU_STATE menu_state[] = {
2
   { LCD_SATATE, ... , ... , ... },
3
   { ..... }
4
};
Sehe ich das richtig oder ist das wieder falsch?


Thomas

von Thomas S. (thomass)


Lesenswert?

Hallo,

so ich habe nun mal alles umgeändert wie ich es oben beschrieben also
aus

MENU_STATE menu_state[]  -->   PROGMEM MENU_STATE menu_state[]


Der Aufruf der des Textes aus der Main.c habe ich auch so abgeändert wie 
Karl-Heinz es beschrieben hat.
Das Array M01_String hat 90 char Felder und das Display hat 80 sollte 
also ausreichen.
Wenn ich nun alles übersetze --> keine Fehler/Warnungen etc.
Lasse ich jedoch alles im Simulator ablaufen wird die if Schleife
1
if ( menu_state[uint].state == LCD_Status )

nicht durchlaufen obwohl der LCD_Status eigentlich OK ist.
Irgentwie scheint etwas mit dem zugriff auf den Flash etwas nicht zu 
passen.

Bin etwas ratlos.

Danke schon mal.

Thomas

von Karl H. (kbuchegg)


Lesenswert?

Thomas S. wrote:

> so ich habe nun mal alles umgeändert wie ich es oben beschrieben also
> aus
>
> MENU_STATE menu_state[]  -->   PROGMEM MENU_STATE menu_state[]

OK. Also liegt menu_state nun im Flash.
Daraus folgt jetzt sofort, dass du auf ein Element des Arrays
nicht mehr direkt zugreifen kannst, sondern dass du es zuerst
mit einem pgm_read_xxx zuerst ins SRAM holen musst.

> Der Aufruf der des Textes aus der Main.c habe ich auch so abgeändert wie
> Karl-Heinz es beschrieben hat.

Dann hast du es in die falsche Richtung verändert. Ich bin davon
ausgegangen, dass menu_state nicht im Flash liegt, weil ich dachte
das so aus deinem Code herausgelesen zu haben.

> Das Array M01_String hat 90 char Felder und das Display hat 80 sollte
> also ausreichen.

Gut.

> Wenn ich nun alles übersetze --> keine Fehler/Warnungen etc.

Das sagt gar nichts. Der Compiler überprüft nur ob dein Programm
den Grammatikregeln der Sprache entspricht. Für die Logik bist
immer noch du zuständig.

> Lasse ich jedoch alles im Simulator ablaufen wird die if Schleife
>
>
1
if ( menu_state[uint].state == LCD_Status )
>
> nicht durchlaufen obwohl der LCD_Status eigentlich OK ist.

menu_state liegt jetzt im Flash.
D.h. du kannst so nicht darauf zugreifen. Du musst dir vorher eine
Kopie davon ins SRAM holen, so dass du darauf zugreifen kannst

> Irgentwie scheint etwas mit dem zugriff auf den Flash etwas nicht zu
> passen.

Eine Möglichkeit die Dinge etwas zu entschärfen und für etwas
Übersicht zu sorgen ist es, die Variablen anders zu benennen.
Lass den Namen jedes Teils das im Flash liegt mit dem Zusatz
'flash' beginnen. Dann verrät dir der Variablenname selbst ob
du einen Zugriff machen kannst, oder ob du zunächst ins SRAM umkopieren
musst.
1
typedef struct
2
{
3
  unsigned int state;
4
  const char *ptext_01;
5
  const char *ptext_02;
6
  int (*pfunction)(char input);
7
}MENU_STATE;
8
 
9
PROGMEM MENU_STATE flash_menu_state[];
10
11
.....
12
   for(uint=0; flash_menu_state[uint].state ; uint++)

Boing, das kann nicht gehen. flash_menu_state ist eine Variable
die im Flash liegt und auf die kann man nicht einfach so zugreifen.
Ergo muss der Eintrag uint zuerst ins SRAM geholt werden.
1
  unsigned int menu_state;
2
3
  uint = 0;
4
  menu_state = pgm_read_word( &menu_state[uint].state );
5
  while( menu_state ) {
6
     ....
7
8
     uint++;
9
     menu_state = pgm_read_word( &menu_state[uint].state );
10
  }

Und so gehst du jetzt jeden Zugriff auf flash_menu_state durch.
Immer dran denken: Aus flash_menu_state kannst du nicht direkt
lesen, sondern du musst dir den interessierenden Wert zuerst
mit einem pgm_read_xxx ins SRAM (also auf in normale Variable)
holen.
Da sich bei dir alles in der Schleife abspielt, könnte man auch
darüber nachdenken, nicht jeden Member der Struktur einzeln in
SRAM Variablen zu holen, sondern das jeweils interessierende
Arrayelement (also 1 Struktur als ganzes) in einem Rutsch in
eine Kopie im SRAM zu holen.

von Thomas S. (thomass)


Lesenswert?

> Da sich bei dir alles in der Schleife abspielt, könnte man auch
> darüber nachdenken, nicht jeden Member der Struktur einzeln in
> SRAM Variablen zu holen, sondern das jeweils interessierende
> Arrayelement (also 1 Struktur als ganzes) in einem Rutsch in
> eine Kopie im SRAM zu holen.

JA das würde ich gerne machen jedoch dann blicke ich vermutlich noch 
weniger durch wie gerade.

Also ich habe nun alle Strukturen im Flash und rufe die mit pgm_read_*** 
ab.
Ich habe nun die korrekte Sartadresse schon mal aus dem Flash lesen 
können.
Also ist der nächste Schritt sich den Text zu holen.
Ich habe nur Zugriffe pgm_read_*** für Byte,word etc. gefunden.
Wenn ich jedoch einen String auslesen will muss ich ja mehrere 
hintereinander auslesen bis \n --> Stringende.
Ich habe in Beispielen im AVR-GCC-Tutorial gesehen das dort mit strcpy 
ein Arrays aus dem Flash ausgelesen wird.
Kann ich dieses auch bei mir verwenden?
Habe leider keine brauchbaren Beispiele mit STrkturen und Flash 
gefunden.

Danke schon mal

Thomas

von Thomas S. (thomass)


Lesenswert?

@Karl-Heinz,

ich habe es nun hinbekommen das der Text so wie es sein soll auf dem 
Display erscheint.
Doch da macht sich schon das nächste Problem auf sobald ich den 
Funktionpointer aufrufe gibt es einen Restart ich schiesse den uC also 
ins Nichts.
Mein Strukt ist so definiert
1
typedef struct
2
{
3
  unsigned int state;
4
  const char *ptext_01;
5
  const char *ptext_02;
6
  int (*pfunction)(char input);
7
}MENU_STATE;
in der Mainschleife ist der Pointer so definiert
1
int (*pStateFunc)(char);
die Übergabe
1
pStateFunc = flash_menu_state[uint].pfunction;
Wenn ich nun die Funktion aufrufen will
1
if(pStateFunc)
2
{
3
   LCD_naechster_Status = pStateFunc(Eingabe_Taster);
4
   pStateFunc = NULL;
5
}
Startet das Programm wieder neu.

Was mache ich noch falsch?

Thomas

von Karl H. (kbuchegg)


Lesenswert?

Das hier

> pStateFunc = flash_menu_state[uint].pfunction;

kann nicht richtig sein. Wie der Name flash_menu_state schon
sagt, ist das eine Variable die im Flash liegt. Also kannst
du die nicht mit einer normalen Zuweisung auslesen, sondern musst
wieder eine pgm_read_xxx Funktion dafür benutzen.
Hinweis: Ein Funktionspointer ist auch nichts anderes als eine
2 Byte Variable. Du kannst also pgm_read_word dafür benutzen.
Das Ergebnis wirst du dir zurechtcasten müssen. Spätestens
jetzt ist es Zeit für einen typedef um die Funktionspointer
Syntax zu entschärfen
1
typedef int (*CallbackPtr) (char);
2
3
typedef struct
4
{
5
  unsigned int state;
6
  const char *ptext_01;
7
  const char *ptext_02;
8
  CallbackPtr pfunction;
9
} MENU_STATE;
10
11
....
12
13
    pStateFunc = (CallbackPtr) pgm_read_word( &flash_menu_state[uint].pfunction );
14
 
15
    if(pStateFunc)
16
    {
17
      LCD_naechster_Status = pStateFunc(Eingabe_Taster);
18
      pStateFunc = NULL;
19
    }


PS: uint ist ein selten dämlicher Name für eine Schleifenvariable.
Nenn solche Dinge i, j, k aber keinen Namen den man mit einem
Datentyp verwechseln kann.

von Karl H. (kbuchegg)


Lesenswert?

Thomas S. wrote:

> Also ist der nächste Schritt sich den Text zu holen.
> Ich habe nur Zugriffe pgm_read_*** für Byte,word etc. gefunden.
> Wenn ich jedoch einen String auslesen will muss ich ja mehrere
> hintereinander auslesen bis \n --> Stringende.

Nicht bis \n. \n ist in einem String Schall und Rauch. Die C
Definition eines Strings lautet: es gehören solange Zeichen
zum String bis ein \0 Zeichen auftaucht, welches zum String
gehört aber das Ende das Strings markiert.

> Ich habe in Beispielen im AVR-GCC-Tutorial gesehen das dort mit strcpy
> ein Arrays aus dem Flash ausgelesen wird.

Nicht strcpy.  strcpy_P !

Alle vordefinierte Funktionen, die mit Flash arbeiten können, haben
vereinbarungsgemäß immer ein _P am Namensende

> Kann ich dieses auch bei mir verwenden?
> Habe leider keine brauchbaren Beispiele mit STrkturen und Flash
> gefunden.

Du hast einen Pointer, der ins Flash zeigt, hier ist er
1
    char* pstrflash01 ;
2
3
    pstrflash01 = (char *) (pgm_read_word(&(flash_menu_state[uint].ptext_01)));

an dieser Stelle ist in pstrflash01 ein Pointer gespeichert, dessen
Inhalt die Adresse im Flash ist, an der der String abgelegt wurde.
Also kannst du mittels strcpy_P den tatsächlichen String von dort
holen.
1
    strcpy_P(M01_String, pstrflash01);

M01_String muss als char Array natürlich groß genug definiert sein.
Aber danach hast du den String ganz normal im SRAM liegen und kannst
ihn von dort weiter bearbeiten.

von 3347 (Gast)


Lesenswert?

Erstaunlich was man alles machen muss, um den LPM Befehl nicht benutzen 
zu muessen...

von Karl H. (kbuchegg)


Lesenswert?

3347 wrote:
> Erstaunlich was man alles machen muss, um den LPM Befehl nicht benutzen
> zu muessen...


Alles halb so wild, wies im Moment aussieht.

von Hannes L. (hannes)


Lesenswert?

Man gut, dass es Assembler gibt...

;-)

...

von Thomas S. (thomass)


Lesenswert?

@Karl-Heinz,

muss mich erst mal entschuldigen da ich mich erst jetzt wieder melde 
aber ich hatte letzte Woche einiges um die Ohren und konnte mich nicht 
mit meiner Menusteuerung beschäftigen.
Ich habe mich gestern und heute nochmals hingesetzt und ein kleineres 
Projekt gemacht damit es etwas übersichtlicher wird.
Ich habe nun einiges was Du mir geschrieben hast mit eingebunden und so 
langsam lichten sich die Nebel.
Eigentlich habe ich schon gedacht das alles läuft bis ich auf folgendes 
gestößen bin.
Sobald ich einen String für mein Display laden will es jedoch in diesem 
Menüpunkt keinen String gibt verabschiedet sich der uC.
Also solange es einen String gibt ist alles OK wenn jedoch kein String 
im Speicher steht verabschiedet sich der uC.
1
typedef struct
2
{
3
  unsigned int state;
4
  const char *ptext_01;
5
  const char *ptext_02;
6
  int (*pfunction)(char input);
7
}MENU_STATE;
Achja wenn ich die Pointerzeile so ändere wie Du es vorgeschlagen hast 
bekomme ich eine Fehlermeldung. NUR SO ZUR INFO nicht das es daran 
liegt.
MENU_STATE sieht wie folgt aus.
1
PROGMEM MENU_STATE flash_menu_state[] =
2
{
3
  //STATE   STATE_TEXT_Display_1   STATE_TEXT_Display_2   STATE_FUNKTION
4
  {ST_01,   MT01_01,               MT02_01,               LCD_01},
5
  {ST_02,   MT01_02,               MT02_02,               LCD_02},
6
  {ST_03,   MT01_03,               MT02_03,               NULL},
7
  {ST_04,   MT01_04,               MT02_04,               LCD_04},
8
  {ST_05,   MT01_05,               MT02_05,               LCD_05},
9
  {ST_06,   MT01_06,               MT02_06,               LCD_06},
10
  {ST_07,   MT01_07,               MT02_07,               LCD_07},
11
  {ST_08,   MT01_08,               NULL,                  LCD_08},
12
  {ST_09,   MT01_09,               NULL,                  LCD_09},
13
  {ST_10,   MT01_10,               MT02_10,               LCD_10},
14
}

Nun rufe ich aus der main das lesen der Strings wie folgt auf.
1
pstrflash = (const char *) (pgm_read_word(&(flash_menu_state[value_uinteger1].ptext_01)));
2
strcpy_P(M01_String, pstrflash);
3
4
pstrflash = (const char *) (pgm_read_word(&(flash_menu_state[value_uinteger1].ptext_02)));
5
strcpy_P(M02_String, pstrflash);
6
LCD_01_StatusText =  M01_String;
7
LCD_02_StatusText =  M02_String;
Das ganze funktioniert bis flash_menu_state ST_07 sobald ich nun in den 
Menupunkt ST_08 springe und strcpy_P(M02_String, pstrflash); ausgeführt 
wird kommt es zum Absturz.
Wenn strcpy_P(M01_String, pstrflash); ausgeführt wird steht das richtige 
im Array.
Wenn ich die zweite Adresse des 2 Strings auslese hat der Pointer den 
Wert 0x0000.
Muss ich nach der Abfrage der Adresse nochmals prüfen ob der Pointer die 
Adresse 0x0000 hat und dann eben nichts tun?
So funktioniert es zumindest.
1
pstrflash = (const char *) (pgm_read_word(&(flash_menu_state[value_uinteger1].ptext_02)));
2
if(pstrflash) 
3
{
4
   strcpy_P(M02_String, pstrflash);
5
   LCD_02_StatusText =  M02_String;
6
}
Werde morgen nochmal alles prüfen ob ich nicht doch noch einen Fehler 
übersehen habe.
Achja bevor ich haue bekomme, ich habe MENU_STATE die Name nur der 
Übersichtlichkeit umbenannt.

Gruß

Thomas

von Thomas S. (thomass)


Lesenswert?

@Karl-Heinz,

so ich habe nun mal alles nochmal gemacht.
Ich kann nun keine Fehler in der Menuführung mehr finden ich hoffe es 
zumindest.
Ich frage nun immer bevor ich den Pointer zuweise die übergebene Adresse 
ab, ist diese 0x0000 ist kein String vorhanden und dann wird auch nichts 
verarbeitet.
Ich musste nur noch die for Schleifen etwas verändern da diese einen 
falschen Abbruchwert lieferten.
Dies  bemerkte ich jedoch erst als ich mehr Menupunkte definiert habe da 
kam dann ein vorzeitiger Abruch dies habe ich nicht bemerkt als noch 
wenige Menupunkte definiert waren.
Die Pointerdefinition in meinem Struct habe ich so gelassen und nicht 
verändert wie Du es vorgeschlagen hast.
Wollte mich der Fehlermeldung nicht mehr stellen.

Ich danke Dir für Dein Hilfe, Ratschläge und Beispiele.

Thomas

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.