Mein aktuelles Projekt beinhaltet die Erzeugung von 200-kHz-PWM-Wellenformen mit Timer 1 im schnellen PWM-Modus. Ich möchte einen 16-Bit-Zähler jedes Mal erhöhen, wenn der Timer überläuft (alle 5 μs).
volatile uint16_t count;
ISR(TIMER1_OVF_vect)
{
++count;
}
Der Mikrocontroller ist ein ATmega8, der mit 16 MHz läuft, sodass nur 80 Zyklen übrig bleiben, um den Interrupt zu bedienen und die Variable zu inkrementieren, bevor der nächste Interrupt ausgelöst wird. Wenn ich mir den kompilierten Code ansehe ...
00000890 <__vector_8>:
890: 1f 92 push r1
892: 0f 92 push r0
894: 0f b6 in r0, 0x3f ; 63
896: 0f 92 push r0
898: 11 24 eor r1, r1
89a: 8f 93 push r24
89c: 9f 93 push r25
89e: 80 91 c9 00 lds r24, 0x00C9
8a2: 90 91 ca 00 lds r25, 0x00CA
8a6: 01 96 adiw r24, 0x01 ; 1
8a8: 90 93 ca 00 sts 0x00CA, r25
8ac: 80 93 c9 00 sts 0x00C9, r24
8b0: 9f 91 pop r25
8b2: 8f 91 pop r24
8b4: 0f 90 pop r0
8b6: 0f be out 0x3f, r0 ; 63
8b8: 0f 90 pop r0
8ba: 1f 90 pop r1
8bc: 18 95 reti
...Ich fand heraus, dass die generierte Interrupt-Service-Routine leicht weiter optimiert werden könnte. Dies ist das erste Mal, dass ich versucht habe, die Inline-Assemblierung in ein C-Programm einzufügen, und ich habe festgestellt, dass es unnötig frustrierend ist, dies zu lernen, und es erfordert, eine ziemlich esoterische Syntax zu verstehen. Ich würde gerne wissen, wie ich auf die Inline-Assembly zugreifen kann uint8_t count
(da die Variable statisch zugewiesen wird, anders als in jeder Antwort, die ich im Web gesehen habe). Ist der Code sonst in Ordnung, oder habe ich noch etwas übersehen?
ISR(TIMER1_OVF_vect, ISR_NAKED)
{
asm volatile("push r24" "\n\t"
"in r24, __SREG__" "\n\t"
"push r24" "\n\t"
"push r25" "\n\t"
"lds r24, %A0" "\n\t"
"lds r25, %B0" "\n\t"
"adiw r24, 1" "\n\t"
"sts %B0, r25" "\n\t"
"sts %A0, r24" "\n\t"
"pop r25" "\n\t"
"pop r24" "\n\t"
"out __SREG__, r24" "\n\t"
"pop r24" "\n\t"
"reti" "\n\t"
: "=r" (count) /*this does*/
: "0" (count)); /*not work*/
}
Als Nebenbemerkung gibt es eine Möglichkeit, den Compiler dazu zu bringen, ein Registerpaar speziell für zu reservieren uint8_t count
, da dies eine Reduzierung der ISR-Länge um mindestens 6 Anweisungen ermöglichen würde (durch Eliminieren der lds- und sts-Anweisungen, eines Push-to-Stack und eines pop vom Stapel)?
Ich habe Ihren Beitrag gefunden, als ich nach einer Optimierung der ISR-Routine gesucht habe. Endlich habe ich eine Lösung, die Sie (und ich) haben wollten.
Ich verwende Atmel Studio 6.1 (GCC 3.4.2.1002)
ISR(TIM0_OVF_vect,ISR_NAKED)
{
asm volatile(
"push r24" "\n"
"in r24, __SREG__" "\n"
"push r24" "\n"
"push r25" "\n"
"lds r24, %A[_ts]" "\n"
"lds r25, %B[_ts]" "\n"
"adiw r24,1" "\n"
"sts %B[_ts], r25" "\n"
"sts %A[_ts], r24" "\n"
"pop r25" "\n"
"pop r24" "\n"
"out __SREG__,r24" "\n"
"pop r24" "\n"
"reti" "\n"
:
: [_ts] "m" (ts)
: "r24", "r25"
);
}
Hier ts
wird deklariert, wie ich benannte Operanden ( ) anstelle von Standard volatile unsigned int ts = 0;
verwende[_ts] "m" (ts)
%0
Das Ergebnis ist:
0000005a <__vector_11>:
5a: 8f 93 push r24
5c: 8f b7 in r24, 0x3f ; 63
5e: 8f 93 push r24
60: 9f 93 push r25
62: 80 91 60 00 lds r24, 0x0060
66: 90 91 61 00 lds r25, 0x0061
6a: 01 96 adiw r24, 0x01 ; 1
6c: 90 93 61 00 sts 0x0061, r25
70: 80 93 60 00 sts 0x0060, r24
74: 9f 91 pop r25
76: 8f 91 pop r24
78: 8f bf out 0x3f, r24 ; 63
7a: 8f 91 pop r24
7c: 18 95 reti
Lassen Sie den Compiler die Routinearbeit für Sie erledigen.
asm volatile("adiw %0,1\n\t"
: "=w" (count)
: "0" (count)
);
reti();
Auch , register
aber der Compiler nimmt es nur als Vorschlag.
count
tatsächlich in den Registern r23 und r24 zum Inkrementieren oder woanders gespeichert ist.ISR(TIMER1_OVF_vect){ ++count; }
in Bezug auf die Leistung nicht besser als der ursprüngliche C-Code, da beide in genau denselben Assemblercode kompiliert werden.
David
jms
David