Las interrupciones están diseñadas para hacer un poco de trabajo y luego salir. Puede pensar en ellos como un complemento de la estructura solo de bucle principal. Por ejemplo:
void main(void)
{
//startup initialization
while(1)
{
if(flag1)
{
flag1 = 0;
//do something triggered by flag1
}
if(flag2)
{
flag2 = 0;
switch(flag2_state)
{
case 0:
//do action0 from flag2
break;
case 1:
//do action1 from flag2
break;
//etc.
default:
//correct an invalid state
break;
}
flag2_state++;
}
//etc.
}
}
Observe cómo el controlador de flag2
hace cosas diferentes cada vez que se activa, pero siempre deja para permitir que el resto del código siga funcionando. Esta es la diferencia entre ejecutar en "metal desde cero" como usted lo está haciendo, y ejecutar en un administrador de tareas multiproceso como el que tendría como parte de un sistema operativo como Windows, Mac o Linux. Una vez que entiendas eso, entonces podemos introducir interrupciones:
void main(void)
{
//startup initialization
while(1)
{
if(flag1)
{
flag1 = 0;
//do something triggered by flag1
}
//etc.
}
}
interrupt [flag2] void flag2_isr(void)
{
flag2 = 0;
switch(flag2_state)
{
case 0:
//do action0 from flag2
break;
case 1:
//do action1 from flag2
break;
//etc.
default:
//correct an invalid state
break;
}
flag2_state++;
}
Aquí hago algunas suposiciones sobre la sintaxis, pero creo que entiendes la idea. Todo el punto de las interrupciones es reducir la latencia de ciertos eventos, o en otras palabras, el tiempo que lleva manejarlos.
En el primer ejemplo, si flag1
y flag2
se activaran inmediatamente después de verificar flag2
, y flag1
tardara mucho tiempo en procesarse, entonces flag2
tendría que esperar tanto. incluso empezar Eso podría no ser aceptable.
En el segundo ejemplo, flag2
se maneja inmediatamente, incluso si flag1
aún se está procesando. Pero es mucho más importante dejar el ISR porque bloquea completamente el ciclo principal para que no haga nada.
Físicamente, una interrupción es una llamada de función que el hardware realiza automáticamente en respuesta a flag2
. Debido a que esto puede suceder en cualquier momento sin previo aviso, debe diseñar bien la interacción para evitar algunos errores extraños. (La palabra clave volatile
es realmente útil aquí) También es más importante que antes borrar flag2
, o la llamada a la función volverá a suceder, una y otra vez, quemando la máquina de estados como si no hubiera existido. , y bloqueando el bucle principal casi como si no estuvieras abandonando el ISR en absoluto.
Una vez que entiendas eso, puedo explicar que la razón por la que el código de bloqueo (tu ejemplo original) funciona en un sistema operativo multitarea (hay varias formas de hacerlo; esta es solo una de ellas) es que el sistema operativo tiene una interrupción invisible para el usuario que se activa desde un temporizador. Esto puede hacer todo tipo de cosas que se basan en un intervalo de tiempo, además de cambiar de tareas. Para hacer el cambio, simplemente guarde el puntero de la pila (¿en qué contexto estoy ejecutando?) En una biblioteca administrada por el sistema operativo y cargue una diferente de la misma biblioteca. Ahora, cuando el ISR se va, va a una tarea diferente. Por lo tanto, cada tarea se puede escribir como si fuera la única cosa en ejecución. No es así porque no tienes ese conmutador de contexto.