www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Anfänger: Hilfer, Stack underflow Problem


Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo liebes Forum,

wer von euch Profis hilft einem Neuling auf dem Gebiet AVR & ASM?

Folgendes Problem:

STK 500
AVR Studio 4.13
ATMEGA 8515

Ich habe diesen CODE (soll aus einer "db" daten rausholen und diese an 
die LEDs weitergeben) und es kommt andauert die Fehlermeldung:
AVR Simulator: Stack Underflow at 0x0015
AVR Simulator: Invalid opcode 0xffff at address 0x00ffff
.include "m8515def.inc"
.def temp=r16

   ldi   temp, LOW(RAMEND)
    out   SPL, temp
    ldi   temp, HIGH(RAMEND)
    out   SPH, temp
 
    ldi temp, 0xFF    ; Port b = Ausgang
    out DDRB, temp

    ldi ZL, LOW(text*2)         ; Adresse des Strings in den
    ldi ZH, HIGH(text*2)        ; Z-Pointer laden

  rjmp lcd_flash_string
main:
  
  
  out PORTB,temp
  rcall w0

ret
text:   
  .db 0b00000001,0b00000000,0b00000100,0b00001000,0b00010000,0b00100000,0b01000000,0b10000000
  .db 0b00011010,0b00110000,0b10001111,0b11111110,0b01100011,0b01000001,0b00111011,0b10010001,0xff                ; Stringkonstante, durch eine 0
                                       ; abgeschlossen  


lcd_flash_string:
           push  temp
 
lcd_flash_string_1:
           lpm   temp, Z+
           ;cpi   r30, 0x27
     cpi  temp, 0xff
           breq  lcd_flash_string_2
           rcall  main
           rjmp  lcd_flash_string_1
 
lcd_flash_string_2:
           pop   temp
           ret




w0:
push temp
;ret      PORTB 1 24
ldi    r18, 2

Warten:    ldi    r16, 200  ;r16 auf 250
    
Warten1:  ldi    r17, 250  ;r17 auf 250
    
Warten2:
    dec    r17      ;ziehe -1 von r17 ab
    brne  Warten2    ;wenn Z (r17) nicht 0 dann letzten befehl nochmal
    dec    r16      ;(r17 ist jetzt 0) ziehe -1 von r16 ab
    brne  Warten1    ;0?weiter:dec r16
    dec    r18
    brne  Warten
    pop temp
  ret



Ich komme einfach nicht weiter, der Stackpointer ist doch schon 
initialisiert. Daher verstehe ich nicht warum ein Underflow kommt.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Yatko Jaens (denim)

>AVR Simulator: Stack Underflow at 0x0015
>AVR Simulator: Invalid opcode 0xffff at address 0x00ffff

Hast du auch im Simulator den 8515 eingestellt? Das ist eine getrennte 
Einstellung vom Projekt!

MFG
Falk

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, ist (leider) eingestellt... (unter debug, avr options)

ATmega8515

Flash Size: 8192 bytes
Eeprom Size: 512 bytes
Internal SRAM Size: 512 bytes
External SRAM Size: 65536 bytes
IO Size: 64 bytes
Max Speed: 16.00 MHz

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohnn, ich sehe gerade dein "Kunstwerk" genauer an. Die wilden rcalls 
sind totaler Mist!

AVR-Tutorial: Stack

MfG
Falk

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich weiss.. schäm mich auch... aber komme nicht weiter...
hab da das tut auch durchgelesen, alles aufeinmal verstehen ist auch 
echt schwer...
Hatte mir das Buch von Günter Schmitt "Microcomputertechnik mit 
Controllern der Atmel AVR-Risc Familie" geholt, aber da blicke ich auch 
ncith durch... da geht es wirklich ins eingemachte...

