Mi proyecto actual consiste en producir formas de onda PWM de 200 KHz utilizando el temporizador 1 en modo PWM rápido. Me gustaría incrementar un contador de 16 bits cada vez que se desborda el temporizador (cada 5 μS)
volatile uint16_t count;
ISR(TIMER1_OVF_vect)
{
++count;
}
El microcontrolador es un ATmega8 que funciona a 16 MHz, lo que deja solo 80 ciclos para dar servicio a la interrupción e incrementar la variable antes de que se dispare la siguiente interrupción. Mirando el código compilado...
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
... Descubrí que la rutina de servicio de interrupción generada podría optimizarse aún más fácilmente. Esta es la primera vez que intento incluir el ensamblado en línea en un programa C, y descubrí que aprender a hacerlo es innecesariamente frustrante y requiere comprender una sintaxis bastante esotérica. Me gustaría saber cómo acceder uint8_t count
dentro del ensamblaje en línea (ya que la variable se asigna de forma estática a diferencia de todas las respuestas que he visto en la web). ¿Está bien el código o me he perdido algo más?
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*/
}
Como nota al margen, ¿hay alguna manera de hacer que el compilador reserve un par de registros específicamente para uint8_t count
, ya que eso permitiría reducir la longitud de ISR en al menos 6 instrucciones (al eliminar las instrucciones lds y sts, un push to stack y un pop de la pila)?
Encontré tu publicación cuando estaba buscando la optimización de la rutina ISR. Finalmente, tengo una solución que tú (y yo) queríamos obtener.
Uso 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"
);
}
Aquí ts
se declara como volatile unsigned int ts = 0;
utilizo operandos con nombre ( [_ts] "m" (ts)
) en lugar de predeterminados%0
El resultado es:
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
Deje que el compilador haga el trabajo duro por usted.
asm volatile("adiw %0,1\n\t"
: "=w" (count)
: "0" (count)
);
reti();
Además, register
pero el compilador solo lo toma como una sugerencia.
count
se almacena realmente en los registros r23 y r24 para incrementar o en otro lugar.ISR(TIMER1_OVF_vect){ ++count; }
rendimiento del código C original, ya que ambos compilan exactamente el mismo código ensamblador.
David
jms
David