ISR, volatile y ATOMIC_BLOCK

4

Desde mi experiencia hasta ahora, soy consciente de lo siguiente a lo que debo prestar atención cuando trabajo con ISR:

  • Un ISR debería completarse rápidamente
  • Las variables compartidas entre un ISR y la ruta de ejecución principal se deben declarar volatile para evitar que se optimicen los accesos
  • El acceso a las variables compartidas con 16 bits y más se debe acceder en ATOMIC_BLOCK porque la CPU de 8 bits no puede acceder a dichas variables en un solo ciclo

Pero me pregunto acerca de las funciones llamadas desde un ISR:

  1. ¿Llamar a una función desde un ISR agrega alguna sobrecarga en particular? Mirando el desmontaje, el código en una función y el mismo código directamente en el ISR dan las mismas instrucciones
  2. Supongo que todo lo que se aplica a un ISR también se aplica a una función llamada desde un ISR, ya que es la misma ruta de ejecución.
  3. Si una función llamada desde el espacio de usuario contiene un ATOMIC_BLOCK , ¿es válido llamarla también desde un ISR?
pregunta Torsten Römer

2 respuestas

4

Respuesta no específica de AVR:

Saltar a un ISR desde un subproceso de ejecución normal generalmente requiere un cambio de contexto , donde el estado actual de la CPU se guarda en algún lugar (pila, registros ocultos) para que el ISR pueda usar la CPU como necesita, y cuando termina, el ISR restaura el contexto (de modo que para el programa principal, la CPU se encuentra en el estado exacto en que se encontraba antes del ISR) y continúa. Es un salto asíncrono desde la ejecución normal: el programa no decide deliberadamente bifurcarse, algún mecanismo externo (una señal, algún hardware, lo que sea) toma la decisión.

Una llamada de función ordinaria no requiere un cambio de contexto per se. El código está siguiendo una ruta conocida, por lo que no es necesario guardar / conservar todos los registros de la CPU. Dependiendo de la función, es posible que tenga que pasarle argumentos y volver a leer un valor de retorno, pero ya que no está adelantando la ejecución normal (es decir, el programa sabe a dónde va y cuándo va a ir allí) y no espera para volver con todo como estaba antes de la llamada, no estás haciendo un cambio de contexto.

Si no ve ningún cambio de contexto en su código ISR (podría ser push, pops, etc.) entonces tal vez el hardware se encarga de eso por usted. En otros dispositivos (muchas partes de Microchip, por ejemplo) necesita cuidar el cambio de contexto en el código. En general, espero que un salto de ISR requiera un código adicional para encargarse del cambio de contexto frente a una llamada de función XXX (anulación) sin efecto.

Llamar a una función desde un ISR no es tan diferente de llamar a una función en el espacio de usuario. El código se ramificará de la misma manera que antes, con los argumentos pasados y los valores devueltos devueltos. Sin embargo, si se encuentra en un dispositivo de pila limitada, tenga cuidado: la llamada a la función estará enviando y extrayendo cosas hacia y desde la pila que probablemente ya está manteniendo el contexto del código fuera del ISR. El desbordamiento de pila es siempre una posibilidad con llamadas de función anidadas (la recursión es realmente mala para esto).

Un contenedor ATOMIC_BLOCK bloquea las interrupciones de la activación mientras se está ejecutando un segmento de código. Si ya está en un ISR, supongo que esto evitaría que el ISR sea precedido por otro ISR de mayor prioridad. Puedo entender por qué puede querer hacer esto, por lo que me parece válido (nota: no soy un experto en ARM)

    
respondido por el Adam Lawrence
1

Me gustaría enfatizar la diferencia entre los bloques atómicos y las funciones llamadas desde user & Código ISR. Solo busque los artículos de Wikipedia para Operación atómica y Reentrant .

Atomic en el nivel incrustado se ilustra mejor con operaciones de lectura-modificación-escritura. Algunos códigos de operación como INC o DEC son atómicos por diseño de hardware; porque cualquier ISR se activará solo antes o después de que se haya completado el código de operación.

Sin embargo, algunas operaciones son demasiado complejas. O algunas MCU de 32 bits como PIC32 o ARM no pueden realizar estas operaciones en el espacio de memoria (I / O). En este caso, se debe completar una operación de lectura-modificación-escritura. Se puede producir un problema si se produce una interrupción o un cambio de contexto (por ejemplo, RTOS). Es posible que el valor ya se haya leído pero aún no se haya escrito, mientras que el otro contexto cambia el valor.

Un bloque atómico generalmente desactiva las interrupciones. Esto evitará los cambios de contexto y permitirá que se completen las operaciones en los datos. Es mejor mantener cortos los bloques atómicos, para que las interrupciones se reparen lo antes posible.

Los bloques atómicos pueden ser útiles si su micro tiene un controlador de interrupción anidado. Un controlador de interrupción anidado puede anticipar la ejecución de un ISR de baja prioridad con uno de mayor prioridad.

Los microcontroladores ARM tienen ldrex y strex que cargan y almacenan variables con un monitor de hardware exclusivo. En la tienda, es posible verificar si el acceso a la variable fue exclusivo; de lo contrario, el software puede volver a realizar el cálculo e intentarlo nuevamente.

Me gustaría agregar que a pesar de que sus operaciones de hardware son atómicas, no significa que la función sea reentrante o segura para subprocesos. Reingresar significa que una función puede ser llamada nuevamente dentro de la ejecución de esa función.

Es importante tener cuidado con el lugar donde almacena el estado de la función. No trabaje en variables estáticas globales o locales. Si está escribiendo rutinas ISR, esto es inevitable ya que es el único medio de comunicación con el código principal. En este caso, me aseguraría de que cada variable solo se escriba desde una fuente (solo se escriba desde ISR o desde el código principal) y se defina como estático-global (no accesible desde el archivo).

    
respondido por el Hans

Lea otras preguntas en las etiquetas