Habe mir hier sämtliche Tipp Tutorials angeschaut und mich an die 
Empfehlungen gehalten und entsprechendes Equipment gekauft. Und es wird 
ja empfohlen ASM zu programmieren, das ist aber wirklich ncith so 
einfach. Ich überlege ob ich nicht lieber direkt mit C anfangen sollte, 
da dort solche Probleme wohl nicht passieren können...

Vielen Dank das Du mir helfen möchtest :)

Autor: Bernhard M. (boregard)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du machst:
  rjmp lcd_flash_string
und von dort
   ret
das gibt den Stackunterlauf!

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wie müsste denn der code korrekterweise aussehen?

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, vielen dank! habs hinbekommen, habe statt dem ret ein rjmp auf 
lcd_flash_string gemacht. Du hast mir den richtigen Denkanstoss gegeben 
:)

Habe aber noch eine andere Frage:
Das scheint ja normal zu sein bei dem STK 500 das die LEDs leuchten bei 
LOW. Gibt es da auf dem Brett eine Einstellung das man es umkehrt, also 
bei LOW auch die entsprechende LED dunkel bleibt?

Autor: AVRFan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
  ...
  ...
  rcall lcd_flash_string

main:
  out PORTB,temp
  rcall w0

   rjmp main

;-------------------------------------

text:   
  .db 0b00000001,0b00000000,0b00000100,0b00001000,0b00010000,0b00100000,0b01000000,0b10000000
  .db 0b00011010,0b00110000,0b10001111,0b11111110,0b01100011,0b01000001,0b00111011,0b10010001,0xff                ; Stringkonstante, durch eine 0
                                       ; abgeschlossen  
  ...
  ...

Probier mal das. (Aber Du solltest auch verstehen, wann warum ein 
'rcall' stehn muss und wann ein 'rjmp' richtig ist).

Autor: AVRFan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Das scheint ja normal zu sein bei dem STK 500 das die LEDs leuchten bei
>LOW.

Ja.

>Gibt es da auf dem Brett eine Einstellung das man es umkehrt, also
>bei LOW auch die entsprechende LED dunkel bleibt?

Nein, zum Glück nicht.  Denn wenn es die gäbe und man würde sie mal so 
und mal so konfigurieren, dann käme man blos noch durcheinanderer.

Man merkt sich einfach

L -> LED an
H -> LED aus

und gut ist.

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, vielen Dank an alle :)

der code sieht jetzt so aus (ich schäm mich auch)
ich glaube das was da bei lcd_flash_string_2 passiert ist nicth ganz 
korrekt oder? wenn ich da ncith drin habe, wird R30 nicht auf 26 
zurückgestellt und zählt einfach weiter...


lcd_flash_string_2:
           pop   temp

     ldi ZL, LOW(text*2)         ; ist das schlimm wenn das hier andauernd wiederholt wird, oder gibt es einen anderen weg?
           ldi ZH, HIGH(text*2)        ; Z-Pointer laden

            rjmp  lcd_flash_string
      ;ret

Autor: AVRFan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Diese beiden Zeilen...
     ldi ZL, LOW(text*2)
     ldi ZH, HIGH(text*2)
gehören.. .
lcd_flash_string:
           push  temp

           [--> hierhin! <--]
>wenn ich da ncith drin habe, wird R30 nicht auf 26
>zurückgestellt und zählt einfach weiter...

So ist es.

Und passieren wegen dem Aufrufen der Zeilen tut da garantiert nix.

Während Dein Programm läuft und Dein ATmega8515 mit 8 MHz getaktet ist, 
wird die Instruktion 'dec r17' in der w0-Routine schließlich auch 
ungefähr 2.6 Millionen mal pro Sekunde (klar, wieso so oft?) ausgeführt, 
und sie geht nicht kaputt dadurch.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Yatko Jaens (denim)

Irgendwie hab ich die Befürchtung, dass du das noch nicht wirklich 
verstanden hast.

Hier mal eine Musterlösung.

Und der klare Hinweis.

