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

9

Tengo un kit LPC1114. Los últimos días he estado desenterrando la implementación de CMSIS de Cortex M0 para encontrar cómo se hacen las cosas en él. Hasta ahora entendí cómo se asignan cada registro y cómo puedo acceder a él. Pero aún no sé cómo se implementan las interrupciones. Todo lo que sé sobre las interrupciones en CMSIS es que hay algunos nombres de controladores de interrupciones mencionados en el archivo de inicio. Y puedo escribir mis propios controladores 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 asigna la tabla de interrupciones en el CMSIS?
  3. ¿Cuáles son las principales diferencias entre NVIC y la implementación de interrupción en AVR / PIC? (excepto que NVIC se puede asignar en cualquier lugar del flash)
0xakhil
fuente

Respuestas:

14

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

Desde una perspectiva de programación en C, los manejadores de interrupciones se definen en el archivo cr_startup_xxx.c (por ejemplo, el archivo cr_startup_lpc13.c para LPC1343). Todos los posibles manejadores de interrupciones 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 resolverá qué función incluir en el binario final junto con la tabla de vectores de interrupción de cr_startup_xxx.c

Se muestran ejemplos de interrupciones de GPIO desde puertos en los archivos de demostración en gpio.c. Hay una entrada de interrupción a la 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, entonces habilitaría los bits de interrupción individuales PIO1_4 y PIO1_5 en GPIO0IE. Cuando se activa su función de controlador de interrupciones PIOINT0_Handler (), depende de usted determinar cuáles de las interrupciones PIO1_4 o PIO1_5 (o ambas) están pendientes leyendo el registro GPIO0RIS y manejando la interrupción adecuadamente.

Austin Phillips
fuente
10

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

  1. En los chips NXP más grandes (como LPC17xx) hay un par de pines de interrupción dedicados (EINTn) que tienen su propio controlador de interrupciones. 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 desencadenado la interrupción.
  2. No estoy muy familiarizado con LPC11xx, pero parece que tiene una interrupción por puerto GPIO. Tendrá que verificar nuevamente el registro de estado para descubrir 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 activarán cuando esté en estado de suspensión).
  3. La tabla de manejador predeterminada 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 repara un par de los primeros (como NMI y HardFault), el resto son específicos de chip. Si necesita cambiar los vectores en tiempo de ejecución, puede reasignarlo a 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 prioridad más baja se reanuda cuando finaliza la interrupción de prioridad más alta.
    • apilamiento automático del estado del procesador en la entrada de interrupción; esto permite escribir manejadores de interrupciones directamente en C y elimina la necesidad de envoltorios de ensamblaje.
    • encadenamiento de la cola: en lugar de hacer estallar y presionar 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.

Como 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.

Igor Skochinsky
fuente
8

Las respuestas de Austin e Igor son lo suficientemente detalladas. Sin embargo, quiero responder de otra manera, tal vez lo encuentres ú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 de bloque de conexión de pin.
  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 GPIOnIE de la máscara de interrupción GPIO y configurar la lógica de bit (que corresponde al pin) 1.
  4. Configure la interrupción para el borde ascendente o el borde descendente o ambos modificando los registros de detección de interrupción GPIO GPIOnIBE y GPIOnIS.
  5. Habilite la fuente de interrupción PIO_0 / PIO_1 / PIO_2 / PIO_3 en el Control de interrupción vectorial anidado utilizando las 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 el startup_LPC11xx.sarchivo de códigos de inicio . 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
}
Phuong Pham
fuente