Hi, ich bekomme folgendes Problem nicht inden Griff: Wenn ich den Timer1 im Capture-Modus das erste mal frei schalte, löst er gleich einen Interrupt aus. Zwischendurch im Programm wird er dann mal wieder gesperrt und zurückgesetzt. Beim erneuten starten des Timers tritt der plötzliche Interrupt nicht mehr auf. Ich frage mich also warum der Timer1 nach einem Hardware-Reset beim ersten freischalten einen Interrupt gibt. Ein voriges setzen von "kein IRQ da" im TIFR hilft auch nicht. Momentan schaut das wie folgt aus: (einmalige Konfiguration beim Start des Programmes) ldi r16, high(15625) out OCR1AH, r16 ldi r16, low(15625) out OCR1AL, r16 ldi r16, 0b00010100 out TIMSK, r16 Später, wenn der Timer1 frei (und auf 0 zurück gesetzt wird) wird folgendes ausgeführt. cli ldi r16, 0b00000000 out TCNT1L, r16 out TCNT1H, r16 ldi r16, 0b00001100 out TCCR1B, r16 ldi r16, 0b11111111 out TIFR, r16 sei
Ich glaube mal was im Datenblatt zu dieser Vorgehensweise gelesen zu haben. Aktiviere den Interrupt im Zweifelfalle erst gleich nach Start des Timers. P.S.: ldi r16, 0b11111111 ist schlecht zu verstehen (zumal nichtmal kommentiert), lieber r16, (1<<XXX) | (1<<YYY) schreiben.
Generelle Fragen: - Welches Käferchen genau macht denn die Sorgen? - In Assembler bin ich nicht wirklich fit, aber macht es wirklich Sinn, in beide Register (H und L) den gleichen Wert zu laden? - Welchen Wert hat denn Dein Counter zu diesem Zeitpunkt (Initialisierung?) - Du weißt schon, daß Du im zweiten Block nicht mehr auf OCR1A zugreifst?
ldi r16, 0b11111111 out TIFR, r16 ist einfach nur alle Interruptzeigeregister auf 1 (also "kein Interrupt vorhanden") setzen
Lutz: - ATMega8 - H und L gehören zu dem 16-Bit Counterregister und müssen für "0" eben beide zurückgesetzt werden - Der Counter hat irgendeinen Wert. Deswegen wird er manuell auf 8 gebracht und DANN neu gestertet. - das B ist mir auch nicht schlüssig aber mit A geht es nicht.
Hmm, kann es daran liegen, dass sich die "Rücksetz- und Freischaltroutine" innerhalb eines interruptes (wird durch einen Interrupt ausgelöst) befindet und der Timer1 dann nach Aktivierung automatisch das I-Flag aus dem SREG Bearbeitet und deswegen auslöst?
Ein Interrupt wird immer nur dann ausgelöst, wenn sowohl I-Flag im SREG, als auch das Freischaltbit im Steuerregister des Timers und das betreffende Interruptflag im TimerFlagRegister gesetzt ist. Die sicherste Variante, einen Interrupt zu aktivieren, ohne daß er gleich auslöst, ist die Interruptursache auszuschließen und direkt vor der Aktivierung das Interruptflag im TimerFlagRegister durch das Schreiben von "1" (!) zu löschen. Wie sieht denn Dein gesamter Code aus? Die gezeigten Segmente sagen nicht viel aus.
Ich setze (wie marko das wohl meinte) vor der Freigabe eines Interrupts grundsätzlich die INT-Flags zurück. Wenn diese von der Harware nicht ständig gesetzt werden sollte das funktionieren.
> ldi r16, 0b00000000 > out TCNT1L, r16 > out TCNT1H, r16 Das geht schief! Bei 16-Bit-Registern muss immer das High-Byte zuerst geschrieben werden. Beim Lesen ist es umgekehrt.
Travel: Genau das habe ich auch versuchst, löschen aller Interrupte (oder deren Flags) Das Problem ist, dss der unerwünschet Interrupt NUR dann auftritt, wenn der Timer das erste Mal initalisiert wird (nach Anschalten der Baugruppe). Sobald das ein mal geschehen ist laufen weitere Interrupte (auch nach erneutem softwarezzurücksetzen) einwandfrei. Ich habe das Programm (600 Zeilen) auf das Wesentliche heruntergekürzt. ---------------------------------- .include "m8def.inc" ; ATMega8 Definitionsdatei ; ########## ORGANISATION UND INTERRUPTTABELLEN ########## .CSEG rjmp START ; Reset Einsprungmarke .ORG INT1addr rjmp IRQ_EVENTS ; IRQ Serviceprogramm .ORG OC1Aaddr rjmp CTC_MATCH ; Timer1 (16-Bit) Serviceprogramm ; ########## HAUPTPROGRAMMBEREICH ########## START: ; Begin Hauptprogramm (Stackinitaklisierung usw.) ; Konfiguriere Timer1 (16 Bit) für CTC-Modus ldi r16, high(15624) ; Lade Low-Byte der Vergleichzahl out OCR1AH, r16 ; Setze Low-Byte der Vergleichszahl ldi r16, low(15624) ; Lade High-Byte der Vergleichzahl out OCR1AL, r16 ; Setze High-Byte der Vergleichszahl ; Konfiguriere Interrupte ldi r16, 0b00001100 ; INT1 konfigurieren (steigende Flanke) out MCUCR, r16 ; Lade Konfiguration nach MCUCR ldi r16, 0b10000000 ; INT1 aktivieren out GICR, r16 ; Lade Konfiguration nach GICR ; Leerlaufschleife (wird vom Interrupt verlassen) loop: rjmp loop ; Springe ; Behandel den CTC-Timer Interrupt CTC_MATCH: (Schreibt sachen auf ein Display) (Geht nach Ablauf der zeit zu CTC_STOP) rcall CTC_STOP reti ; Springe zurück (und gebe wieder Interrupt frei) CTC_STOP: ldi r16, 0b00001000 ; Halte Timer an out TCCR1B, r16 ret ; Behandle die Taster-Interrupte IRQ_EVENTS: (tut diverses Zeugs mit Tastern, guckt welcher gedrückt wurde und geht bei einem bestimmten zur TIMER1-Routuine) TIMER: ldi r16, 0b00000000 out TCNT1L, r16 out TCNT1H, r16 ldi r16, 0b11111111 out TIFR, r16 ldi r16, 0b00010000 ; Lade "Interrupt bei Capture in Timer1" out TIMSK, r16 ; Setze Capture-Mode ldi r16, 0b00001100 ; Schalte Timer1 an out TCCR1B, r16 reti ; Springe zurück (und gebe wieder Interrupt frei)
@ marko (Gast) >Das Problem ist, dss der unerwünschet Interrupt NUR dann auftritt, wenn >der Timer das erste Mal initalisiert wird (nach Anschalten der >Baugruppe). Sobald das ein mal geschehen ist laufen weitere Interrupte Logisch, dann sieht die Capture-Logik eine Flanke. Machs einfach so. Timer initialisieren Capture Funktion initialisieren Capture interrupt freigeben Capture Interrut Flag löschen Interrupts global freigeben MFG Falk
Hi Falk In der Tat tue ich nichts anderes. Der sicherheit halber: - Vergleichwert nach OCR1A laden - Capture Flag für Timer1 Match A in TIMSK setzen - TIFR komplett (alle Flags) auf "1" setzen (Interrupflags Löschen) - Globale Interrupte freigeben (SEI) Irgendwann kommt dann der Punkt, an dem ein Tastendruck einen Interrupt auslöst. Innerhalb des Interruptes wird der TCNT1 auf 0 zurückgesetzt und danach der TCCR1B gestartet. Nach diversen CTC-Interrupten wird der TCCR1B wieder gesperrt. Die erneute Freigabe erfolgt wieder durch den Tastendruck. (und so weiter)
@ marko (Gast) >- Vergleichwert nach OCR1A laden >- Capture Flag für Timer1 Match A in TIMSK setzen >- TIFR komplett (alle Flags) auf "1" setzen (Interrupflags Löschen) >- Globale Interrupte freigeben (SEI) Sollte passen. >Irgendwann kommt dann der Punkt, an dem ein Tastendruck einen Interrupt >auslöst. Warum löst eine Taste einen Interrupt aus? Taste sollte man besser pollen. > Innerhalb des Interruptes wird der TCNT1 auf 0 zurückgesetzt >und danach der TCCR1B gestartet. ??? Was willst du mit TCCR1B starten? MfG Falk
Falk: Die Tastersache (nebensächlich): da sind 2 Taster: "Zeit rauf/runter" und "Start/Stop". Die Taster sind entprellt. Sie bestimmen aber wann die uUr (Timer im Sekundentakt) startet und damit auch wann ich den Timer lösche und starte oder eben stoppe. TCCR1B starte (und stoppe) ich , in dem ich bei den ersten 3 Bits (0-2, Teiler) eben "000" (Stop) oder "100" (Systemtakt durch 256) lade.
@ marko (Gast) >Die Tastersache (nebensächlich): da sind 2 Taster: >"Zeit rauf/runter" und "Start/Stop". Die Taster sind entprellt. Sie >bestimmen aber wann die uUr (Timer im Sekundentakt) startet und damit >auch wann ich den Timer lösche und starte oder eben stoppe. Dazu braucht man dennoch keinen Interrupt. Ein Pollen im 10ms Timerinterrupt ist vollkommen ausreichend. >TCCR1B starte (und stoppe) ich , in dem ich bei den ersten 3 Bits (0-2, >Teiler) eben "000" (Stop) oder "100" (Systemtakt durch 256) lade. Ahhh, du stopst und startest Timer1 DURCH TCCR1B! MfG Falk
Genau. Der Interrupt (timer) ist NICHT zum entprellen der Tasten da. Der Timer ist für den sekundentakt der Uhr zuständig. Un das Problem ist, dass bei der ersten Timeraktivierung (irgendwann nach dem Strom angelegt und der Start-Taster gedrück wurde) eben eine Sekunde sofort abgezogen wird. Das kommt daher, dass der Timer irgendwie beim ersten Mal sofort einen Interrupt auslöst. Bei allen nachfolgenden Durchläufen ist alles wie es sin soll. Das verstehe ich nicht. Irgendwo muss was sein, was einen Flag setzt. Da ist aber nichts.
@ marko (Gast) >Un das Problem ist, dass bei der ersten Timeraktivierung (irgendwann >nach dem Strom angelegt und der Start-Taster gedrück wurde) eben eine >Sekunde sofort abgezogen wird. Das kommt daher, dass der Timer irgendwie >beim ersten Mal sofort einen Interrupt auslöst. Bei allen nachfolgenden >Durchläufen ist alles wie es sin soll. Ahhhh, es dämmert. Ich glaube nicht, dass dein Timer einen Interrupt auslöst, das ist sehr systematisch und deterministisch. Aber wenn deine Tastenauswertung per Software auf einen Flanke reagiert, dann ist es durchaus möglich, dass deine Variablen für diese Tastenauswertung falsch initialisiert sind. Denn meist sind die Tasten Low aktiv, mit Pull-Ups. Deine Variablen werden aber meist mit Null initialisiert, wenn nich explizit was anderes gemacht wird. Die erste Abfrage sieht die Tasten jetzt auf HIGH (PINx) und wertet das als steigende Flanke, und löst dabei eine Aktion in deiner Software aus. >Das verstehe ich nicht. Irgendwo muss was sein, was einen Flag setzt. Da >ist aber nichts. In deiner Tastenauswertung. MfG Falk
Hmm, wäre schön, wenn das so wäre. Ich habe eben das Progrmm testweise umgebaut: es startet selbstständig (ohne Tastenaufforderung) die Timerinitalisierung. Die wartezeit vom Timer1 habe ich auf ca. 3 sekunden erhöhrt. Was passieren SOLLTE, ist, dass nach ca. 3 sekunden die Uhr eine Sekunde weiter geht. Sie tut es aber sofort nach dem einschalten, wartet also keine 3 Sekunden sondern macht sofort einen Interrupt. Was mich etwas verwirrt ist, dass ich TCCR1B (B) starte/stoppe aber den vergleichwert nach OCR1A (A) lade. Ich hab auch noch mal Im CTC-Clock Tutorial hier auf dem Board nachgeguckt. Das selbe Beispiel. Ansich würde ich davon ausgehen, dass ich versehens mit zwei verschiedenen Timern arbeite oder versehens Sachen in 2 verschiedenen Timerregistern speicher. Aber nach dem Erst-Aufruf-Fehler läuft ja alles so, wie es soll.
Ich habe mir inzwischen die Mühen gemacht und das Programm auf ein Minimum reduziert. Wie oben auch, also ohne Taster, startet das Programm bei dem Erstdurchlauf einen Interrupt ohne zu warten. Mir fällt nichts anderes meher ein.
>ldi r16, 0b00000000 >out TCNT1L, r16 >out TCNT1H, r16 Sieht Dein Code immer noch so aus? Das High-Register muß zuerst geschrieben werden. Ach: und welcher Interrupt wird ausgelöst? Du sprichst immer von Capture, hast aber den Compare (!) Interrupt aktiviert. Noch eine Idee: initialisiere die TimerRegister TCNT0H/L vor dem Löschen der InterruptFlags und vor dem Aktivieren der Interrupts mal auf 1.
@ marko (Gast) >Ich habe mir inzwischen die Mühen gemacht und das Programm auf ein >Minimum reduziert. Wie oben auch, also ohne Taster, startet das Programm >bei dem Erstdurchlauf einen Interrupt ohne zu warten. Na dann zeig mal den Quelltext. Als Anhang. MFG Falk
Hmm, ja, ich hab mir die Arbeit gemacht das noch mal auf einem anderen Weg zu schreiben (noch simpler und ohne Wartezeiten). Plötzlich ging es. Beim Vergleichen fiel der Groschen: in einer Subroutine ein pop vergessen... NARF! Danke für Eure tatkräftige Unterstützung! :)
Eine Heiseforum-"Weisheit" besagt: Mit "C" wäre das nicht passiert! :)
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.