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.
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.
Also um mehr sagen zu können, reicht das Schnipselchen oben nicht aus.
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.
1 | in SRSK, SREG |
2 | |
3 | |
4 | ;.... |
5 | |
6 | out SREG, SRSK |
So sieht das immer beim Hannes Lux aus.
Ok, danke die Herrn! Klappt jetzt ganz gut. Man lernt hier halt jede Minute dazu!:D
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).
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.