controlador de interrupción de desbordamiento del temporizador avr-gcc en ensamblaje en línea

3

Mi proyecto actual involucra la producción de formas de onda PWM de 200KHz usando el temporizador 1 en modo PWM rápido. Me gustaría incrementar un contador de 16 bits cada vez que el temporizador se desborda (cada 5 μS)

volatile uint16_t count;
ISR(TIMER1_OVF_vect)
{
    ++count;
}

El microcontrolador es un ATmega8 que funciona a 16MHz, dejando solo 80 ciclos para reparar la interrupción e incrementar la variable antes de que se active 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 trato de incluir el ensamblaje en línea en un programa en C, y he descubierto que aprender a hacerlo es es innecesariamente frustrante y requiere entender una sintaxis bastante esotérica. Me gustaría saber cómo acceder a uint8_t count dentro del ensamblaje en línea (ya que la variable se asigna estáticamente a diferencia de cada respuesta que tengo visto en la web). ¿El código está bien de otra manera, 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 una manera de hacer que el compilador reserve un par de registros específicamente para uint8_t count , ya que permitiría reducir la longitud del ISR en al menos 6 instrucciones (eliminando las instrucciones de pds y pts, un empuje)? apilar, y un pop de la pila)?

    
pregunta jms

2 respuestas

2

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;
Uso operandos con nombre ( [_ts] "m" (ts) ) en lugar de defailt %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
    
respondido por el mvi
1

Deje que el compilador haga el trabajo por usted.

asm volatile("adiw %0,1\n\t"
             : "=w" (count)
             : "0" (count)
            );
reti();

También, register , pero el compilador solo lo toma como una sugerencia.

    
respondido por el Ignacio Vazquez-Abrams

Lea otras preguntas en las etiquetas