Fragen zum Stack?

zum Hintergrung: ganz normale Abläufe im loop (pollingbetrieb). Um Speicher zu sparen wiederholbare Bedingungen (Rechnen, Merken, Displayausgaben) in eine Funktion packen. Über Parameter werden dieVariablen beschickt undanschließend wieder in den passenden Variabel gespeichert. Alles okay, bis aus den Zustand, daß ein erster Aufruf problemlos funktioniert, aber ein darauf folgender eine falsche Dispalyausgabe iniziert (falsche Positionen). Jetzt meine Frage: Kann es am Stack liegen? Wie groß ist die Stacktiefe, wenn z.B. in der ersten Funktion, weitere Funktionen integriert sind, die ja alle über den Stack eine Verbindung haben.

LCD.txt (2.67 KB)

Ohne den Kode zu sehen kann ich dazu nichts sagen.

Ein Stackproblem äußert sich eher in Reboots, nicht im Ändern von Displaypositionen.

Eine Angabe des verwendeten Arduinos ist auch unerlässlich um irgendeine Aussage zu Stack zu machen. :wink:

Die mögliche Stackgröße hängt auch an der Menge des verwendeten Ram Speichers ab. Die Daten im RAM wachsen von unten nach oben während der Stack von oben nach unten wächst.

Grüße Uwe

Ein Stackproblem äußert sich eher in Reboots, nicht im Ändern von Displaypositionen

Wobei es oft nicht wirklich ein Reboot ist, sondern schlimmer. Undefiniert eben.
Auf dem Stack liegen ja nicht nur lokale Variable, sondern auch Rücksprung-Adressen.
Aber wenn der Stack in sich ok bleibt, kann er schonmal in die globalen Variablen hineinreichen, und diese verändern.

freeRam wird dich interessieren.

Es ist ein UNO. belegt 11278 von 32256 / 639 (rund 31%) und 1409 lokale Var von 2048. Hier dürften die Ursachen nicht zu suchen sein oder? den Code habe ich oben als Dateianhang geladen.

Der oben hinzugefügte Kodeausschnitt erscheint mir auf den ersten Blick unverdächtig.

//######################################################################
//#--- strukturierte Ausgabe auf LCD -Spalte, Zeile, Pointer, delay ---#
//######################################################################
void LCD_Ausgabe_String(int Sp, int Ze, char* Str, int T) {
  lcd.setCursor(Sp, Ze);
  lcd.print(Str); delay (T); //Schreibe Text in Spalte/Zeile
}
//######################################################################
//#---- strukturierte Ausgabe auf LCD -Spalte, Zeile, Zahl, delay -----#
//######################################################################
void LCD_Ausgabe_Zahl(int Sp, int Ze, int Zahl, int T) {
  lcd.setCursor(Sp, Ze);
  if (Zahl > 0) {              //keine Ausgabe, wenn Null Std.
    lcd.setCursor(Sp , Ze);    // Spalte 12, Zeile 1
    if (Zahl < 10000) {
      lcd.print(" ");   // ein leerzeichen wenn Zahl kleiner 4-stellig
    }
    if (Zahl < 1000) {
      lcd.print(" ");   // ein leerzeichen wenn Zahl kleiner 3-stellig
    }
    if (Zahl < 100) {
      lcd.print(" ");  // ein leerzeichen wenn Zahl kleiner 2-stellig
    }
    if (Zahl < 10) {
      lcd.print(" ");  // ein leerzeichen wenn Zahl kleiner 1-stellig
    }
  }
  lcd.print(Zahl); delay (T); //Schreibe Zahl rechtsbündig in Spalte/Zeile
}
//######################################################################
//# ---- Z = Zeile | LM = Laufzeit (Std) | LS = Laufzeit (Sek)-------- #
//######################################################################
void LCD_Print(int Z, word LM, word LS, word LG) {
  LCD_Aus00 = LCD_Aus0;            //starte LCD_Backlight-Zyklus on
  LCD_BACK_ON_OFF();
  // Ausgabe der Stunden und Sekunden - Einschaltzeit
  //Sekundenausgabe  // max. 4 Stellen (sek / h)
  //Stundenausgabe
  if (LM > 0) {                      //keine Ausgabe, wenn Null Std.
    lcd.setCursor(15, Z); lcd.print("h");  // Cursor Spalte letzte | Zeile
    LG = LG / Stunde;     //Laufzeit in Sekunden bis 3600
    LCD_Ausgabe_String(BS_Col - 2, Z, G, 0);  //Anzeige "Gesamt"
    LCD_Ausgabe_Zahl(BS_Col, Z, LG, Anzeigezyklus); //Verzögerung der ersten Anzeige
    LCD_Ausgabe_String(BS_Col - 2, Z, A, 0);  //Anzeige "Aus"
    LCD_Ausgabe_Zahl(BS_Col, Z, LM, Anzeigezyklus);
  }
  LCD_Ausgabe_String(BS_Col - 2, Z, A, 0);  //Anzeige "Aus"
  lcd.setCursor(15, Z);              //Cursor Spalte letzte | Zeile
  lcd.print("s");                    //Laufzeit in Sekunden bis 3600
  LCD_Ausgabe_Zahl(BS_Col, Z, LS, 0);
  //Rückgabewerte
  if (Z = 0) {                       // Pumpe 1 aktuallisieren
    BS_Lauf_M1 = LM; BS_Lauf_S1 = LS;
  }
  if (Z = 1) {                       // Pumpe 2 aktuallisieren
    BS_Lauf_M2 = LM; BS_Lauf_S2 = LS;
  }
}

