Hi
Ich habe den Quellcode im Anhang geschrieben, der auch soweit
funktioniert, jedoch nur wenn das "sei" im Label sek steht.
Die Aufgabe des Codes ist nach jedem Timer0 Overflow Interrupt einen
Zähler zu erhöhen und wenn dieser den Wert 31 hat einen weiteren Zähler
um eins erhöhen (dies geschieht immer so nach ca. 1 sekunde)
Wenn ich dass besagte "sei" entferne funktioniert der Code für exakt
eine Sekunde, danach nicht mehr. Wenn das "sei" an dieser Stelle ist
funktioniert der Code einwandfrei.
Was bewirkt das "sei" an dieser Stelle?
Ich freue mich auf eure Hilfe
Sichere mal Dein SREG, bevor Du die eigentliche
Interrupt-Service-Routine ausführst, nach der ISR mußt Du´s wieder
rücksichern. Also:
in Temp, SREG
push Temp
.....
.....
pop Temp
out SREG, Temp
reti
Weil im sek Teil direkt (ohne reti) in die Endlosschleife gesprungen
wird, was zum einen dazu führt das es irgendwann garantiert zu einem
Stackoverflow kommt, zum anderen wird (ohne reti) das globale
Interruptflag nicht autom. gesetzt.
In dem Fall, wo du "sek" anspringst, beendest du deine ISR nicht mehr
mit "reti". Das hat einerseits den Effekt, dass das I-Flag im SREG
nicht wieder gesetzt wird und dadurch kein Interrupt mehr abgearbeitet
wird.
Dein "sei" täuscht nur vor, dass es funktioniert, aber dein Stack
läuft über, weil kein RETI erreicht wird, dass die Rücksprungadresse
des Int-Aufrufs wieder vom Stack nimmt.
Du musst deine ISR so schreiben, dass sie immer mit RETI beendet wird.
Der Übersichtlichkeit halber nimmt man dazu immer das selbe RETI. Denn
wenn das Programm etwas größer wird (wenn es eine Mainloop bekommt),
dann musst du in der ISR noch das SREG sichern und ggf lokale Variablen
beschaffen, indem du Registerinhalte vorübergehend auf Stack legst (PUSH
/ POP). Der Stack muss dabei immer ausgewogen sein, jedes PUSH braucht
sein POP, jedes (R)Call sein RET und jeder Int-Aufruf sein RETI.
Falls du mal übern Zaun schaun willst, dann schau mal hier:
www.hanneslux.de/avr
...
Danke für die schnelle Hilfe!
Das Problem mit dem Stapelüberlauf habe ich verstanden, nur sehe ich
das irgendwie bei der simulation im Studio nicht?
Ich sehe auch keine Möglichkeit, wie ich dass umgehen kann (Ich bin
totaler neuling)
in Temp, SREG
push Temp
.....
.....
pop Temp
out SREG, Temp
reti
Wie kann mir das Helfen?
Dieses Fragment sollte in jeder ISR (Interruptserviceroutine) stehen,
für die Pünktchen setzt Du den eigenlichen Code ein, den die ISR
abarbeiten soll. Verwendest Du dann in der ISR auch noch andere
Variablen, die Du auch im Hauptprogramm benutzt, so mußt Du diese
anfangs auch push-en und am Ende der ISR wieder pop-en, damit nach dem
Interrupt alles so ist, wie es vorher war.
ok und wie mach ich das wenn ich wie in meinem Code ohne reti aus der
ISR rausspringe? dann hab ich ja dennoch dass gleiche Problem wenn ich
dass richtig verstanden habe.
Das darfst Du nicht! RETI ist IMMER das letzte, was eine ISR ausführen
muß! Wenn Du aus der ISR ´raus willst, mußt Du immer noch die ganzen
POP-s und das RETI ausführen lassen, sonst raucht Dein Stack ab!
in Temp, SREG
push Temp
.....
.....
pop Temp
out SREG, Temp
reti
Das ist natürlich Quark :-), es sei denn, Temp wird ausschliesslich in
ISRs verwendet.
Vollständig muss es so aussehen:
push Temp
in Temp, SREG
push Temp
.....
.....
pop Temp
out SREG, Temp
pop Temp
reti
Ich habe immer gerne ein eigenes Register für die SREG-Sicherung
benutzt.
.def sreg_bak=R2
ISR:
in sreg_bak, SREG
.
.
out SREG, sreg_bak
reti
Das bietet sich an, wenn man noch freie Register hat oder es auf
kürzeste Interrupt-Laufzeit ankommt. Bei den RAM-losen Typen geht es
nur so.