ARM: Interrupciones

En ocasiones, en vez de esperar a que llegue el turno del if que muestrea el estado del botón que estamos pulsando para que ocurran cosas, nos interesa que el microcontrolador esté atento al botón y actúe inmediatamente cuando se pulsa.

Recuerdo el concepto de interrupción en la parte de PICs:

La interrupción se definiría como decirle al PIC “deja absolutamente todo lo que estés haciendo y atiende a la función que he escrito para cuando ésto ocurra”

Un ARM se comporta casi de la misma forma, lo que se añade de nuevo es una lista de prioridades para las distintas interrupciones, debido a que éstos microcontroladores son capaces de llevar muchas más, y están diseñados de forma que pueden atender a todas, si no en el acto, un poco después al finalizar con la anterior.

Por ejemplo, la placa STM32F4Discovery soporta 240 fuentes de interrupción distintas y 256  niveles de prioridad.

Las interrupciones de las que hablaremos ahora son las exteriores, dadas por los pines (EXTI0,1,2,...)

Los ARM disponen de un controlador de interrupciones llamado Nested Vectored Interrupt Controller (NVIC), que compara la prioridad de la interrupción que acaba de saltar con la prioridad de la que se está ejecutando.

CMSIS utiliza unos números (IRQn) para identificar las interrupciones, siendo IRQn = 0 la primera y los valores negativos "excepciones del núcleo del procesador". Por ejemplo el timer principal es el -1 y la línea 0 de interrupciones externas  el 6.

Una vez definida una interrupción, solo hay que ir a la librería stm32f4xx_it.c y añadir la función handler (ISR) correspondiente a dicha interrupción.

Veamos un ejemplo en el que se configura el pin PA0 (botón de usuario) para encender/apagar un LED mediante interrupción:

void main(){
   LED_Init(LED3) //Configura el pin del LED como salida, no voy a entrar en detalle
   EXTILine0_Config() //Configura el pin PA0 como entrada y su interrupción

   while(1); //Esperar a que llegue la interrupción
}

void EXTILine0_Config(){

   //Se declaran las estructuras
   GPIO_InitTypeDef GPIO_InitStructure;
   EXTI_InitTypeDef EXTI_InitStructure;
   NVIC_InitTypeDef NVIC_InitStructure;
 
   //Se le da reloj a GPIOA
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
   //Y también a SYSCFG con APB2
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

   //Se configura el pin PA0 como entrada
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
   GPIO_Init(GPIOA, &GPIO_InitStructure);

   //Se conecta el pin PA0 a la línea 0 de EXTI
   SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

   //Se configura la línea 0
   EXTI_InitStructure.EXTI_Line = EXTI_Line0; //Nos referimos a la línea 0
   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //Va a generar interrupción
   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //Con el flanco de subida del botón
   EXTI_InitStructure.EXTI_LineCmd = EXTI_LineCmd_ENABLE; //Habilitamos la línea
   EXTI_Init(&EXTI_InitStructure); //Función de configuración con los parámetros dados

   //Ahora se habilita la interrupción y se le da el nivel de prioridad más bajo
   NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;  //A qué EXTI nos referimos
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //Define la prioridad
   //con la que se ejecuta esta interrupción si ya hay otra en marcha, dejando la
   //otra para otro momento si es necesario. A menor número mayor prioridad
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
   //Si hay 2 interrupciones con la misma PreemptionPriority se define aquí la
   //prioridad que tiene ésta sobre la otra, el número más bajo es el más urgente
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //Habilita
   NVIC_Init(&NVIC_InitStructure); //Configura con los parámetros dados
}

A continuación, nos vamos a la librería stm32f4xx_it.c  y abrimos una nueva función que será el handler:

void EXTI0_IRQHandler(){
   if(EXTI_GetITStatus(EXTI_Line0) != RESET){   //Esto es como una bandera
   //Si no está en RESET significa que se ha activado la interrupción
      LED_Toggle(LED3) //Cambia el estado del LED al contrario del que estaba
      EXTI_ClearITPendingBit(EXTI_Line0) //Pone la bandera a RESET
   }
}

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *