Ich muss eine Stoppuhr bei 10 Hz mit Timer1 auf einem PIC16F628 erhöhen. Der externe Takt beträgt 1 MHz und wird von einem verpackten Oszillator (EPSON sg8002db) geliefert. Ohne Prescaler sollte der Wert zum Einstellen des Timers (glaube ich) sein:
Uhr tickt in 1 Sekunde: 1000000
Timer tickt in 1 Sekunde: 250000 (Uhr/4)
Timer-Ticks in 1/10 Sekunde: 25000 (Timer-Ticks/10)
Also: 65536 - 25000 = 40536
aber ich muss die Latenzzeit vom Timer-Überlauf bis zum Zurücksetzen des Uhrwerts berücksichtigen - dies ist die Anzahl der Zyklen, die vom Überlauf bis zum Einstellen des Timer-Werts benötigt werden.
Der IRQ-Code lautet:
irq movwf w_temp ; save state
swapf STATUS, w
clrf STATUS
movwf status_temp
movf PCLATH, w
movwf pclath_temp
clrf PCLATH
btfss PIR1,TMR1IF ; timer1 IRQ?
goto notimer1
bcf PIR1,TMR1IF ; yes, clear it
movLw T1SPEED >> 8 ; reset timer1
movwf TMR1H
movLw T1SPEED & 0xff
movwf TMR1L ; timer1 is off and running again
call timer ; increment clock
notimer1 btfss INTCON, T0IF ; timer0 IRQ?
goto notimer0
bcf INTCON, T0IF ; yes, clear it
call led_set ; update display
btfss PORTA,6 ; button pressed?
goto nobut
clrf digit0 ; yes, reset clock
clrf digit1
clrf digit2
clrf digit3
nobut
notimer0 movf pclath_temp, w ; restore state
movwf PCLATH
swapf status_temp, w
movwf STATUS
swapf w_temp, f
swapf w_temp, w
retfie
Was für mich irgendwo im Bereich von etwa 14 Zyklen aussieht, also LATENCY = 14
65536 - (25000 - LATENZ) = 40550
Dies ergibt jedoch eine viel zu langsame Uhr, die mehrere Sekunden pro Minute verliert. Wenn ich den LATENCY-Wert auf ~ 200 ändere (z. B. Timer1 auf 40736 setze), ist es nah - innerhalb von 1 Sekunde pro Minute, aber immer noch nicht genau. Tatsächlich ist es mit LATENCY = 199 zu schnell und mit LATENCY = 200 langsam.
Ich kann nicht sehen, wo diese zusätzlichen Zyklen ausgegeben werden - es setzt den Taktwert als erstes in der IRQ-Routine. Ich kann im Datenblatt nichts darüber finden, dass Timer1 während einer Interrupt-Routine gestoppt wird, aber ist es so? Wenn dem so ist, wäre das schade, weil die Routine abhängig davon, welche Ziffern überlaufen, eine variable Anzahl von Zyklen benötigt.
Ist es notwendig, 2 verschiedene Nachladewerte auszuwählen und zwischen ihnen zu wechseln, um genau 10 Hz zu treffen?
Anstatt den 16-Bit-Timer 1 zu verwenden, der ein erneutes Laden des Werts im Code erfordert, verwenden Sie den 8-Bit-Timer 2, der ein voreingestelltes Register PR2 hat. Laden Sie PR2 mit 250, mit einem Prescaler-Wert von 1:1. Es wird dann alle 1 ms unterbrochen und automatisch neu geladen, sodass keine Latenzprobleme auftreten.
Inkrementieren Sie innerhalb Ihres Interrupts einfach einen einzelnen Byte-Zähler. Verwenden Sie in Ihrer Basisroutine eine While-Schleife und testen Sie, wann immer der Zähler gleich 100 ist (dh 1/10 Sekunde ist abgelaufen). Setzen Sie dann den Zähler zurück und aktualisieren und setzen Sie Ihre Uhr / LED dort in der Basisebene zurück, nicht in Ihrer Interrupt-Routine.
Anstatt Timer 1 zurückzusetzen, aktualisieren Sie den Alarm einfach so, dass er 0,1 Sekunden weiter in der Zukunft liegt
brhans
Charlie Skilbeck