Definir devoluciones de llamada para interrupciones

Estoy trabajando con un STM32 y estoy un poco confundido acerca de las interrupciones, específicamente las interrupciones vectoriales anidadas (NVI). Según tengo entendido, hay un vector NVI (llamado NVIC) donde cada interrupción tiene una prioridad (a veces configurable) y una dirección (consulte la página 157 del manual de referencia de ARM aquí ).

Ahora asumo que para cada tipo de interrupción es posible adjuntar una función de devolución de llamada, y sospecho que la dirección adjunta a cada interrupción está relacionada con la dirección de la devolución de llamada.

¿Cuál es exactamente la dirección adjunta a una interrupción dada? ¿Cómo puedo definir (en C, digamos) la función de devolución de llamada para una interrupción determinada?

No sé de primera mano sobre el brazo, pero muchos procesadores tienen una instrucción especial de retorno de interrupción, que extrae la información adicional de la pila que generalmente viene con una interrupción. Sin embargo, solo es una preocupación real si está escribiendo en ensamblador; la mayoría de los compiladores de C le permiten decorar su función con la palabra 'interrupción', y el compilador se encarga de elegir el código de salida correcto.
@JustJeff: ¿Podría señalar la documentación sobre esta decoración de 'interrupción'?
por lo general, es trivial: simplemente coloque la palabra 'interrupción' delante del tipo de retorno de la función. Entonces, en lugar de void foo() { ... } , tendrías que interrumpir void foo() { ... }
@JustJeff Solo para tener en cuenta que la sintaxis para definir una interrupción depende del combo compilador/enlazador y no del procesador de destino. Por ejemplo, el compilador ARM GCC y el compilador ARM IAR pueden definir la sintaxis para una función de interrupción de manera diferente a pesar de que están creando código para el mismo procesador.
@AngryEE: no creo que haya dicho que depende de la arquitectura de destino. Pero sí, los detalles pueden variar según las circunstancias. Solo quería señalar el hecho de que generalmente hay algo que debe hacer, ya que la pila se configura de manera un poco diferente para las interrupciones que para las llamadas regulares.
github.com/dwelch67 mire mbed, stm32f4d, stm32vld y otros. Sé que stm32f4d/blinker05 tiene una interrupción. es un cortex-m4 pero por lo que pides da igual, las diferencias no estan en esa zona.

Respuestas (2)

Los ARM implementan una tabla de interrupciones para almacenar la dirección de cada controlador de interrupciones (o devolución de llamada, básicamente lo mismo). Básicamente, todas las direcciones de los controladores de interrupción se almacenan en la memoria del programa en una ubicación predefinida. Cuando ocurre una interrupción, el procesador sabe en qué parte de la tabla está la entrada de esa interrupción, la toma y se bifurca a la dirección almacenada allí.

¿Cómo se llena esta tabla? Por lo general, toda esta información está contenida en un archivo de inicio. Por lo general, solo define una matriz constante de punteros, llénelo con las direcciones de las devoluciones de llamada que desea usar. Aquí hay un sitio web que detalla cómo crear un archivo de inicio para un microcontrolador ARM específico. Lo complicado de esto es que casi todos los detalles de la creación del archivo dependen en gran medida del enlazador, el compilador y el chip que está utilizando, por lo que tendrá que encontrar un ejemplo o intentar crear uno propio.

el cortex-m3 es muy amigable con las interrupciones. No necesita un trampolín en asm ni necesita agregar ninguna sintaxis de compilador no estándar para que el compilador lo haga por usted. El hardware se ajusta a un abi conservando una cierta cantidad de registros y cambiando de modo. El registro de enlace está codificado para que, al regresar de la función, el hardware sepa que en realidad es un retorno de interrupción. Entonces, todas las cosas que tenía que hacer en un brazo y muchos otros procesadores no tiene que hacerlo.

Del mismo modo, a un nivel de dolor, la corteza-m (y otros brazos más nuevos) tienen un trillón de vectores en la tabla de vectores, docenas a cientos de interrupciones, etc., como se menciona en un comentario anterior, consulte http://github.com/ dwelch67/stm32f4d el ejemplo de blinker05 usa interrupciones con un temporizador, puedes ver en vectores.s que lo único que haces es colocar el nombre de la función:

.word hang
.word tim5_handler
.word hang

Y luego escribe el código C:

//-------------------------------------------------------------------
volatile unsigned int intcounter;
//-------------------------------------------------------------------
// CAREFUL, THIS IS AN INTERRUPT HANDLER
void tim5_handler ( void )
{
    intcounter++;
    PUT32(TIM5BASE+0x10,0x00000000);
}
// CAREFUL, THIS IS AN INTERRUPT HANDLER
//------------------------------------------------------------------

Ahora, al igual que con cualquier interrupción de algún periférico/dispositivo, probablemente tenga que, en el controlador, decirle al dispositivo que elimine la interrupción; de lo contrario, podría quedarse atascado constantemente volviendo a ingresar al controlador.

Mis ejemplos no usan un IDE, usan herramientas de código abierto (gnu gcc y binutils y el compilador clang de llvm). Elimine los binarios clang de la línea all: en el archivo MAKE si no tiene/quiere usar llvm. Las herramientas gnu son fáciles de conseguir, ya sea compilando a partir de fuentes (tengo instrucciones en algún lugar de github, probablemente en varios lugares) o simplemente obtenga la versión lite de codesourcery (ahora mentor graphics). Mis ejemplos están desarrollados y probados en Linux, pero eso no debería desanimar a los usuarios de Windows, cambie algunas cosas como rm -f *.o a del *.o, cosas así o simplemente cree un archivo por lotes a partir de los comandos ensamblador/compilador/enlazador. en el archivo MAKE.

Recomiendo encarecidamente desensamblar su binario, especialmente si está tratando de colocar un controlador en la tabla de vectores, con tantos es fácil contar mal y no tenerlo en la dirección correcta. Necesita saber la dirección de los documentos del brazo y luego verifique el desmontaje. el ejemplo de blinker05 cuando se desmonta:

 8000100:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}
 8000104:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}
 8000108:       08000179        stmdaeq r0, {r0, r3, r4, r5, r6, r8}
 800010c:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}
 8000110:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}

offset 0x108 siendo la entrada objetivo. Tenga en cuenta que las direcciones en la tabla deben ser impares 0x178 es la dirección real, arm quiere que el conjunto de lsbit indique que es una dirección de conjunto de instrucciones de pulgar (cortex-m3 solo conoce extensiones de pulgar y thumb2, no puede ejecutar instrucciones de brazo).