www.mikrocontroller.net

Forum: Compiler & IDEs Wer sieht den Fehler?


Autor: Frank Erdrich (erdi-soft)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Leute,

ich hab da mal wieder ein Problem.
Versuche mich auch gerade an nem Menü für ein Display. Bin dabei nach 
verschiedenen Vorschlägen und Ideen hier ausm Forum vorgegangen.

Hier ein Teil des Codes:
void showMenu(void)
{
   uint8_t doexit;
   uint8_t changed;
   static uint8_t menuepos = 3;
  
   changed = 1;
   doexit = 0;
  
   lcd_clrscr();
  
   PORTD |= (1 << Disp_Light);

   do
   {      
      if(changed)
      {  
         lcd_gotoxy(0,0);
      
         lcd_putc(0x7E);
         lcd_puts_p(menue[menuepos].text);
                  
         changed = 0;
      }
    
      if(getKeyPressed(1<<KEY_LEFT))
      {
         menuepos++;
         //menuepos = pgm_read_byte(&menue[menuepos].previous);
         changed = 1;
      }
      else if(getKeyPressed(1<<KEY_MID))
      {
         if(doexit == 2)
         {
            lcd_gotoxy(0,1);
            lcd_puts("   ");
            doexit = 0;
         }
         else
         {
            lcd_gotoxy(0,1);
            lcd_puts("Bla");
            doexit = 2;
         }
         //menuepos = pgm_read_byte(&menue[menuepos].next);
         //menuepos--;
         changed = 1;
      }
      else if(getKeyPressed(1<<KEY_RIGHT))
      {
         //menue[menuepos].fp();
  
         changed = 1;
      
         if(menuepos == (MENUESIZE - 1))
         {
            doexit = 1;
         }
      }
   } while(doexit != 1);
}

Das Problem ist nun folgendes. Es funktioniert eigentlich alles soweit 
richtig, es sei denn, ich beschreibe irgendwo die Variable menuepos 
(einer der auskommentierten Befehle). Dabei ist es egal, ob diese 
Variable als static innerhalb oder außerhalb der Funktion angelegt wird 
oder ob es eine lokale innerhalb der Funktion ist.
Dann gibt das Display wirre Zeichen aus.
Und ich sehe den Fehler nicht. Bzw. kann mir das Verhalten nicht 
erklären.

Bin für jede Idee dankbar.


Gruß,
Frank.

Autor: Frank Erdrich (erdi-soft)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, hat sich erledigt.

Muss man erst mal wissen, dass man sich die Adressen noch mit diesen 
Konstrukten aus dem Speicher holen muss:
lcd_puts_p((const char *) pgm_read_word (&menue[menuepos].text));

fp = (void(*)(void)) pgm_read_word (& menue[menuepos].fp);

Wobei das komischerweise ohne antasten von menuepos auch ohne 
funktioniert hat. Vielleicht kann mir das trotzdem mal jemand noch etwas 
erklären.


Gruß,
Frank.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frank Erdrich wrote:
> Ok, hat sich erledigt.
>
> Muss man erst mal wissen, dass man sich die Adressen noch mit diesen
> Konstrukten aus dem Speicher holen muss:
>
>
> lcd_puts_p((const char *) pgm_read_word (&menue[menuepos].text));
> 
> fp = (void(*)(void)) pgm_read_word (& menue[menuepos].fp);
> 
>

Das hängt jetzt davon ab, wie du die Datenstruktur
für menue definiert hast. Dieses Code Schnipsel hier
impliziert, dass du sie mit einem PROGMEM angelegt hast.

> Wobei das komischerweise ohne antasten von menuepos auch ohne
> funktioniert hat. Vielleicht kann mir das trotzdem mal jemand noch etwas
> erklären.

Das war reiner Zufall. Du hast zufällig aus irgendwelchem
SRAM Speicher Werte gelesen, die zufälligerweise so waren, dass
sie mit den Werten im Flash übereingestimmt haben. Dadurch ist
aus einem falschen Zwischenschritt wieder das Richtige entstanden

