Forum: Mikrocontroller und Digitale Elektronik Problem: Interrupt + flags


von Hannes Neuling (Gast)


Lesenswert?

Hallo!
Nach stundenlangem Gesuche habe ich ein Problem entdeckt welches mich 
ziemlich überascht, da es ein grundsätzliches Problem zu sein scheint. 
Situation:

Tasten abfragen. "Entprellungsroutine" aus dem Tutorial hier im Forum: 
Lesen, warten, nochmal lesen und ggf. Ausführung. Das ganze für fallende 
Flanke bei active low. Das ist zwar weder robust noch elegant hat aber 
eigentlich funktioniert bis ich auf die Idee gekommen bin den Timer 
Interrupt von 1s auf 0,5s runterzudrehen (da bei mir was blinkt, und 1s 
blinken war mir zu langsam). Jetzt das Problem: bei gedrückter Taste 
wird die dazu gehörige Routine zig-fach aufgerufen....Also hab ich alles 
noch mal durch geschaut und nichts entdeckt (vorher hatte es ja 
funktioiert und sonst hatte ich nichts geändert). Schnell habe ich also 
bemerkt dass es mit dem Timer Interrupt zu tun hatte und nach Schritt 
für Schritt Debug gesehen, dass der Fehler daran liegt, dass RETI die 
flags nicht widerherstellt. Ich bin zwar kein grosser Spezialist, aber 
sollte der das nicht machen?

Bei mir sieht das so aus:

...

mov  temp1, key_now      ; und in temp1 sichern
eor  key_now, key_old    ; mit dem vorhergehenden Zustand XOR
mov  key_old, temp1      ; und den jetzigen Zustand für den nächsten
                         ; Schleifendurchlauf als alten Zustand merken

breq end_ta              ; Das Ergebnis des XOR auswerten:
                         ; wenn keine Taste gedrückt war -> neuer
                         ; Schleifendurchlauf

and  temp1, key_now      ; War das ein 1->0 Übergang, wurde der Taster 
also
                         ; gedrückt (in key_now steht das Ergebnis vom 
XOR)
...

Wenn der Interrupt jetzt beim 2. 'mov' ausgeführt wird, verliert sich 
das ergebnis der XOR, sprich die Flags, und statt zu 'end_ta' zu 
branchen macht der einfach weiter -> Der Tastendruck wird mehrmals 
ausgewertet.

Ich weiss schon, dass ich das ganze auch anders machen kann usw. aber 
das ist ja ein grundsätzliches Problem, oder?

Wäre dankbar für etwas Aufklärung und Hilfe!

von Johannes M. (johnny-m)


Lesenswert?

> ...dass RETI die flags nicht widerherstellt...
Welche Flags? reti setzt lediglich das I-Bit im SREG und lädt den 
Programmzähler mit der um eins inkrementierten, auf dem Stack 
befindlichen Rücksprungadresse.

Und zur Entprellung: Du musst bei einer Abfrage (Einlesen der 
betreffenden Pins) den Zustand speichern und bei der jeweils nächsten 
Abfrage mit dem dann aktuellen Zustand vergleichen. Dann kannst Du den 
Taster drücken, so lange Du willst, es wird trotzdem nur einmal die 
entsprechende Aktion durchgeführt.

EDIT:
Sehe gerade, dass Du das mit der Flankenerkennung ja schon machst.

von Simon K. (simon) Benutzerseite


Lesenswert?

Meinst du die Prozessor-Flags im SREG? Damit hat RETI nichts zu tun. 
RETI sorgt nur dafür, dass der Prozessor an der unterbrochenen Stelle 
fortsetzt.

von Johannes M. (johnny-m)


Lesenswert?

Also um mehr sagen zu können, reicht das Schnipselchen oben nicht aus.

von Hannes Neuling (Gast)


Angehängte Dateien:

Lesenswert?

Verstehe ich nicht. Wieso hat der nichts damit zu tun? Der unterbricht 
an irgendeiner Stelle wo vielleicht, wie in meinem Fall, gerade Flags 
gesetzt wurden, verändert diese, und beim zurückspringen werden Falsche 
Flags ausgewertet....

Im Anhang ist die Tastenabfrage Routine, aber ich denke dass das Problem 
trotzdem grundsätzlicher Art ist, da es ja auch an einer anderen Stelle 
passieren kann.

