Forum: Compiler & IDEs Wer sieht den Fehler?


von Frank E. (erdi-soft)


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:
1
void showMenu(void)
2
{
3
   uint8_t doexit;
4
   uint8_t changed;
5
   static uint8_t menuepos = 3;
6
  
7
   changed = 1;
8
   doexit = 0;
9
  
10
   lcd_clrscr();
11
  
12
   PORTD |= (1 << Disp_Light);
13
14
   do
15
   {      
16
      if(changed)
17
      {  
18
         lcd_gotoxy(0,0);
19
      
20
         lcd_putc(0x7E);
21
         lcd_puts_p(menue[menuepos].text);
22
                  
23
         changed = 0;
24
      }
25
    
26
      if(getKeyPressed(1<<KEY_LEFT))
27
      {
28
         menuepos++;
29
         //menuepos = pgm_read_byte(&menue[menuepos].previous);
30
         changed = 1;
31
      }
32
      else if(getKeyPressed(1<<KEY_MID))
33
      {
34
         if(doexit == 2)
35
         {
36
            lcd_gotoxy(0,1);
37
            lcd_puts("   ");
38
            doexit = 0;
39
         }
40
         else
41
         {
42
            lcd_gotoxy(0,1);
43
            lcd_puts("Bla");
44
            doexit = 2;
45
         }
46
         //menuepos = pgm_read_byte(&menue[menuepos].next);
47
         //menuepos--;
48
         changed = 1;
49
      }
50
      else if(getKeyPressed(1<<KEY_RIGHT))
51
      {
52
         //menue[menuepos].fp();
53
  
54
         changed = 1;
55
      
56
         if(menuepos == (MENUESIZE - 1))
57
         {
58
            doexit = 1;
59
         }
60
      }
61
   } while(doexit != 1);
62
}

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.

von Frank E. (erdi-soft)


Lesenswert?

Ok, hat sich erledigt.

Muss man erst mal wissen, dass man sich die Adressen noch mit diesen 
Konstrukten aus dem Speicher holen muss:
1
lcd_puts_p((const char *) pgm_read_word (&menue[menuepos].text));
2
3
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.

von Karl H. (kbuchegg)


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:
>
>
1
> lcd_puts_p((const char *) pgm_read_word (&menue[menuepos].text));
2
> 
3
> fp = (void(*)(void)) pgm_read_word (& menue[menuepos].fp);
4
>
>

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.

von Frank E. (erdi-soft)


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:
1
static const char menu_string0[] PROGMEM = "Set Time       ";
2
3
const MENU_ENTRY menue[] PROGMEM = {
4
  { menu_string0, 3, 1, setTime },
5
  { menu_string1, 0, 2, setDate },
6
  { menu_string2, 1, 3, setIntervall },
7
  { menu_string3, 2, 0, menueExit }
8
};

von Karl H. (kbuchegg)


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:
>
>
1
> static const char menu_string0[] PROGMEM = "Set Time       ";
2
> 
3
> const MENU_ENTRY menue[] PROGMEM = {
4
>   { menu_string0, 3, 1, setTime },
5
>   { menu_string1, 0, 2, setDate },
6
>   { menu_string2, 1, 3, setIntervall },
7
>   { menu_string3, 2, 0, menueExit }
8
> };
9
>

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.


von Karl H. (kbuchegg)


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.

von Frank E. (erdi-soft)


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.

von Karl H. (kbuchegg)


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 :-)


von Frank E. (erdi-soft)


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.

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.