PIC ISR: ¿verifica todas las fuentes de interrupción o solo una?

Tengo curiosidad por saber si hay algún beneficio particular al usar una llamada en lugar de un goto al verificar cada fuente de interrupción en una ISR. Mi código actual haría algo como esto:

.intr   CODE        4

    pagesel     $

    btfsc       INTCON, TMR0IF          ; Timer0
      goto      Timer0_Entry

    banksel     PIR1                    ; Bank #0
    btfsc       PIR1, SSP1IF
      goto      I2C_Entry               ; I2C Peripheral
...

pero eso es en una pic16 de rango medio mejorado donde se guarda el contexto por usted. Si estuviera en un PIC16F88, digamos, habría una ganancia (menor) para evitar todo el tedioso contexto, excepto en una situación en la que dos fuentes de interrupción simultáneas estaban pendientes.

Hacerlo con gotos crea implícitamente una lista de prioridades que puede detener otras fuentes: si tuviera varias interrupciones de una fuente, esa fuente detendría las otras fuentes de irq hasta que se detuviera. Golpeé eso una vez al olvidar que el indicador irq del puerto serie se afirma de forma predeterminada.

Pero aparte de eso, ¿es solo una cuestión de gusto? El golpe que recibes (en una situación de múltiples irq) al hacer un retfie y luego regresar al ISR de inmediato no es particularmente grave.

Creo que esto es situacional. En la mayoría de los casos (según mi experiencia), tener dos interrupciones pendientes debería ser 'raro', por lo que, a menos que haya una necesidad apremiante de responder lo más rápido posible a ambas interrupciones (tal vez para reducir el jitter o algo así), no hay necesidad de preocuparse por obtener un doble.

Respuestas (1)

TL; DR: use la menor cantidad de interrupciones posible y mantenga sus ISR cortos. La descarga de trabajo no crítico al bucle principal es de gran ayuda.


En un PIC, con su único vector de interrupción (doble para PIC18), a menudo hago esto:

void ISR()
{
    if(FLAG1_EN && FLAG1)
    {
        //ISR1
    }
    if(FLAG2_EN && FLAG2)
    {
        //ISR2
    }
    //etc.
}

Esto evita las llamadas a funciones del ISR, lo que hace que mi compilador genere un montón de código adicional para guardar contexto, incluso en un chip que hace (algo de) eso automáticamente.

Si es necesario, puedo ordenarlos por prioridad y regresar de cada uno sin verificar el resto de las banderas, para que las prioridades más altas sean encuestadas con más frecuencia. O para ahorrar algo de latencia, puede retroceder y repetir el ISR, solo regresando si no hay ninguno activo.

O, si algunos ISR son tareas periódicas que solo tienen que promediar la frecuencia correcta, puede hacer que sus ISR simplemente establezcan una bandera y se vayan. El bucle principal luego busca las banderas y realiza las actividades cuando llega a ellas. Esto hace que el ISR real sea muy corto y permite que una tarea de mayor prioridad interrumpa su actividad. De hecho, incluso podría usar el indicador de interrupción en el ciclo principal y no habilitar la interrupción real para él.

En esta plataforma, nunca se le garantizará que una interrupción de súper alta prioridad será atendida con una latencia constante, a menos que pueda convertirla en la única fuente de interrupción real. Siempre existe la posibilidad de que se posponga hasta que se termine uno diferente. Hay formas de reducir esa posibilidad y la consecuencia de que suceda, pero nunca eliminarla.

Habiendo dicho eso, Spehro tiene un buen punto acerca de que las interrupciones generalmente ocurren una a la vez y generalmente no son un problema. A menos que pases todo tu tiempo en el ISR; entonces casi garantizas una colisión.