von Karl H. (kbuchegg)


Lesenswert?

Hannes Neuling wrote:
> Verstehe ich nicht. Wieso hat der nichts damit zu tun?

Der RETI hat damit aber überhaupt nichts zu tun :-)
Der RETI kümmert sich nur darum, das Rückgängig zu machen
was beim Aufruf (dem impliziten Call) gemacht wurde:
Nämlich die Rücksprungadresse vom Stack zu holen und
wieder dorthin zu springen.

> Der unterbricht
> an irgendeiner Stelle wo vielleicht, wie in meinem Fall, gerade Flags
> gesetzt wurden, verändert diese, und beim zurückspringen werden Falsche
> Flags ausgewertet....

Ja und.
Du bist der Programmierer. Wenn in deiner Interruptroutine
die Flags verändert werden, dann ist es deine verd.... Pflicht
dafür zu sorgen, dass das SREG Register beim Eintritt in
die Interrupt Routine gesichert und beim Verlassen wieder
hergestellt wird.
Wenn deine Interrupt Routine keine Flags verändert, dann
braucht das auch nicht gemacht werden.

Der Übergang von einer Hochsprache zu Assembler ist wie
der Übergang vom Knaben zum Mann: Ab sofort ist nicht mehr
die Mutti da, die hinter dir wegräumt. Ab sofort musst du
schon selbst dafür sorgen, dass Ordnung herrscht.

von Matthias (Gast)


Lesenswert?

...zt wurden, verändert diese, und beim...


Wenn du in einer Interrupt-Routine Flags/Register veränderst, dann musst 
DU SELBST dafür sorgen, dass diese vor em Ändern gesichert, und vor dem 
Verlassen der Interrupts wieder hergestellt werden.
push & pop sind hier die Befehle...

von Danilo (Gast)


Lesenswert?

Hallo Hannes Neuling,

wie Johannes M. schon bemerkt hat...RETI holt die Rücksprungadresse vom 
Stack und setzt das I-Bit im SREG. Um die anderen Flags mußt Du Dich in 
Deiner Interrupt-Routine selber kümmern. Also PUSHen und POPen.

Danilo

von Hannes Neuling (Gast)


Lesenswert?

'Türlich bin ich der Programmierer, aber ich muss dem ja bei einem call 
auch nicht sagen das er die Rücksprungposition speichern soll. Das macht 
der schon selber. Aber ist schon klar. Nur denke ich mich sehr dunkel 
daran aus dem Untericht zu erinnern, dass das beim interrupt so war: Bei 
Interrupt wir Rücksprung und Flags gespeichert, mit reti wieder alles 
hergerichtet. War ein anderer uC, kann mich aber trotzdem täuschen.

von Simon K. (simon) Benutzerseite


Lesenswert?

Hannes Neuling wrote:
> 'Türlich bin ich der Programmierer, aber ich muss dem ja bei einem call
> auch nicht sagen das er die Rücksprungposition speichern soll. Das macht
> der schon selber. Aber ist schon klar. Nur denke ich mich sehr dunkel
> daran aus dem Untericht zu erinnern, dass das beim interrupt so war: Bei
> Interrupt wir Rücksprung und Flags gespeichert, mit reti wieder alles
> hergerichtet. War ein anderer uC, kann mich aber trotzdem täuschen.

1. Die Rücksprungadresse zu speichern ist essenziell, wenn es danach 
wieder zur aufgerufenen Stelle gehen muss. Ansonsten kannst du statt 
call auch jmp benutzen, das ist das gleiche, nur ohne die speicherung 
der rücksprungadresse,

2. µCs sind verschieden. Es gibt welche, die das machen, und welche, die 
das nicht machen.

von Hannes Neuling (Gast)


Lesenswert?

Aha, hab' ich's doch gewusst;D Na dann muss ich mal aufpassen, solche 
Details können einen Verrückt machen bei der Fehlersuche...

Übrigens, bin schon ganz gaga, push sreg funzt nicht.....:S

von Simon K. (simon) Benutzerseite


Lesenswert?

Hannes Neuling wrote:
> Aha, hab' ich's doch gewusst;D Na dann muss ich mal aufpassen, solche
> Details können einen Verrückt machen bei der Fehlersuche...
>
> Übrigens, bin schon ganz gaga, push sreg funzt nicht.....:S