Was du daraus lernen sollst:
Testen kann nie das Fehlen von Fehlern nachweisen sondern immer
nur das Vorhandensein von Fehlern. Auch wenn es so aussieht, als
ob alles in Ordnung ist, heist das noch lange nicht, dass es
tatsächlich so ist.

Autor: Frank Erdrich (erdi-soft)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Reproduzierbar zufällig? Auch nach neu programmieren und/oder Reset oder 
Spannung weg, warten, bis Kondensator entladen, und dann wieder dran? 
Halte ich, ehrlich gesagt, für sehr unwahrscheinlich.
Es sei denn, der Compiler würde sich schon zur Compilezeit den ersten 
String, der durch die Initialisierung von menuepos referenziert wird, 
von Anfang an in den RAM kopieren. Aber das kann ich mir beim besten 
Willen nicht vorstellen.

Auch anderweitig greife ich auf const char arrays (String arrays) zu, 
ohne die Adresse über pgm_read_word aus dem Flash zu holen. Und das 
funktioniert ohne Probleme. Deshalb ist es ja so schwer zu verstehen.

Hier mal noch das Menü-Array und einer der Strings:
static const char menu_string0[] PROGMEM = "Set Time       ";

const MENU_ENTRY menue[] PROGMEM = {
  { menu_string0, 3, 1, setTime },
  { menu_string1, 0, 2, setDate },
  { menu_string2, 1, 3, setIntervall },
  { menu_string3, 2, 0, menueExit }
};

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frank Erdrich wrote:
> Reproduzierbar zufällig?

Wenn du das gleiche Programm mit den gleichen Werten immer
wieder startest, kommt auch immer wieder das gleiche heraus.

> Auch nach neu programmieren und/oder Reset oder
> Spannung weg, warten, bis Kondensator entladen, und dann wieder dran?

Das hat nichts damit zu tun.
Aus den gleichen Grundeinstellungen im Speicher und dem
ablaufen lassen des gleichen Programmes kommt wieder dasselbe
beobachtbare Verhalten zu tage.

> Halte ich, ehrlich gesagt, für sehr unwahrscheinlich.

Ist aber so.

> Deshalb ist es ja so schwer zu verstehen.

Genau das ist das Problem.
Solche Dinge sind extrem schwer zu verstehen und nachzuvollziehen.
Wenn man es ganz genau wissen will, dann bleibt nichts anderes
übrig, als jedes Byte im Speicher zu überwachen und absolut
jeden Programmschritt im Detail nachzuvollziehen.

>
> Hier mal noch das Menü-Array und einer der Strings:
>
>
> static const char menu_string0[] PROGMEM = "Set Time       ";
> 
> const MENU_ENTRY menue[] PROGMEM = {
>   { menu_string0, 3, 1, setTime },
>   { menu_string1, 0, 2, setDate },
>   { menu_string2, 1, 3, setIntervall },
>   { menu_string3, 2, 0, menueExit }
> };
> 

Na da haben wir es ja. menue ist mittels PROGMEM in den Flash
gewandert. Ergo muss der Zugriff über die Flash Funktionen
gehen. Tust du das nicht, dann benutzt der AVR die Speicheradresse,
die eigentlich für das Flash gedacht war um damit Werte aus dem
SRAM zu holen. Was er dabei erwischt kann niemand vorhersagen,
ausser er analysiert das Programm komplett.

Bei einem menupos von 0 hat er halt zufällig einen Wert aus dem
SRAM gekriegt, mit dem er in weiterer Folge aus dem Flash was
richtiges bekommen hat. In dem Moment, in dem du menupos
verändert hast, ist dann was anderes aus dem SRAM gekommen,
das nicht mehr gepasst hat und du hast dein Problem entdeckt.

