Schönen guten Abend.
Ich möchte einen Interrupt realisieren der mit 80khz auftritt.
Dafür nutze ich den 8-Bit Counter TCNT0. (bei einem at_tiny2313)
Ich habe ursprünglich errechnet, dass ich bei 16MHz Tackt und einem
Prescaler von 1 einen Registerwert von 100 brauche um auf meine 80MHz zu
kommen. Aber in der Realität scheint es dass ich dort den Wert 77
eingeben muss (habe ich mit meinem Multimeter, ein gutes von PeakTech,
gemessen), und 40kHz gemessen (das sollte richtig sein, da es ja eine
Periodendauer misst).
also ich nutze folgenden Code:
1
intmain(void)
2
{
3
DDRB|=0xFF;
4
PORTB=0x00;
5
//USI_init();
6
interrupt_init();
7
8
sei();
9
while(1)
10
{
11
asm("nop");
12
}
13
}
14
15
voidinterrupt_init(void)
16
{
17
TCCR0B=_bv(CS00);//prescaler 1
18
TIMSK=_bv(TOIE0);// interrupt enable für Timer-Overflow
19
}
20
21
ISR(TIMER0_OVF_vect)
22
{
23
TCNT0=77;// 100 zählerstände zählen => 80kHz, 77 in der realität
24
PORTB^=_bv(PB0);
25
}
Um das ganze herauszufinden habe ich mir die zugehörige lls Datei
angesehn, und folgenden ASM Code gefunden:
1
5e: 1f 92 push ;2
2
60: 0f 92 push r0 ;2
3
62: 0f b6 in r0, 0x3f ; 63 ;1
4
64: 0f 92 push r0 ;2
5
66: 11 24 eor r1, r1 ;1
6
68: 8f 93 push r24 ;2
7
6a: 9f 93 push r25 ;2
8
TCNT0 = 77; // 100 zählerstände zählen => 80kHz
9
6c: 8d e4 ldi r24, 0x4D ; 77 ;2
10
6e: 82 bf out 0x32, r24 ; 50
11
PORTB ^= _bv(PB0);
12
70: 98 b3 in r25, 0x18 ; 24
13
72: 81 e0 ldi r24, 0x01 ; 1
14
74: 89 27 eor r24, r25
15
76: 88 bb out 0x18, r24 ; 24
16
78: 9f 91 pop r25
17
7a: 8f 91 pop r24
18
7c: 0f 90 pop r0
19
7e: 0f be out 0x3f, r0 ; 63
20
80: 0f 90 pop r0
21
82: 1f 90 pop r1
22
84: 18 95 reti
die zahlen hinten bis zu der ; 77 stehen für die Tackte die der Befehl
braucht.
Zusammengerechnet brauche ich 14 Tackte bis die 77 im Register ist und
hochgezählt wird.
100-77 sind für mich aber 23, also wo sind die fehlenden 9 Tackte?
lg Coco
Die braucht der Prozessor, um überhaupt erstmal zur ISR zu springen,
wenn der Interrupt auftritt. Da kann aber variieren.
Generell sollte man für sowas den CTC-Modus benutzen und kein manuelles
Neuladen des Zählers, denn das ist nie so richtig genau, und Fehler
addieren sich auf.
ok, dachte, dass der Einsprung in die ISR die "push"-Befehle dauert, und
nicht selber noch etwas verbraucht.
Dann werd ich mir wohl Morgen mal ein Beispiel mit dem CTC-Modus
schreiben.
Danke für die schnelle Antwort.
Das sind gleich 2 Sachen im Argen:
1) Bei 16 MHz Systemtakt und einer Interrupt-Häufigkeit von 80 kHz sind
200 Takte nötig und nicht 100.
2) Der Zähler zählt aufwärts. Wenn du ihn also mit 77 vorlädst, sind es
256-77 = 179 Zähler-Takte bis zum nächsten Überlauf. Das plus die 14
Takte, die du noch gefunden hast, plus die, die du vergessen hast, sind
dann auch zusammen 200 und der Grund, warum 77 das gewünschte Ergebnis
liefert.
Im Handbuch heißt es dazu:
"The interrupt execution response for all the enabled Atmel®AVR®
interrupts is four clock cycles minimum. After four clock cycles, the
Program Vector address for the actual interrupt handling routine is
executed. During this 4-clock cycle period, the Program Counter is
pushed onto the Stack. The Vector is normally a jump to the interrupt
routine, and this jump takes three clock cycles. If an interrupt
occurs during execution of a multi-cycle instruction, this instruction
is completed before the interrupt is served."
4+3 = 9, also exakt das, was Du gesucht hast, wobei noch die Abarbeitung
des gerade in der Hauptschleife befindlichen Befehls dazu kommen müsste.
Dein µC verbringt in der Hauptschleife einen Takt mit einem NOP und 2
Takte mit einem RJMP. Entsprechend verteilt ist die Wahrscheinlichkeit,
dass der µC gerade im ersten Takt des Sprungbefehls ist, also nicht
unmittelbar nach dem Takt unterbrechen kann, 1/3. In 1/3 der Fälle wird
also eine Periode Deiner Frequenz um einen Takt länger.
CTC halte ich hier auch für angebrachter.
coco jack schrieb:> Aber in der Realität scheint es dass ich dort den Wert 77> eingeben muss (habe ich mit meinem Multimeter, ein gutes von PeakTech,> gemessen), und 40kHz gemessen (das sollte richtig sein, da es ja eine> Periodendauer misst).
Warum guckst du dir nicht erstmal die simulierte Realität an, indem du
in AVR Studio das ganze im Debugger laufen läßt und z.B. im
Einzelschritt bzw. mit Hilfe von Breakpoints in der ISR anhand der
Stop-Watch genau die Laufzeit bis zum nächsten Interrupt und die Zeiten
für den Einspung verfolgen kannst. Diskrepanzen wie 80 vs. 40 kHz siehst
du sofort ohne das Schätzeisen herauszukramen.