Reservier am besten ein eigenes Register für die Sicherung des SREGs.
1
in SRSK, SREG
2
3
4
;....
5
6
out SREG, SRSK

So sieht das immer beim Hannes Lux aus.

von Hannes Neuling (Gast)


Lesenswert?

Ok, danke die Herrn!
Klappt jetzt ganz gut. Man lernt hier halt jede Minute dazu!:D

von Rudi (Gast)


Lesenswert?

Der Unterschied zwischen RET und RETI sollte doch eigentlich sein das 
letzteres zuzüglich zum Laden der Rücksprungadresse vom Stack noch den 
Interrupt demaskiert und das Flag zurücksetzt? Normalerweise sollte der 
Interrupt während der Serviceroutine gesperrt sein und sein Flag gesetzt 
sein!

von Karl H. (kbuchegg)


Lesenswert?

Hannes Neuling wrote:
> Aha, hab' ich's doch gewusst;D Na dann muss ich mal aufpassen, solche
> Details können einen Verrückt machen bei der Fehlersuche...

Drum gibts auch Hochsprachen. :-)
Da muss man sich um solche Details dann nicht mehr kümmern.

>
> Übrigens, bin schon ganz gaga, push sreg funzt nicht.....:S

Wenns mit den Registern knapp wird (oder ich eine 100%
full prove Funktion fürs Tutorial brauche, dann sieht
das bei mir so aus:

    push temp
    in   temp, SREG
    push temp


;....

    pop  temp
    out  SREG, temp
    pop  temp

von Johannes M. (johnny-m)


Lesenswert?

> ...aber ich muss dem ja bei einem call
> auch nicht sagen ...
Jetzt mal abgesehen von der Rücksprungadresse, die in jedem Fall 
gespeichert werden muss, gibt es da einen ganz gewaltigen Unterschied, 
der unbedingt zu beachten ist: Ein call tritt an einer ganz bestimmten 
Stelle im Programm auf. Da hat der Programmierer die volle Kontrolle 
über Flags und Abläufe. Deshalb muss bei einem call i.d.R. auch nicht 
viel gesichert werden.

Ein Interrupt hingegen kann jederzeit an jeder beliebigen Stelle im 
Programm dazwischenhauen. Da hat der Programmierer keine Kontrolle. 
Deshalb müssen nach dem Einsprung in den Interrupt Handler alle Flags 
und Register, die eventuell wichtig für den weiteren Programmablauf sein 
könnten und die im Interrupt Handler verändert werden könnten, gesichert 
und vor dem Rücksprung wiederhergestellt werden, sonst kann das zu 
Problemen führen.

Und wie Simon schon sagte: Es gibt µCs, die beim Auftreten eines 
Interrupt-Ereignisses das Statusregister automatisch sichern, und es 
gibt welche, die das nicht tun. Die AVRs gehören zu den letzteren...

von Johannes M. (johnny-m)


Lesenswert?

Rudi wrote:
> Der Unterschied zwischen RET und RETI sollte doch eigentlich sein das
> letzteres zuzüglich zum Laden der Rücksprungadresse vom Stack noch den
> Interrupt demaskiert und das Flag zurücksetzt? Normalerweise sollte der
> Interrupt während der Serviceroutine gesperrt sein und sein Flag gesetzt
> sein!
Das auslösende Flag wird bereits beim Einsprung in den Interrupt Handler 
automatisch gelöscht (Der Vollständigkeit halber die Ausnahme: Das UART 
RXC-Flag, das erst beim Lesezugriff auf UDR gelöscht wird). Zusätzlich 
löscht die Hardware das I-Bit im SREG. Der einzige Unterschied zwischen 
ret und reti ist, dass letzteres eben das I-Bit wieder setzt.

Tritt während der Abarbeitung des Interrupt das auslösende Ereignis 
erneut auf, dann wird auch das betreffende Flag wieder gesetzt und der 
Handler wird nach seiner Beendigung erneut aufgerufen, sobald er dran 
ist (also wenn keine anderen Interrupts anstehen, deren Vektoren an 
niedrigeren Adressen in der Vektortabelle stehen).

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.