PIC16 10-Hz-Timer mit Timer1 und 1-MHz-Uhr

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?

Wie viel 'Zeug' machen Sie in Ihren 'Timer'- und 'set_led'-Routinen?
Nicht viel, aber Sie haben Recht, dass sie irgendwie das Problem verursachen. Ich denke, das liegt daran, dass der Timer0 (höhere Frequenz) ISR oft läuft, wenn Timer1 umbricht, was die Drift verursacht.

Antworten (2)

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.

Perfekt, funktioniert super, danke. Ich habe prescale=10 verwendet und bis 10 gezählt, bevor ich die Ziffer erhöht habe. Außerdem musste ich 249 für den Nachladewert verwenden, was genau zu sein scheint. Ich denke, das Problem bei der Verwendung von Timer1 besteht darin, dass die ISR von Timer0 (höhere Frequenz) häufig ausgeführt wird, wenn Timer1 umbricht, was die Drift verursacht.

Anstatt Timer 1 zurückzusetzen, aktualisieren Sie den Alarm einfach so, dass er 0,1 Sekunden weiter in der Zukunft liegt