- rcall ruft Unterprogramme auf. Die müssen IMMEr mit ret  abgeschlossen 
werden.
- rjmp verzweigt im Programablauf. Damit sollte man sorgsam umgehen, um 
keinen Spaghetticode zu produzieren, so wie du im Moment.
- Wer STRINGs als Binärzahlen kodiert hat einen an der Waffel

.include "m8515def.inc"
.def temp=r16

    ldi   temp, LOW(RAMEND)
    out   SPL, temp
    ldi   temp, HIGH(RAMEND)
    out   SPH, temp
 
    ldi   temp, 0xFF    ; Port b = Ausgang
    out   DDRB, temp

    ldi   ZL, LOW(text*2)         ; Adresse des Strings in den
    ldi   ZH, HIGH(text*2)        ; Z-Pointer laden

    rcall lcd_flash_string

main:
    rjmp main                     ; Endlosschleife, nix mehr zu tun
  
text:   
  .db "Test",0   ; Stringkonstante, durch eine 0
                 ; abgeschlossen  

lcd_flash_string:
           push  temp
lcd_flash_string_1:
           lpm   temp, Z+
           cpi   temp,0
           breq  lcd_flash_string_2  
           out   PORTB,temp
           rjmp  lcd_flash_string_1 
lcd_flash_string_2:
           pop   temp
           ret

MfG
Falk

P.S. Wie kann man ein schönes, korrektes Beispiel aus dem tutorial nur 
so böse verstümmeln . . . ?

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ AVRFan (Gast)

>Diese beiden Zeilen...

>     ldi ZL, LOW(text*2)
>     ldi ZH, HIGH(text*2)

>gehören.. .

>lcd_flash_string:
>           push  temp

>           [--> hierhin! <--]

>So ist es.

Nööö, das ist schon OK. Z ist ein Parameter.

MFg
Falk

Autor: AVRFan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>das ist schon OK

Hmm, hast Du auch die Zeile

lpm   temp, Z+

gesehen?  Z wird dadurch hochgezählt, aber das sollte man nicht 
unbegrenzt machen.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ AVRFan (Gast)

>gesehen?  Z wird dadurch hochgezählt, aber das sollte man nicht
>unbegrenzt machen.

Das macht man auch nicht, wenn man prüft, ob das gelesene Zeicghen eine 
Null ist. Siehe meine korrigierte Version, oder gleich das Original 
hier.

http://www.mikrocontroller.net/articles/AVR-Tutori...

MfG
Falk

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nochmals vielen Dank für eure Ratschläge!

Sieht gut aus. Ich arbeite dann mal eure Texte gleich durch.


>- Wer STRINGs als Binärzahlen kodiert hat einen an der Waffel

Oder langeweile :D -> Binär war aus dem Grunde, weil das Steuerdaten für 
die LEDs sein sollten. Da sehe ich besser welche LEDs an bzw aus sein 
sollen/sind. Gibt es noch eine andere Möglichkeit Daten in Array zu 
schreiben und sie rauszuholen? Das war das einzige was ich gefunden 
habe. Eigentlich benötige ich statt der 0 (oder einem FF ) ein Zeichen 
was ncith in diesem Bereich liegt, denn ich kann dann diese 
Kombinationen der LED gar nciht mehr nutzen.

Später kommt da noch ein Multiplexer hinter, dann muss ich sowieso nicht 
so hoch zählen. Aber jetzt kann ich dann z.b. 0x00 oder 0b00000000 nicht 
nutzen, weil das natürlich mit cpi   temp,0 matched.

Autor: AVRFan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Das macht man auch nicht, wenn man prüft, ob das gelesene Zeicghen eine
>Null ist.

OK, aber wenn man die Z-Ladezeilen (ldi ZL... ZH...) in der 
Initialisierung ganz oben lässt, ist man auf einen einmaligen Aufruf 
von 'lcd_flash_string' beschränkt (ab dem zweiten Mal kommt Nonsens 
raus). Das halte ich für keinen guten Programmierstil.

