Ich habe mal versucht, meine Interrupt Service Routine in Assembler umzuschreiben. Ich würde mich freuen, wenn mal jemand drüber schauen kann, ob sie korrekt ist. Mein erster Funktionstest war erfolgreich, aber das muss ja nichts bedeuten. Ich bin etwas unsicher, weil ich bisher noch nie Assembler und C kombiniert habe.
Code:
// System Zeitmesser in Millisekunden
volatile uint32_t system_time;
// Vorteiler für den System Zeitmesser
register uint8_t prescaler __asm("r2");
// Timer 0 Unterbrechung (75khz). Inkrementiert system_time bei jedem 75ten Interrupt.
// Wurde durch die folgende Assembler Version ersetzt.
/*
ISR(TIMER0_COMP_vect) {
if (--prescaler==0) {
prescaler=75;
system_time++;
}
}
*/
// Timer 0 Unterbrechung (75khz). Inkrementiert system_time bei jedem 75ten Interrupt.
// Diese Assembler Version ist wesentlich effizienter, als die obige C Version.
void __attribute__ ((naked)) TIMER0_COMP_vect (void)
{
__asm__ __volatile (
"push r0" "\n\t"
"in r0,__SREG__" "\n\t" // Sichere SREG in r0
"dec r2" "\n\t" // --prescaler
"brne return" "\n\t" // if (prescaler!=0) goto return:
"\t" "push r1" "\n\t"
"\t" "push r24" "\n\t"
"\t" "push r25" "\n\t"
"\t" "push r26" "\n\t"
"\t" "push r27" "\n\t"
"\t" "ldi r24,75" "\n\t" // prescaler=75
"\t" "mov r2,r24" "\n\t"
"\t" "lds r24,%0" "\n\t" // system_time++
"\t" "lds r25,%0+1" "\n\t"
"\t" "lds r26,%0+2" "\n\t"
"\t" "lds r27,%0+3" "\n\t"
"\t" "eor r1,r1" "\n\t"
"\t" "adiw r24,1" "\n\t"
"\t" "adc r26,r1" "\n\t"
"\t" "adc r27,r1" "\n\t"
"\t" "sts %0,r24" "\n\t"
"\t" "sts %0+1,r25" "\n\t"
"\t" "sts %0+2,r26" "\n\t"
"\t" "sts %0+3,r27" "\n\t"
"\t" "pop r27" "\n\t"
"\t" "pop r26" "\n\t"
"\t" "pop r25" "\n\t"
"\t" "pop r24" "\n\t"
"\t" "pop r1" "\n\t"
"return:" "\n\t"
"out __SREG__,r0" "\n\t"
"pop r0" "\n\t"
"reti" "\n\t"
:: "i" (&system_time)
);
}
Verglichen mit der ersten Variante habe ich den Prescaler als RegisterVariable deklariert und die Taktfrequenz von 100khz auf 75khz reduziert (auf diese Idee hätte ich eigentlich auch eher kommen können. Bei den ersten 74 Interrupts wird lediglich der Prescaler r2 decrementiert. Nur beim 75ten Interrupt wird die 32 Bit Variable incrementiert und die dazu benutzten Register auf dem Stack gesichert (der C Compiler hatte sie schon im Prolog gesichert). Außerdem konnte ich bei meinem manuellen "Compilieren" ein paar Zeilen einsparen.
Ich verspreche mir davon, dass mein System-Timer nun erheblich weniger Grund-Last produziert (pi mal Daumen nur noch ein viertel).
Sind meine Gedanken dazu korrekt?
Lesezeichen