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!
> ...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.
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.
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.
...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...
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
'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.
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.
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
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.
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!
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
> ...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...
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).