Ohne den Kontext (d.h. das gesamte Programm, evtl. mit Herkunft der verwendeten nicht-standard Libraries) nützt das nichts.

Bitte poste das vollständige Programm (in Kode Tags).

Wenn es zu fett dazu ist (das befürchte ich, wenn ich den Auschnitt betrachte),
hänge es an wie den Ausschnitt zuvor, aber in einem neuen Post.

Ich vermute du überschreibst die Variablen, mit denen die gezeigten Routinen aufgerufen werden.

und 1409 lokale Var von 2048

Das sind die statischen Variablen zur kompilierzeit.
Während der Ausführung braucht der Sketch dann noch weiteren Speicher.

Daraus ergibt sich: Dein Sketch braucht zuviel Speicher.

Grüße Uwe

Die Variablen 2 mal 3 Stück sind okay, nur die Anzeige spinnt, LCD 16*2 - Zeile eins ist okay, reagiert auch immer richtig, darstellung im zeitlich rahmen, also zuerst Text dann folgt eine var "G" mit einem Wert, Warteschleife, dann ein "A" mit einem Wert und delay, zuletzt ein anderer Wert und wieder delay. Jetzt zu einem beliibigen anderen Zeitpunkt soll das gleiche in Zeile 2 geschehen, hat jetzt nichts mehr mit Z1 zu tun, soll der gleiche Ablauf gestartet werden und jetzt erscheint der die G-Variable an einer anderen Stelle im LCD. Werden die Functionen gekillt und vollständig in die Hauptfunktion geschrieben,also selbstständig, dann ist ja alles zufriedenstellend. Also werde ich es erstmal dabei belassen. ev. ist auch ein Geschwindigkeitsproblem auf dem I2CL-Bus, denn nur die Anzeige in Z2, keine Ausgabe auf Z1, zeigt das gleiche Verhalten mit der Funktion. Da im Moment noch Reserven vorhanden sind, wird die Sparvariante verworfen. Zuerst einmal Dank für die Anregeungen zum Stackverhalten, hatte vor 30 Jahren ähnliche Probleme mit Stack usw. da waren die Geräte nur wesentlich größer, schon was so ein klein kerl alles kann. Also Danke für die Infos.

Wenn du nicht wissen willst, woran es liegt, auch gut.

Dir ist bewusst dass ein Funktionsaufruf meist nur 2 Byte Stack kostet,
da die Parameter in aller Regel in Registern übergeben werden?

Dazu kommen die lokalen Variablen, in deinem Fall keine.

"Es ist irgendwas mit dem Stack" halte ich für Voodoo-Glauben.

Die hohe RAM-Belastung könnte man wahrscheinlich deutlich senken,
aber deinen Kode möchtest du ja für dich behalten.

Viel Erfolg!

Whandall:
Dir ist bewusst dass ein Funktionsaufruf meist nur 2 Byte Stack kostet,

Mehr, da einige Register aus dem Stack gesichert werden müssen. Sieht man in Assembler schön wo dann meistens erstmal eine Reihe PUSH am Anfang kommen und Ende POP

Ist halt komplizierter und vom Kontext abhänig, müsste man im Assembler Kode ansehen.

Mehr hier What registers are used by the C compiler?

müsste man im Assembler Kode ansehen.

Habe ich in meiner IDE eingebaut.
Bei jedem kompilieren, wirft es mir auch eine Disassembler Datei aus.

Ihr könnt euch ja zwischen “meist” und “mehr” auf “mindestens” einigen.

Eine Funktion ohne lokale Variable und ohne Aufrufparameter braucht nur die Rücksprungadresse auf dem Stack.

int g;
void test() { g++;}
void setup() {
   test();   
}
void loop() { }
00000090 <_Z4testv>:
  90: 80 91 00 01 lds r24, 0x0100
  94: 90 91 01 01 lds r25, 0x0101
  98: 01 96       adiw r24, 0x01 ; 1
  9a: 90 93 01 01 sts 0x0101, r25
  9e: 80 93 00 01 sts 0x0100, r24
  a2: 08 95       ret

000000a4 <setup>:
  a4: 0c 94 48 00 jmp 0x90 ; 0x90 <_Z4testv>

000000a8 <loop>:
  a8: 08 95       ret

Da setup mit einem Funktionsaufruf endet, wird sogar call (und die Rücksprungadresse auf dem Stack) gespart.
Aber das zählt nicht ; )

Nett dass du dir die Mühe gemacht hast. :wink:

Deine erläuterungen wurden in den grauen Zellen wieder aktiviert, habe 20 jahre andere arbeiten gemacht, jetzt als rentner ist es ein hobby damit alsheimer nicht zuschlägt, da ging alles nur im Assembler. aber wahrscheinlich ist die ursache mit dem i2cl-bus zu suchen, kann der interrupt (nicht benutzt und nichts dazu eingetragen) vielleicht das übel sein. mir fehlen dazu entsprechende unterlagen, werdewohl doch erstmal ein buch suchen, um mich auf den stand zu bringen, denn echtzeitsysteme sind mir ein guter begriff - die entwicklungsumgebungen nicht so geläufig.

Ohne deinen Kode ganz zu sehen kann man nichts dazu sagen außer vielleicht:

I2C benötigt keinen von dir zu programmierenden Interrupt, also liegst du damit

SOFT_HARD:
wahrscheinlich ist die ursache mit dem i2cl-bus zu suchen, kann der interrupt (nicht benutzt und nichts dazu eingetragen)

falsch.