Meine Idee:
lcd_out_text1:
   ldi  ZL, low(2*text1)
   ldi  ZH, low(2*text1)
   rjmp lcd_flash_string

lcd_out_text2:
   ldi  ZL, low(2*text2)
   ldi  ZH, low(2*text2)
   rjmp lcd_flash_string

lcd_out_text3:
   ldi  ZL, low(2*text3)
   ldi  ZH, low(2*text3)
   rjmp lcd_flash_string

lcd_out_text4:
   ...
   ...

lcd_flash_string:
   push  temp
   ...
   ...
   pop temp
   ret


Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  AVRFan (Gast)

>OK, aber wenn man die Z-Ladezeilen (ldi ZL... ZH...) in der
>Initialisierung ganz oben lässt, ist man auf einen einmaligen Aufruf
>von 'lcd_flash_string' beschränkt (ab dem zweiten Mal kommt Nonsens
>raus). Das halte ich für keinen guten Programmierstil.

Was ist beschränkt? Das ist ein einfaches grundgerüst, das sehr einfach 
und SICHER erweitert werden kann.

Und du musst wohl auch noch mal nde Unterschied zwischen rcall und rjmp 
durcharbeiten  . . .

>lcd_out_text1:
>   ldi  ZL, low(2*text1)
>   ldi  ZH, low(2*text1)
>   rjmp lcd_flash_string

@ Yatko Jaens (denim)

>Oder langeweile :D -> Binär war aus dem Grunde, weil das Steuerdaten für
>die LEDs sein sollten. Da sehe ich besser welche LEDs an bzw aus sein

Ok, das ist was anderes.

>sollen/sind. Gibt es noch eine andere Möglichkeit Daten in Array zu
>schreiben und sie rauszuholen? Das war das einzige was ich gefunden

Warum? Ist die nicht gut genug?

>habe. Eigentlich benötige ich statt der 0 (oder einem FF ) ein Zeichen
>was ncith in diesem Bereich liegt,

???
Welchen Bereich zwischen 0x00..0xFF willst sonst in einem BYTE nutzen?

>so hoch zählen. Aber jetzt kann ich dann z.b. 0x00 oder 0b00000000 nicht
>nutzen, weil das natürlich mit cpi   temp,0 matched.

Dann nimm eine andere Zeichen. Idealerweise wird das als Konstante 
definiert, das ist übersichtlich und einfach anzupassen.
.equ data_end = 0xC5

....

.db  1,2,3,4,5,data_end

. . .

     cpi   temp, data_end


Das gleiche kann man auch mit diversen LED-Zuständen machen, dann ist 
der Datensatz besser lesbar.

Mfg
Falk

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>sollen/sind. Gibt es noch eine andere Möglichkeit Daten in Array zu
>>schreiben und sie rauszuholen? Das war das einzige was ich gefunden
>
> Warum? Ist die nicht gut genug?

ok, dann scheine ich da auf dem richtigen weg zu sein

>>habe. Eigentlich benötige ich statt der 0 (oder einem FF ) ein Zeichen
>>was ncith in diesem Bereich liegt,
>
> ???
> Welchen Bereich zwischen 0x00..0xFF willst sonst in einem BYTE nutzen?

ich dachte ehr an sowas hier:
.db  1,2,3,4,5,"ENDE"
. . .

     cpi   temp, "ENDE"
aber das geht ja nicht, weil das nicht geht :)

Autor: AVRFan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Das ist ein einfaches grundgerüst, das sehr einfach
>und SICHER erweitert werden kann.

Na klar, ich hab ja ein Beispiel dafür gegeben.

>Und du musst wohl auch noch mal nde Unterschied zwischen rcall und rjmp

? (Alle "lcd_out_text..."-Routinen werden natürlich mit 'rcall' 
aufgerufen)

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ AVRFan (Gast)

>? (Alle "lcd_out_text..."-Routinen werden natürlich mit 'rcall'
>aufgerufen)

