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


von Yatko J. (denim)


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
1
.include "m8515def.inc"
2
.def temp=r16
3
4
   ldi   temp, LOW(RAMEND)
5
    out   SPL, temp
6
    ldi   temp, HIGH(RAMEND)
7
    out   SPH, temp
8
 
9
    ldi temp, 0xFF    ; Port b = Ausgang
10
    out DDRB, temp
11
12
    ldi ZL, LOW(text*2)         ; Adresse des Strings in den
13
    ldi ZH, HIGH(text*2)        ; Z-Pointer laden
14
15
  rjmp lcd_flash_string
16
main:
17
  
18
  
19
  out PORTB,temp
20
  rcall w0
21
22
ret
23
text:   
24
  .db 0b00000001,0b00000000,0b00000100,0b00001000,0b00010000,0b00100000,0b01000000,0b10000000
25
  .db 0b00011010,0b00110000,0b10001111,0b11111110,0b01100011,0b01000001,0b00111011,0b10010001,0xff                ; Stringkonstante, durch eine 0
26
                                       ; abgeschlossen  
27
28
29
lcd_flash_string:
30
           push  temp
31
 
32
lcd_flash_string_1:
33
           lpm   temp, Z+
34
           ;cpi   r30, 0x27
35
     cpi  temp, 0xff
36
           breq  lcd_flash_string_2
37
           rcall  main
38
           rjmp  lcd_flash_string_1
39
 
40
lcd_flash_string_2:
41
           pop   temp
42
           ret
43
44
45
46
47
w0:
48
push temp
49
;ret      PORTB 1 24
50
ldi    r18, 2
51
52
Warten:    ldi    r16, 200  ;r16 auf 250
53
    
54
Warten1:  ldi    r17, 250  ;r17 auf 250
55
    
56
Warten2:
57
    dec    r17      ;ziehe -1 von r17 ab
58
    brne  Warten2    ;wenn Z (r17) nicht 0 dann letzten befehl nochmal
59
    dec    r16      ;(r17 ist jetzt 0) ziehe -1 von r16 ab
60
    brne  Warten1    ;0?weiter:dec r16
61
    dec    r18
62
    brne  Warten
63
    pop temp
64
  ret


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

von Falk B. (falk)


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

von Yatko J. (denim)


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

von Falk B. (falk)


Lesenswert?

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

AVR-Tutorial: Stack

MfG
Falk

von Yatko J. (denim)


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

von Bernhard M. (boregard)


Lesenswert?

Du machst:
1
  rjmp lcd_flash_string
und von dort
1
   ret
das gibt den Stackunterlauf!

von Yatko J. (denim)


Lesenswert?

wie müsste denn der code korrekterweise aussehen?

von Yatko J. (denim)


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?

von AVRFan (Gast)


Lesenswert?

1
  ...
2
  ...
3
  rcall lcd_flash_string
4
5
main:
6
  out PORTB,temp
7
  rcall w0
8
9
   rjmp main
10
11
;-------------------------------------
12
13
text:   
14
  .db 0b00000001,0b00000000,0b00000100,0b00001000,0b00010000,0b00100000,0b01000000,0b10000000
15
  .db 0b00011010,0b00110000,0b10001111,0b11111110,0b01100011,0b01000001,0b00111011,0b10010001,0xff                ; Stringkonstante, durch eine 0
16
                                       ; abgeschlossen  
17
  ...
18
  ...

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

von AVRFan (Gast)


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.

von Yatko J. (denim)


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...
1
lcd_flash_string_2:
2
           pop   temp
3
4
     ldi ZL, LOW(text*2)         ; ist das schlimm wenn das hier andauernd wiederholt wird, oder gibt es einen anderen weg?
5
           ldi ZH, HIGH(text*2)        ; Z-Pointer laden
6
7
            rjmp  lcd_flash_string
8
      ;ret

von AVRFan (Gast)


Lesenswert?

Diese beiden Zeilen...
1
     ldi ZL, LOW(text*2)
2
     ldi ZH, HIGH(text*2)
gehören.. .
1
lcd_flash_string:
2
           push  temp
3
4
           [--> 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.

von Falk B. (falk)


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

1
.include "m8515def.inc"
2
.def temp=r16
3
4
    ldi   temp, LOW(RAMEND)
5
    out   SPL, temp
6
    ldi   temp, HIGH(RAMEND)
7
    out   SPH, temp
8
 
9
    ldi   temp, 0xFF    ; Port b = Ausgang
10
    out   DDRB, temp
11
12
    ldi   ZL, LOW(text*2)         ; Adresse des Strings in den
13
    ldi   ZH, HIGH(text*2)        ; Z-Pointer laden
14
15
    rcall lcd_flash_string
16
17
main:
18
    rjmp main                     ; Endlosschleife, nix mehr zu tun
19
  
20
text:   
21
  .db "Test",0   ; Stringkonstante, durch eine 0
22
                 ; abgeschlossen  
23
24
lcd_flash_string:
25
           push  temp
26
lcd_flash_string_1:
27
           lpm   temp, Z+
28
           cpi   temp,0
29
           breq  lcd_flash_string_2  
30
           out   PORTB,temp
31
           rjmp  lcd_flash_string_1 
32
lcd_flash_string_2:
33
           pop   temp
34
           ret

MfG
Falk

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

von Falk B. (falk)


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

von AVRFan (Gast)


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.

von Falk B. (falk)


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-Tutorial:_Speicher#Flash-ROM_2

MfG
Falk

von Yatko J. (denim)


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.

von AVRFan (Gast)


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:
1
lcd_out_text1:
2
   ldi  ZL, low(2*text1)
3
   ldi  ZH, low(2*text1)
4
   rjmp lcd_flash_string
5
6
lcd_out_text2:
7
   ldi  ZL, low(2*text2)
8
   ldi  ZH, low(2*text2)
9
   rjmp lcd_flash_string
10
11
lcd_out_text3:
12
   ldi  ZL, low(2*text3)
13
   ldi  ZH, low(2*text3)
14
   rjmp lcd_flash_string
15
16
lcd_out_text4:
17
   ...
18
   ...
19
20
lcd_flash_string:
21
   push  temp
22
   ...
23
   ...
24
   pop temp
25
   ret

von Falk B. (falk)


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.
1
.equ data_end = 0xC5
2
3
....
4
5
.db  1,2,3,4,5,data_end
6
7
. . .
8
9
     cpi   temp, data_end

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

Mfg
Falk

von Yatko J. (denim)


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:
1
.db  1,2,3,4,5,"ENDE"
2
. . .
3
4
     cpi   temp, "ENDE"
aber das geht ja nicht, weil das nicht geht :)

von AVRFan (Gast)


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)

von Falk B. (falk)


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

von Yatko J. (denim)


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

von Falk B. (falk)


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

von Yatko J. (denim)


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

von AVRFan (Gast)


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.

von Yatko J. (denim)


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:
1
com Temp

das klappt gut :)

von AVRFan (Gast)


Lesenswert?

>com Temp

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

Gute Nacht!

von Yatko J. (denim)


Lesenswert?

gute Nacht euch beiden und nochmals 1000 x Danke!

von Falk B. (falk)


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.

1
daten_anfang:
2
.db (daten_ende-daten_anfang)*2, 1,2,3,4,5,6,7
3
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

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.