ATtiny2313 Timer1 Problem

OP #627111
Lesenswert?

Moin,

ich hab da mal ne Frage zum Timer1 des ATtiny2313.
Also ich habe ein Programm geschrieben, welches den Timer1 so benutzt, 
dass er rein rechnerisch nach 100ms einen Overflow auslösen müsste.

Es wird der interne Quarz als Taktquelle benutzt (1Mhz).

Also
1Mhz entspricht 1 µs

Daraus folgt 100ms entsprechen 100 000 Zähltakten mit Prescaler 1. Mit 
Prescaler 256 sind es rund 391 Zähltakte. Da Timer1 aufwärts zählt macht 
das also einen Startwert von 65145.

Merkwürdigerweise machen das meine Tinys (hab 12 Stück) aber nicht. Bei 
mir dauert es statt 100ms knappe 17s (man beachte den Faktor von 170!) 
im ersten Durchlauf. Danach funktioniert das Programm wie gewünscht.

Woran kann das liegen?

Die Programmstruktur ist:
Interrupt für Timer1 aktivieren
Interrupts generell zulassen
Startzeit für Timer 1 laden
Timer1 starten
Endlosschleife

Interrupt Timer 1:
Register sichern
Timer 1 stoppen
Irgendwas machen
Startzeit für Timer 1 laden (kann von 100ms Laufzeit abweichen)
Timer1 starten
Register wieder herstellen
Interrupt beenden

PS.: Die Zeit von 17s hab ich mit einem Oszi ermittelt.
OP #627676
Lesenswert?

@Andreas Kaiser:
Offensichtlich hast du recht, aber wieso?

@TIMER_MAN:
Bascom benutze ich nicht, ausserdem scheinen die Berechnungen ja zu 
stimmen (zumindest im zweiten Durchlauf, siehe Programmcode).

@Läubi:
Nein die Fuse ist nicht gesetzt, also CLKDIV = 1, damit läuft der Tiny 
mit 1MHz, sonst mit 8MHz, siehe Manual auf Seite 25: Default Clock 
Source; Ebenfalls mit Oszi überprüft.

@Johannes M.:
Könntest du das näher erläutern? Was wäre denn der Unterschied ob ich 
von 0 bis x zähle oder von x bis 65535 und den Overflow nutze? Ausserdem 
ist das nicht ganz ohne, wie die Erläuterung auf Seite 99 des Handbuchs 
zeigt (das Beispiel, wenn man den oberen Wert nahe dem unteren wählt, 
was ja dann hier der Fall wäre).

Programmcode, nur relevante Teile:
65145 in Hex: $FE79
1
Start:
2
  
3
  ; Interrupt aktivieren
4
  LDI r16,(1<<TOIE1)
5
  OUT TIMSK,r16
6

7
  ; Timer1 Zeit laden
8
  LDI r16,$79
9
  OUT TCNT1L,r16
10

11
  LDI r16,$FE
12
  OUT TCNT1H,r16
13

14
  ; Timer1 starten
15
  LDI r16,$04
16
  OUT TCCR1B,r16
17

18
Final:
19

20
  RJMP Final
21

22
Timer1Overflow:
23
  
24
  ... ; Register retten
25

26
  ; Timer1 stoppen
27
  LDI r16,$00
28
  OUT TCRR1B,r16
29

30
  ; Timer1 Zeit laden
31
  LDI r16,$04
32
  OUT TCNT1L,r16
33

34
  LDI r16,$A7
35
  OUT TCNT1H,r16
36

37
  ; Timer1 starten
38
  LDI r16,$04
39
  OUT TCCR1B,r16
40

41
  ... ; Register wiederherstellen und Interrupt beenden
#627685
Lesenswert?

Hier passt der CTC Mode besser, weil nur der ein präzises Intervall 
garantiert (z.B. WGM1x=1100 in TCCR1A/B).

Dein Problem allerdings hängt mit der Reihenfolge zusammen, in der du 
den Timer schreibst. Du schreibst nämlich nicht wirklich ins "high" 
Register, sondern tatsächlich in ein Temp-Register, das zusammen mit dem 
darauf folgenden Schreibzugriff aufs "low" Register dann im Timer 
landet. Und das passiert erst beim ersten Interrupt

Steht übrigens in der Doku.
Persönliche Seite #627690
Lesenswert?

