¿Cómo se implementan los controladores de interrupciones en CMSIS de Cortex M0?

Tengo un equipo LPC1114. Los últimos días he estado investigando la implementación de CMSIS de Cortex M0 para encontrar cómo se hacen las cosas en él. Hasta ahora entendí cómo se mapean cada registro y cómo puedo acceder a él. Pero aún no sé cómo se implementan las interrupciones en él. Todo lo que sé acerca de las interrupciones en CMSIS es que hay algunos nombres de controladores de interrupciones mencionados en el archivo de inicio. Y puedo escribir mis propios manejadores simplemente escribiendo una función C con los mismos nombres mencionados en el archivo de inicio. Lo que me confunde es que en la guía del usuario se dice que todos los GPIO se pueden usar como fuentes de interrupción externas. Pero solo hay 4 interrupciones PIO mencionadas en el archivo de inicio. Así que dime:

  1. ¿Cómo puedo implementar controladores de interrupción externos para otros GPIO?
  2. ¿Dónde se mapea la tabla de interrupciones en el CMSIS?
  3. ¿Cuáles son las principales diferencias entre NVIC y la implementación de interrupciones en AVR/PIC? (excepto que NVIC se puede mapear en cualquier parte del flash)

Respuestas (3)

La siguiente información se suma a la excelente respuesta de Igor.

Desde una perspectiva de programación C, los controladores de interrupción se definen en el archivo cr_startup_xxx.c (por ejemplo, el archivo cr_startup_lpc13.c para LPC1343). Todos los manejadores de interrupciones posibles se definen allí como un alias DÉBIL. Si no define su propio XXX_Handler() para una fuente de interrupción, se utilizará la función de controlador de interrupción predeterminada definida en este archivo. El enlazador decidirá qué función incluir en el binario final junto con la tabla de vectores de interrupción de cr_startup_xxx.c

En los archivos de demostración en gpio.c se muestran ejemplos de interrupciones GPIO desde los puertos. Hay una entrada de interrupción al NVIC por puerto GPIO. Cada bit individual en el puerto se puede habilitar/deshabilitar para generar una interrupción en ese puerto. Si necesita interrupciones en los puertos PIO1_4 y PIO1_5, por ejemplo, debe habilitar los bits de interrupción individuales PIO1_4 y PIO1_5 en GPIO0IE. Cuando se activa la función del controlador de interrupciones PIOINT0_Handler(), depende de usted determinar cuál de las interrupciones PIO1_4 o PIO1_5 (o ambas) está pendiente leyendo el registro GPIO0RIS y manejando la interrupción de manera adecuada.

(Tenga en cuenta que los puntos 1 y 2 son detalles de implementación y no limitaciones arquitectónicas).

  1. En chips NXP más grandes (como LPC17xx) hay un par de pines de interrupción dedicados (EINTn) que tienen su propio controlador de interrupción. El resto de GPIO tienen que usar una interrupción común (EINT3). Luego puede sondear el registro de estado de interrupción para ver qué pines han activado la interrupción.
  2. No estoy muy familiarizado con LPC11xx pero parece que tiene una interrupción por puerto GPIO. Nuevamente tendrá que verificar el registro de estado para averiguar los pines específicos. También hay hasta 12 pines que pueden actuar como fuentes de activación. No estoy seguro de si puede secuestrarlos como interrupciones generales (es decir, probablemente solo se activen cuando estén en estado de suspensión).
  3. La tabla del controlador predeterminado se coloca en la dirección 0 (que está en flash). La primera entrada es el valor de reinicio para el registro SP, la segunda es el vector de reinicio y el resto son otras excepciones y vectores de interrupción. ARM corrige algunos de los primeros (como NMI y HardFault), el resto son específicos del chip. Si necesita cambiar los vectores en tiempo de ejecución, puede reasignarlos a la RAM (primero debe copiar la tabla). En LPC11xx, la reasignación se fija al inicio de SRAM (0x10000000), otros chips pueden ser más flexibles.
  4. El NVIC está optimizado para un manejo eficiente de interrupciones:
    • nivel de prioridad programable de 0-3 para cada interrupción. Una interrupción de mayor prioridad se adelanta a las de menor prioridad (anidamiento). La ejecución de la interrupción de menor prioridad se reanuda cuando finaliza la interrupción de mayor prioridad.
    • apilamiento automático del estado del procesador en la entrada de interrupción; esto permite escribir controladores de interrupción directamente en C y elimina la necesidad de envoltorios de ensamblaje.
    • encadenamiento de cola: en lugar de hacer estallar y empujar el estado nuevamente, la siguiente interrupción pendiente se maneja de inmediato
    • llegada tardía: si llega una interrupción de mayor prioridad mientras se apila el estado del procesador, se ejecuta inmediatamente en lugar de la que estaba pendiente anteriormente.

Ya que está familiarizado con los PIC, eche un vistazo a esta Nota de aplicación: Migración de microcontroladores PIC a Cortex-M3

Se trata de M3, pero la mayoría de los puntos también se aplican a M0.

Las respuestas de Austin e Igor son lo suficientemente detalladas. Sin embargo, quiero responderla de otra manera, tal vez te resulte útil.

El LPC11xx (Cortex-M0) tiene 4 niveles para pines GPIO, todos los pines de GPIO0.0 a GPIO0.n comparten el mismo número de interrupción y todos los pines de GPIO3.0 a GPIO3.m comparten el mismo número de interrupción.

Hay seis pasos para inicializar la interrupción GPIO en LPC11xx

  1. Configure la función de pin modificando los registros del bloque de conexión de pines.
  2. Configure la dirección del pin modificando el registro de dirección de datos GPIO (se ingresa el valor predeterminado).
  3. Configure la interrupción para cada pin individual, debe ir al registro de máscara de interrupción GPIO GPIOnIE y configurar el bit (que corresponde al pin) lógica 1.
  4. Configure la interrupción para el flanco ascendente o descendente o ambos modificando los registros de detección de interrupción GPIO GPIOnIBE y GPIOnIS.
  5. Habilite la fuente de interrupción, ya sea PIO_0/PIO_1/PIO_2/PIO_3 en el control de interrupción vectorial anidado mediante funciones CMSIS.
  6. Establezca la prioridad de interrupción mediante el uso de funciones CMSIS.

Implementaciones de código. Necesita dos funciones: una inicializa los 6 pasos anteriores y la segunda es el controlador de interrupciones, que debe tener el mismo nombre que el controlador definido en los códigos de inicio, startup_LPC11xx.sarchivo. Los nombres son de PIOINT0_IRQHandlera PIOINT3_IRQHandler. Si usa un nombre diferente, debe cambiar los nombres en el archivo de inicio.

/*Init the GPIO pin for interrupt control */
void GPIO_Init(){
    LPC_IOCON-> =..              //Pin configuration register
    LPC_GPIO1->FIODIR = ...      //GPIO Data direction register
    LPC_GPIO1->FIOMASK = ..      //GPIO Data mask register - choose  the right pin
    LPC_GPIO1->GPIOnIE = ..      //Set up falling or rising edge 
    NVIC_EnableIRQ(PIO_1);       //Call API to enable interrupt in NVIC
    NVIC_SetPriority(PriorityN); //Set priority if needed
}


/*Must have the same name as listed in start-up file startup_LPC11xx.s */
void PIOINT1_IRQHandler(void){
   //Do something here
}