Hallo zusammen,
mir kommt gerade bei der Programmierung eines ATmega8 etwas spanisch
vor.
Eingestellt ist er auf 8MHz.
Das beduetet ja, dass ein Prozessortakt 125ns dauert.
Ich habe nun folgendes programmiert und mit nem Osca gemessen:
1
intmain(void)
2
{
3
while(1){
4
PORTB^=(1<<PB1);
5
}
6
}
Das bit ist laut Messung je 615ns gesetzt und 615ns zurückgesetzt -
alles ok.
Änder ich jetzt die Programmierung ab und lasse in einer ISR vom Timer0
überlauf den Ausgang togglen, dauert das ganze 5µs
Ich finde das steht nicht im verhältnis. Warum dauert der ISR aufruf so
lange?
1
intmain(void)
2
{
3
//TIMER0 initialisieren
4
TCCR0=(0<<CS02)|0<<CS01|(1<<CS00);// Start Timer 0 OHNE Prescaler
5
TCNT0=255;// Vorladen
6
TIMSK|=(1<<TOIE0);// Interrupts aktivieren
7
sei();// Das I-bit im SREG Register setzen
8
9
//Endlosschleife
10
while(1){
11
}
12
}
13
14
ISR(TIMER0_OVF_vect)
15
{
16
PORTB^=(1<<PB1);
17
TCNT0=255;
18
}
Vor jahren habe ich mal mit nem PIC und Assamlber programmiert - nun ist
es ein ATMEL mit c...
Ich könnte mir daher vorstellen, dass ich den Timer oder die ISR falsch
aufgerufen habe - könnt ihr mir einen Tip geben?
Vielen Dank schonmal.
Thomas Borsdorf schrieb:> Warum dauert der ISR aufruf so> lange?
schau dir den ASM code an! Da wird schon noch etwas mehr gemacht.
Dann wird nach jedem return aus einer ISR auch noch mindestens 1 asm
Befehl ausgführt bevor es wider in eine ISR geht.
Thomas Borsdorf schrieb:> Änder ich jetzt die Programmierung ab und lasse in einer ISR vom> Timer0 überlauf den Ausgang togglen, dauert das ganze 5µs
Wie vertrauenswürdig sind diese 5µs? Sind das 5.000µs?
5µs wären 40 Takzyklen. Ist in der Tat ein wenig viel.
Das LSS-File schon mal angesehen?
Thomas Borsdorf schrieb:> Eingestellt ist er auf 8MHz.
interner Takt?
Dann sind diese 8Mhz auch nicht besonders vertrauenswürdig.
> Das bit ist laut Messung je 615ns gesetzt und 615ns zurückgesetzt -> alles ok.
Da 615 nicht durch 125 teilbar ist, ist das schon mal ein Hinweis, dass
die 8Mhz wohl daneben sein werden. Du solltest mal von diesem Test das
Assembler LSS File ansehen und dort die Takte zählen, die die Schleife
wirklich braucht. Aus den gemessenen 615ns und der Anzahl der Takte
kannst du dir dann die echte Taktzykluszeit ausrechnen.
Karl Heinz schrieb:> 5µs wären 40 Takzyklen. Ist in der Tat ein wenig viel.
Die ISR ist genau 20 Instruktionen lang. Das ergibt hier 40 Taktzyklen.
uwe schrieb:> Laut meiner berechnung sollte es sogar 31,875µs Dauern.
siehe:
Thomas Borsdorf schrieb:> TCNT0 = 255;
Erstmal vorweg: im Anfangsbeitrag sind es 625ns nicht 615ns -
Tippfehler.
Ralf G. schrieb:> Karl Heinz schrieb:>> 5µs wären 40 Takzyklen. Ist in der Tat ein wenig viel.>> Die ISR ist genau 20 Instruktionen lang. Das ergibt hier 40 Taktzyklen.
Das klingt doch schonmal nach einer Erklärung.
Gibt es auch eine Lösung, oder sind alle ISR Aufrufe so "Langsam"
Würde es Sinn machen auf Assambler umzusteigen?
Im anhang die Screenshotas der Messung.
>TCNT0 = 255;
Ah hab das hier übersehen, jetzt verstehe ich was du erreichen willst,
du willst einen Overflow sofort nach dem beedigen des Interupthandlers.
Aber der sichert jetzt erst mal das Statusregister zurück, und die
Register vom Stack und dann springt er zurück in die main. Danach führt
er einen weiteren Befehl aus von der main und springt wieder in den
Handler sichert die Statusflags und packt Register auf den Stack.
Wieviele Register packt er denn auf den Stack und holt sie auch wieder
runter?
-also der sprung in den interupt sind 2 Takte.
-laden des statusflags in ein Register 1 Takt.
-speichern des registers auf dem Stack 2 Takte.
-speichern der restlichen Register n x 2 Takte.
-Portb1 invertieren 1 Takt.
-counter auf 255 setzen 1 Takt.
-laden der gesichtern register n x 2 Takte. (hier ist der Counter
schon wieder übergelaufen)
-laden des registers mit den Flags 2 Takte.
-speichern der flags im Statusregister 1 Takt.
-Rücksprung zu main 2 takte.
-hier wird mindestens 1 Befehl abgearbeitet 1 Takt (obwohl Overflow
gesetzt)
-wieder nach oben
Macht wenn ich von zwei Registern ausgehe 21 Takte.
macht 2,625µs an und 2,625µs aus zusammen 5,25µs.
Ralf G. schrieb:> Karl Heinz schrieb:>> 5µs wären 40 Takzyklen. Ist in der Tat ein wenig viel.>> Die ISR ist genau 20 Instruktionen lang. Das ergibt hier 40 Taktzyklen.
Tatsächlich.
1
5e: 1f 92 push r1
2
60: 0f 92 push r0
3
62: 0f b6 in r0, 0x3f ; 63
4
64: 0f 92 push r0
5
66: 11 24 eor r1, r1
6
68: 8f 93 push r24
7
6a: 9f 93 push r25
8
9
6c: 88 b3 in r24, 0x18 ; 24
10
6e: 92 e0 ldi r25, 0x02 ; 2
11
70: 89 27 eor r24, r25
12
72: 88 bb out 0x18, r24 ; 24
13
74: 8f ef ldi r24, 0xFF ; 255
14
76: 82 bf out 0x32, r24 ; 50
15
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
war mir so noch gar nicht bewusst, dass für Präambel und Postambel schon
14 Befehle draufgehen.
Thomas Borsdorf schrieb:> Gibt es auch eine Lösung, oder sind alle ISR Aufrufe so "Langsam"
man könnte die Funktion als naked deklarieren, dann geht es schneller.
Das kann man aber nur machen, Arbeitsregister in der ISR ändert. In
diesem Fall sollte es gehen, in der Praxis meist nicht.
> Würde es Sinn machen auf Assambler umzusteigen?
ja, aber spätestens wenn der code etwas mehr machen soll, wird es
genauso langsam. Denn da musst du auch die Register sichern und
widerherstellen.
Thomas Borsdorf schrieb:> Gibt es auch eine Lösung, oder sind alle ISR Aufrufe so "Langsam"
Mein Gedächtnis kramt da noch was raus, dass man ISR auch 'naked' machen
kann. Habs aber noch nie wirklich gebraucht. Denn 5µs sind ja nicht
wirklich langsam. Wann hat man schon mal den Fall, dass man Interrupts
in derart kurzen Abständen braucht?
> Würde es Sinn machen auf Assambler umzusteigen?
Wenn du den Speed brauchst.
Brauchst du den wirklich?
Karl Heinz schrieb:> war mir so noch gar nicht bewusst, dass für Präambel und Postambel schon> 14 Befehle draufgehen.
ein schönes Beispiel für Leute die immer behaupten, der Compiler macht
es schon optimal.
Hier geht es sehr großzügig mit Registern um, ein Register hätte
gereicht.
Peter II schrieb:> Karl Heinz schrieb:>> war mir so noch gar nicht bewusst, dass für Präambel und Postambel schon>> 14 Befehle draufgehen.>> ein schönes Beispiel für Leute die immer behaupten, der Compiler macht> es schon optimal.
:-)
Seitenhieb akzeptiert.
> Hier geht es sehr großzügig mit Registern um, ein Register hätte> gereicht.
Jep. Ist halt die einfache allgemeine ISR Präambel. Wundert mich
eigentlich, dass da einiges nicht wegoptimiert wird. Ich hatte das
eigentlich anders in Erinnerung. Zumindest das r1 überhaupt nicht
verändert wird (*), hätte er schon rausfinden können.
* gemeint ist: vom eigentlichen Nutzcode. Der fixe eor r1, r1 in der
Präambel zählt nicht.
> Würde es Sinn machen auf Assambler umzusteigen?
Output compare Einheit verwenden, die macht das togglen ohne Interupt.
Ansonsten wenns nen anderer Pin als OC0 sein muß, dann Output Compare
Interupt. Wenn du dann nichts im interupt machst brauchst du auch nicht
Statusregister und andere auf den Stack legen.
-also der sprung in den interupt sind 2 Takte.
-Pin Togeln 1 Takt
-Rücksprung zu main 2 Takte.
-hier wird mindestens 1 Befehl abgearbeitet 1 Takt (obwohl Overflow
gesetzt)
-wieder nach oben
Sind dann 6 Takte
uwe schrieb:> -also der sprung in den interupt sind 2 Takte.> -laden des statusflags in ein Register 1 Takt.> -speichern des registers auf dem Stack 2 Takte.> -speichern der restlichen Register n x 2 Takte.> -Portb1 invertieren 1 Takt.> -counter auf 255 setzen 1 Takt.> -laden der gesichtern register n x 2 Takte. (hier ist der Counter> schon wieder übergelaufen)> -laden des registers mit den Flags 2 Takte.> -speichern der flags im Statusregister 1 Takt.> -Rücksprung zu main 2 takte.> -hier wird mindestens 1 Befehl abgearbeitet 1 Takt (obwohl Overflow> gesetzt)
Da fehlt ganz zu Anfang noch was, nämlich die statische Interruptlatenz
von 4 Takten. Die werden gebraucht, um die Rücksprungadresse auf den
Stack zu legen.
Super, danke für die Erklärung, jetzt wird es mir auch klar.
Karl Heinz schrieb:> Wenn du den Speed brauchst.> Brauchst du den wirklich?
Gute Frage - ich lese ein Protokol aus und möchte keine Information
verlieren.
Dachte mir ursprünglich, dass es einfacher den Timer einfach hoch zu
stellen.
Jetzt werde ich wohl umdenken und mich mit int0/int1 bechäftigen - beim
PIC war das immer so Aufwendig -.-
Mal schauen wie es mit nem Atmel läuft.
Danke nochmal für die Hilfe und Infos.
Für zeitkritische Aufgaben gibt es daher Controller wie den Cortex-M3,
der generell höhere Taktfrequenzen erreicht, und einen Interrupt in 12
Takten aufruft (inklusive der Sicherung von 4 Registern +
Status-Register), und bei aufeinanderfolgenden Interrupts das
Wiederherstellen-Sichern wegfallen lässt.