Erm... bei mir steht:

>> The device is shipped with CKSEL = “0010”, SUT = “10”,
>> and CKDIV8 programmed.

und in der Tabelle für ClockSources steht:
>> Device Clocking Option CKSEL3..0
>> External Clock 0000 - 0001
>> Calibrated Internal RC Oscillator 4MHz 0010 - 0011
>> Calibrated internal RC Oscillator 8MHz 0100 - 0101
>> Watchdog Oscillator 128kHz 0110 - 0111
>> External Crystal/Ceramic Resonator 1000 - 1111

Ergo: Auslieferungszustand = 4Mhz/8 = 500kHz
Oder überseh ich jezt was?

Ansosnten hat >>Andreas Kaiser<< recht: Reihenfolge beim 
Lesen/Beschreiben von 16bit Registern beachten!
#627723
Lesenswert?

Benjamin K. wrote:
> @Johannes M.:
> Könntest du das näher erläutern? Was wäre denn der Unterschied ob ich
> von 0 bis x zähle oder von x bis 65535 und den Overflow nutze? Ausserdem
> ist das nicht ganz ohne, wie die Erläuterung auf Seite 99 des Handbuchs
> zeigt (das Beispiel, wenn man den oberen Wert nahe dem unteren wählt,
> was ja dann hier der Fall wäre).
Bei einem Preload musst Du im Interrupt Handler jeweils den Startwert 
wieder ins Zählregister schreiben (und dabei die Hinweise in Bezug auf 
16-Bit-Register beachten). Das kann insbesondere bei kurzen Zeiten zu 
Ungenauigkeiten führen, weil der Timer verzögert "nachgeladen" wird. 
Schließlich braucht es einige Taktzyklen vom Auftreten des Interrupts 
(Flag wird gesetzt) bis zum Beginn des eigentlichen Interrupt Handlers. 
In Assembler kann man das zwar sehr präzise kontrollieren und 
kompensieren (indem man den Nachladewert entsprechend anpasst), aber es 
ist unnötiger Programmieraufwand.

Im CTC-Modus schreibst Du einmal den Wert ins Compare-Register und 
brauchst Dich danach nicht mehr drum zu kümmern, solange der Wert 
konstant bleibt. Und wenn man erforderliche Änderungen am Compare-Wert 
synchronisiert (also im entsprechenden Interrupt-Handler) durchführt, 
kommt es i.d.R. auch nicht zu den von Dir angedachten Problemen.
#627741
Lesenswert?

Um beide Compares und den Capture nutzen zu können, verzichte ich meist 
auf den CTC-Mode und lasse Timer1 frei durchlaufen. Im Compare lese ich 
OCR aus, addiere das Intervall dazu und schreibe ins OCR zurück. Das ist 
zwar etwas mehr Programmieraufwand als CTC, ist aber auch jitterfrei, da 
die Zählerei des Timers dadurch nicht berührt wird.

Preload des Timerstandes macht man eigentlich nur noch bei Timern, die 
kein Compare haben und deren Vorteiler so hoch (langsam) ist, dass 
sichergestellt ist, dass der Timer beim Laden noch 0 war.

MfG, BlauBär
OP #627773
Lesenswert?

@Läubi:
Also wenn ich das nicht falsch verstehe, dann steht da:
... and an initial system clock prescaling of 8, resulting in 1.0 MHz 
system clock. ...

Also ein Teiler von 8 sorgt für 1MHz Takt, was ich ja auch über den 
CKOUT-Pin mit dem Oszi nachmessen konnte.

@Andreas:
Das mit der Reihenfolge hats gebracht, danke. Ich habe mein Programm nun 
so geändert, dass die Timerstartwerte erst in zwei Register geladen 
werden, also das untere Byte in ein Regster das obere in das andere 
Register (möchte nachher Werte aus dem Flash laden). Anschließend werden 
die Werte dann in der richtigen Reihenfolge, wie in der Application Note 
doc1493.pdf angegeben, in das TCNT1-Register geladen. Der Wert wird in 
meinem Programm nicht konstant bleiben. Es ist für meine Anwendung auch 
kein Problem wenn der Timer mal etwas länger oder kürzer läuft, hatte 
mich nur über die sehr lange Laufzeit im ersten Zyklus gewundert.

mfg Benny -> solved

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren