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?
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! :)