Hallo,
ich habe da ein kleines Problem mit dem 8Bit Timer0 eines Mega644.
Der Timer scheint zu langsam zu laufen.
Der Mega644 läuft mit 20MHz das stimmt 100% da liegt das Problem nicht.
Ich möchte Timer0 verwenden da das Programm nachher auf einem Tiny13
laufen soll der nur einen 8Bit Timer hat.
Die Timerfrequenz soll bei 1MHz liegen, 1µs Interval.
Um den Timer zu testen habe ich folgendes simples Programm geschrieben:
1
#include<stdlib.h>
2
#include<avr/io.h>
3
#include<avr/interrupt.h>
4
#include<inttypes.h>
5
#include<string.h>
6
7
volatileuint8_tonoff=0;
8
volatileuint16_ttime=0;
9
volatileuint16_ttime2=0;
10
11
ISR(TIMER0_COMPA_vect)// timer0 clear-timer on compare
12
{
13
time++;
14
if(time>=1000)
15
{
16
time=0;
17
time2++;
18
}
19
}
20
21
intmain(void)
22
{
23
time=0;
24
time2=0;
25
onoff=0;
26
27
cli();
28
29
DDRD|=(1<<DDD6);
30
PORTD&=~(1<<PD6);
31
32
TCCR0A=(1<<WGM01);// Timer0 CTC mode
33
TIMSK0|=(1<<OCIE0A);// Timer0 clear-timer on compare interrupt enabled
34
OCR0A=19;// Time0 TOP = 1 µs
35
TCCR0B|=(1<<CS00);// Timer0 start with prescaler 1
36
37
sei();
38
39
while(1)
40
{
41
if(time2>=1000)
42
{
43
time2=0;
44
if(onoff==0)
45
{
46
onoff=1;
47
PORTD|=(1<<PD6);
48
}
49
else
50
{
51
onoff=0;
52
PORTD&=~(1<<PD6);
53
}
54
}
55
}
56
}
Im Timer CTC Interrupt handler wird die Variable "time" erhöht, wenn sie
größer gleich 1000 ist, also nach 1ms, wird Variable "time2" erhöht und
"time" wieder auf 0 gesetzt.
Ist "time2" größer gleich 1000, also nach 1s, wird eine LED ein bzw.
ausgeschaltet und "time2" wieder auf 0 gesetzt.
Also alles recht easy, nur die LED bleibt nicht nur für 1 Sekunde wie zu
erwarten wäre an bzw. aus sondern für ca. 3 Sekunden, eher gefühlt 2,5
Sekunden. Ich weiss das ist jetzt keine hoch wissentschaftliche Methode
das zu testen, aber es sollte doch funktionieren.
Wo liegt da jetzt der Fehler? Zuviel code im Timer Interrupt? - glaube
eher nicht. Es macht auch absolut keinen Unterschied ob der vergleich
von "time2" und das ein oder ausschalten der LED in der while Schleife
in main steht oder im Timer Interrupt selbst.
Datenblatt schon zig mal durchforstet, wenn ichs richtig verstanden habe
sollten die Einstellungen für den Timer richtig sein.
bei 20 MHz
prescaler 1 CS00 = 1
OCR0A = 19
CTC mode WGM01 = 1
clear-timer on compare interrupt enabled OCIE0A = 1
sollte meiner Meinung nach ein 1µs Interval sein bzw. 1MHz Frequenz.
Hat da evtl. jemand ne Idee?
Paul schrieb:> Zuviel code im Timer Interrupt? - glaube> eher nicht.
Doch.
Mit Prolog, Epilog kommst Du kaum unter 30 Zyklen.
Etwas kürzer kriegst Du den Interrupt mit 3 8Bit-Variablen, die bis 100
zählen, da der AVR ein 8Bitter ist.
Paul schrieb:> time++;> if (time >= 1000)> {> time = 0;
hier wird wohl wieder mal das volatile Problem zuschlagen, es vergrößert
den code merklich!
time muss gar nicht volatile sein.
Vielen Dank für die Hilfe!
Es lag wirklich an der zu hohen Frequenz, habs auf 100KHz runter gesetzt
und nun läufts einwandfrei. (Prescaler 8 und OCR0A 24)
Btw. ohne "volatile" wird zu viel code "wegoptimiert" und das Programm
tut einfach garnix mehr, habs ausprobiert.
Paul schrieb:> Btw. ohne "volatile" wird zu viel code "wegoptimiert" und das Programm> tut einfach garnix mehr, habs ausprobiert.
dann kann bei time nicht sein, das wird ja nur in der ISR verwendet.
(oder es gibt noch code, den du uns nicht gezeigt hast)
Nur lokal benutzte Daten, wie time, sollte man nicht global definieren.
Der Grund ist hier sicher daß der Inhalt zwischen 2 Aufrufen der ISR
überleben soll. Und dazu gibt es "static"-Variablen. Diese werden von
der Laufzeitumgebung mit 0 initialisiert. Falls andere Werte gebraucht
werden, dann kann das bei der Deklaration passieren und wird genau ein
mal getan. Es braucht dann auch kein "volatile", denn wer außer der ISR
selbst sollte den Wert ändern, Bugs ausgenommen. Und das sorgt für
kompakteren Code.
1
ISR(TIMER0_COMPA_vect)// timer0 clear-timer on compare
Wenn ich simulavr richtig deute, wird der Timer-Interrupt noch während
der ISR neu getriggert (siehe 0x00f2), inmitted des Epilog. Nicht gut
für code in der main.
neuer PIC Freund schrieb im Beitrag #4399125:
> Wenn ich simulavr richtig deute, wird der Timer-Interrupt noch während> der ISR neu getriggert (siehe 0x00f2), inmitted des Epilog. Nicht gut> für code in der main.
Ja, ich habe das einfach nicht bedacht das da ja noch ein bischen mehr
dazu gehört eine ISR aufzurufen etc. da war die Zeit für die Abarbeitung
einfach zu gering.
Irgendwo muss man ja einfach mal an die Grenzen bei den "kleinen
Dingern" stoßen und wie sich gezeigt hat war die Frequenz von 1MHz für
die eigentlich Aufgabe (CPPM Signal von einem OrangeRX R415X Empfänger
auslesen und 2 x PWM für Motorsteuerung erzeugen) eh overkill.
Das hätte der ATTiny13 den ich nachher eigentlich dafür einsetzen will
nie hinbekommen mit seinem ~9.6 MHz internem Takt.
Carl D. schrieb:> Der Grund ist hier sicher daß der Inhalt zwischen 2 Aufrufen der ISR> überleben soll. Und dazu gibt es "static"-Variablen.
Danke Carl, das es ja auch noch static gibt vergesse ich leider manchmal
:)
wobei sich mir aber "die Nackenhaare aufstellen" wenn ich eine statische
Variablendeklaration in einer Methode sehe, was natürlich absolut
legitim ist, aber für mich eher ein etwas ungewohnter Stil.
Nur, um's nochmal zu dokumentieren:
Es geht prinzipiell mit dem ATmega644 bei 20 MHz.
Zählen wir die Taktzyklen.
1
.org 0
2
rjmp start
3
4
;minimum interrupt response time 4 cycles
5
.org TIMER0_COMPA_vect
6
in r13,SREG ;1
7
subi r22,1 ;1 decrement time variable
8
sbci r23,0 ;1
9
brne timer_x ;2/1
10
movw r23:r22,r15:r14 ;1 reload time variable
11
adiw r25:r24,1 ;2 increment time2 variable
12
timer_x: out SREG,r13 ;1
13
reti ;4
14
15
start:
16
;initialize TIMER0 and port here
17
ldi r22,LOW(1000) ;initialize variable "time"
18
ldi r23,HIGH(1000)
19
movw r15:r14,r23:r22 ;save reload value for variable "time"
20
clr r24 ;clear variable "time2"
21
clr r25
22
sei
23
;do main stuff
Wenn time2 nicht angefasst wird, braucht ein Interruptdurchlauf 14+x
Taktzyklen, anderenfalls 16+x (x ist abhängig vom im Moment des
Interrupts gerade ausgeführten Befehl und kann m.E. zwischen 0 und 2
liegen).
Es bleibt also auch bei 1 MHz Interruptfrequenz noch ein bißchen Zeit
für das Hauptprogramm übrig.
Logischerweise dürfen r13-r15 und r22-r23 nicht mehr anderweitig
verwendet werden und auch r24-r25 ist programmweit ausschließlich für
time2 reserviert, aber ein bißchen Schwund ist halt immer...
Das Beispiel ist natürlich grenzwertig, ein Interrupt alle 10us macht
das Leben deutlich leichter und dann klappt's auch locker mit dem
ATtiny13.