Wenn du dein Programm verändert hättest (und dadurch die Flash
Belegung verändert hättest) oder ein paar Variablen zusätzlich
angelegt hättest (die die SRAM Belegung verändert hätten), oder
ein paar Texte mehr ins Flash gelegt hättest, oder die Reihenfolge
der Text im Flash verändert hättest, oder ein paar Funktions-
aufrufe mehr hättest (verändert den SRAm Zellen, die der Stack
hinterlässt) oder die Aufrufhierarchie veränderst, oder ...
dann hätte die Geschichte eventuell anders ausgesehen und das
Ganze hätte auch bei einem menupos von 0 schon nicht funktioniert. 
Insofern war das Zufall, dass die Umgebungsbedingungen deines
Programmes so zusammengestimmt haben, dass es bei einem menupos von
0 zufällig funktioniert hat.


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> const char arrays

const char Arrays liegen bei einem AVR im SRAM.
Die C-Startupsequenz kopiert sie beim hochfahren des Programmes
aus dem Flash in den SRAM.
Nur dann wenn Dinge explizit als PROGMEM gekennzeichnet werden,
gibt es sie ausschliesslich nur im Flash.

Autor: Frank Erdrich (erdi-soft)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger wrote:
> Wenn du dein Programm verändert hättest (und dadurch die Flash
> Belegung verändert hättest) oder ein paar Variablen zusätzlich
> angelegt hättest (die die SRAM Belegung verändert hätten), oder
> ein paar Texte mehr ins Flash gelegt hättest, oder ...
> dann hätte die Geschichte eventuell anders ausgesehen und das
> Ganze hätte auch bei einem menupos von 0 schon nicht funktioniert.
> Insofern war das Zufall, dass die Umgebungsbedingungen deines
> Programmes so zusammengestimmt haben, dass es bei einem menupos von
> 0 zufällig funktioniert hat.

Genau das hab ich getan. Die Variable von lokal nach global, zusätzliche 
Variablen eingefügt und auch den Wert bei der Erstinitialisierung 
verändert. Es hat mir nach wie vor den jeweils richtigen String 
angezeigt.

Ich vergaß zu erwähnen, dass diese anderen String-Arrays auch im Flash 
per PROGMEM abgelegt sind.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frank Erdrich wrote:
> Karl heinz Buchegger wrote:
>> Wenn du dein Programm verändert hättest (und dadurch die Flash
>> Belegung verändert hättest) oder ein paar Variablen zusätzlich
>> angelegt hättest (die die SRAM Belegung verändert hätten), oder
>> ein paar Texte mehr ins Flash gelegt hättest, oder ...
>> dann hätte die Geschichte eventuell anders ausgesehen und das
>> Ganze hätte auch bei einem menupos von 0 schon nicht funktioniert.
>> Insofern war das Zufall, dass die Umgebungsbedingungen deines
>> Programmes so zusammengestimmt haben, dass es bei einem menupos von
>> 0 zufällig funktioniert hat.
>
> Genau das hab ich getan. Die Variable von lokal nach global, zusätzliche
> Variablen eingefügt und auch den Wert bei der Erstinitialisierung
> verändert. Es hat mir nach wie vor den jeweils richtigen String
> angezeigt.
>
> Ich vergaß zu erwähnen, dass diese anderen String-Arrays auch im Flash
> per PROGMEM abgelegt sind.

Dann bleibt nichts anderes übrig:
Wenn du es genau wissen willst, dann nimm dein ursprüngliches
fehlerhaftes Programm her und gehe es im Assembler Schritt
für Schritt durch, bis du genau weist durch welche Aktion
sich der korrekte Zahlenwert für die Flash Adresse ergeben
hat und warum der zufällig noch im Speicher stand. Hinterfrage
alles und jedes Byte im Speicher. Nur dann hast du eine Chance
rauszufinden was da wirklich abgelaufen ist.

Viel Spass dabei :-)


Autor: Frank Erdrich (erdi-soft)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das werde ich nach den Klausuren wohl auch mal tun.
Hab mir gestern auch mal das Listfile angesehen. Dabei ist mir nix 
auffälliges untergekommen. Na ja, werden sehen. Ich melde mich, wenn ich 
was rausgefunden habe.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.