Forum: Mikrocontroller und Digitale Elektronik AVR CTC Timer dauert unterschiedlich lange?


von Amilio (Gast)


Lesenswert?

Guten Abend,

am ATtiny45 habe ich einen 32.768-kHz-Quarz. Den Timer 0 habe ich auf 
CTC konfiguriert mit einem Vorteiler von 256. Den Vergleichswert habe 
ich auf 127 gesetzt, um einen sekündlichen Interrupt auszulösen (1 Hz). 
Auf den Mikrocontroller programmiert, funktioniert das scheinbar wie 
gewünscht (Sekundenzeiger meiner Armbanduhr ist im Takt mit der 
blinkenden LED).

Im Debugger (Atmel Studio 6.2) hingegen mit dem Simulator als Gerät, 
habe ich einen Breakpoint am Anfang der Interrupt-Routine. In der 
Prozessor-Ansicht bilde ich die Differenz der Cycle-Counter-Werte 
zwischen den einzelnen Interrupts und erwarte konstant 32768. Das ist 
aber nicht der Fall. Es ist immer abwechselnd 32769 bzw. 32767. Im 
Schnitt passt das, aber warum ist die Differenz nicht immer konstant 
32768?

Hier noch ein Minimalbeispiel (Breakpoint auf reti setzen):
1
.include "tn45def.inc"
2
3
.def temp0 = R16
4
5
.org 0
6
    rjmp    init
7
.org OC0Aaddr
8
    rjmp    TIMER0_COMPA
9
10
.cseg
11
12
init:
13
    ldi     temp0, HIGH(RAMEND)
14
    out     SPH, temp0
15
    ldi     temp0, LOW(RAMEND)
16
    out     SPL, temp0
17
    ldi     temp0, (1<<WGM01)   // CTC
18
    out     TCCR0A, temp0
19
    ldi     temp0, (1<<CS02)    // prescaler 256
20
    out     TCCR0B, temp0
21
    ldi     temp0, 128          // timer compare 127: 128*256=32768 -> 1Hz
22
    out     OCR0A, temp0
23
    ldi     temp0, (1<<OCIE0A)  // compare match A interrupt enable
24
    out     TIMSK, temp0
25
    ldi     temp0, (1<<OCF0A)   // output compare Flag 0 A
26
    out     TIFR, temp0
27
    sei
28
29
main:
30
    rjmp main
31
32
TIMER0_COMPA:
33
    reti


Danke schon einmal und einen schönen Abend!

von spess53 (Gast)


Lesenswert?

Hi

Der Interrupt wird erst angesprungen wenn das Programm einen Befehl 
fertig ausgeführt hat. Da die Befehlslänge unterschiedlich ist ergeben 
sich auch unterschiedliche Zeiten für den Interrupt.

MfG Spess

von c-hater (Gast)


Lesenswert?

Amilio schrieb:

> zwischen den einzelnen Interrupts und erwarte konstant 32768. Das ist
> aber nicht der Fall. Es ist immer abwechselnd 32769 bzw. 32767. Im
> Schnitt passt das, aber warum ist die Differenz nicht immer konstant
> 32768?

Das nennt sich variable Interruptlatenz. Die setzt sich aus mehreren 
Komponenten zusammen, in deinem Falle ist aber nur eine davon wirksam, 
nämlich die Ausführungszeit der "unterbrochenen" Instruktion (die in 
Wirklichkeit eben nicht unterbrochen wird), in deinem Fall das rjmp, 
welches allein die Hauptschleife bildet. Je nachdem, wann der Interrupt 
zuschlägt, wird er entweder sofort ausgeführt oder erst zwei Takte 
später, eben um die Laufzeit der rjmp-Instruktion verspätet.

In deinem einfachen Fall könntest du das kompensieren, indem du einfach 
in der ISR ein nop einfügst, dann erwischt der Interrupt die 
Hauptschleife relativ immer im gleichen Moment und die variable Latenz 
verschwindet. Das funktioniert so aber wirklich nur in sehr einfachen 
Fällen, wo alle Instruktionen in der Hauptschleife genau zwei Takte lang 
sind. Normalerweise hat man da aber Instruktionen mit unterschiedlichen 
Laufzeiten drin, von einem Takt bis zu vier oder u.U. (bei den großen 
AVRs oder denen mit externem RAM) sogar noch mehr.

Dazu können dann noch die anderen Quellen kommen, die zur variablen 
Interuptlatenz beitragen, das sind cli/sei-Blöcke in der Hauptschleife 
und konkurrierende Interrupts. Diese Sachen können die variable Latenz 
leider sehr hoch treiben.

Wenn du unter solchen Bedingungen die ISR synchron zum 
Timer-Interruptereignis bekommen willst, mußt du das aktiv mittels 
entsprechendem Code in der ISR machen. Leider verbrauchst du dabei dann 
in der ISR immer mindestens so viel Rechenzeit, wie die variable Latenz 
im schlimmsten Fall dauert und diese Rechenzeit ist wirklich verloren, 
während die variable Latenz selber ja nicht verloren ist, weil etwas 
sinnvolles in dieser Zeit passiert.

Routinen, die solche Synchronisierung leisten können, wurden hier im 
Forum vor etwa drei Monaten in einem Thread ausgiebig diskutiert. Mußt 
du mal die Suche bemühen, ich kann mich leider nicht an den Titel des 
Threads erinnern.

von Peter D. (peda)


Lesenswert?

Amilio schrieb:
> ldi     temp0, 128          // timer compare 127: 128*256=32768 ->
> 1Hz

Du postest offensichtlich nicht den Code, den Du simuliert hast!

von Amilio (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Du postest offensichtlich nicht den Code, den Du simuliert hast!

Doch. Vor dem Post habe ich noch andere Werte probiert und vergessen, es 
wieder auf 127 zu setzen - mein Fehler. Es verhält sich mit anderen 
Werten analog und dank der anderen Posts weiss ich jetzt auch warum.

Für meine Uhranwendung ist der genaue Takt nicht notwendig. Ich wollte 
nur wissen, warum es da die kleinen Abweichungen gibt.

Also danke noch einmal an alle!

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
Noch kein Account? Hier anmelden.