Und was soll das? Das bringt nur Kuddelmuddel und zusätzlichen 
Programmcode. Was spricht gegen die zwei ldi für den Z-Pointer gefolgt 
von dem rcall? Und das immer direkt, wenn irgendwo Text/Daten ausgegeben 
werden sollen.

@ Yatko Jaens (denim)

>ich dachte ehr an sowas hier:

>.db  1,2,3,4,5,"ENDE"

Schau dir mal mein letztes Posting an . . .

MfG
Falk

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Falk Brunner

>>.db  1,2,3,4,5,"ENDE"

>Schau dir mal mein letztes Posting an . . .

ja, aber dann kann ich kombination der LEDs 0xc5 nicht nutzen 
(0b11000101) weil das ja dann der stopper ist. mir würde ja ein 
buchstabe reichen aber die werden ja wohl automatisch umgerechnet. Also 
heisst das jetzt in klartext: man kann nur aus dem bereich 0x00 - 0xff 
ein "stopper" wählen, richtig (byte)?

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Yatko Jaens (denim)

>heisst das jetzt in klartext: man kann nur aus dem bereich 0x00 - 0xff
>ein "stopper" wählen, richtig (byte)?

Ja. Wenn das nicht reicht weil du alle 8 Bit im Byte für deine LEDs 
nutzen willst, kannst du

a) die einzelnen Werte mit 16 Bit kodieren, .dw 1234; dann hast du genug 
Kodes
b) die Anzahl der Daten an die erste Stelle des Datenblocks schreiben

MfG
Falk

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Falk

Danke:)

Ich wähle A, denn B kommt nicht in Frage, weil sich der Inhalt ändert 
(zur Zeit zwar nicht, aber später). Es sei denn es gibt sowas wie 
dw.length (wie bei JS oder PHP)

Zu A: wenn ich angenommen das Ganze mit .dw machen würde und mein 
stopper 0x100 sein würde, kommt bei -> "cpi  temp, 0x100" out of range 
weil der wahrscheinlich nur 8 bit kann. Wie könnte man denn dann ein 
16bit Wert prüfen?

Sorry, bin wirklich totaler anfänger... komme aus der PHP / JS Welt..

Schöne Grüße aus Dortmund!
Oktay

Autor: AVRFan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Was spricht gegen die zwei ldi für den Z-Pointer gefolgt
>von dem rcall?

Man kann die ldi-ldi-rjmp-Dreierpakete schön zusammen mit den .DB- oder 
.DW-Zeilen in eine "Daten"-Includedatei auslagern und danach 
"vergessen". Im Programmcode kommt man dann mit einer einzigen Zeile für 
den Aufruf aus. Ich nutze grundsätzlich immer (auch die kleinste) 
Möglichkeit aus, die einen Code einfacher zu überblicken und/oder 
leichter verstehbar macht.

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ AVRFan (Gast)

>>Gibt es da auf dem Brett eine Einstellung das man es umkehrt, also
>>bei LOW auch die entsprechende LED dunkel bleibt?
>
>Nein, zum Glück nicht.

also, ich hab das mit dem invertieren LEDs dann so gelöst:
com Temp

das klappt gut :)

Autor: AVRFan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>com Temp

Ich hätt's nicht anders gemacht :-)

Gute Nacht!

Autor: Yatko Jaens (denim)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
gute Nacht euch beiden und nochmals 1000 x Danke!

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Yatko Jaens (denim)

>Ich wähle A, denn B kommt nicht in Frage, weil sich der Inhalt ändert
>(zur Zeit zwar nicht, aber später). Es sei denn es gibt sowas wie
>dw.length (wie bei JS oder PHP)

Aber sicher.

daten_anfang:
.db (daten_ende-daten_anfang)*2, 1,2,3,4,5,6,7
daten_ende:

>weil der wahrscheinlich nur 8 bit kann. Wie könnte man denn dann ein
>16bit Wert prüfen?

AVR-Tutorial: Arithmetik

MfG
Falk

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.