En el mundo de los microcontroladores, la capacidad de reaccionar rápidamente a eventos externos es fundamental. Para resolver este tipo de problemas, los microprocesadores incorporan el concepto de interrupción, que es un mecanismo que permite asociar una función a la ocurrencia de un determinado evento. Una interrupción es una señal que detiene la actividad actual del procesador para ejecutar otra función distinta. Una interrupción es una suspensión temporal de la ejecución de un proceso, para pasar a ejecutar una subrutina de servicio de interrupción, la cual, por lo general, no forma parte del programa. Una vez finalizada dicha subrutina, se reanuda la ejecución del programa. El uso de interrupciones es casi obligatorio en un programa avanzado de un microcontrolador.
Las interrupciones son útiles para hacer que las acciones o eventos sucedan automáticamente en los programas de microcontrolador y pueden ayudar a resolver problemas de tiempo y ejecución de los mismos. Las interrupciones con Arduino nos permitirán reaccionar a eventos externos a la placa de una forma rápida. Esto nos va a permitir dos cosas. Por ejemplo, queremos que un LED se encienda o se apague cuando pulsamos un botón. La probabilidad de capturar el evento cuando se pulsa el botón disminuye con el aumento de tiempo de parpadeo. En estos casos, y en muchos otros, nos interesa liberar el procesador de Arduino para que solo cuando se pulse el botón, haga una acción determinada. Los controladores de interrupciones: administran la ejecución de tareas por interrupciones, lo cual permite la multitarea.
Un proceso síncrono es aquel que se ejecuta y hasta que no finaliza solo se ejecuta ese proceso (todo en el mismo loop), mientras que uno asíncrono comienza la ejecución y continúan ejecutándose otras tareas por lo que el proceso total se completa en varios loops. Normalmente no se ve, pero las interrupciones ocurren constantemente durante la operación normal de Arduino.
Tipos de Interrupciones en Arduino
La interrupción puede iniciarse debido a una señal externa, por ejemplo, la pulsación de un botón, o interna, por ejemplo, un temporizador o una señal de software. Internamente, Arduino (mejor dicho el microcontrolador AtMega) tiene ciertas interrupciones configuradas que lanza según la situación. El procesador dentro de cualquier Arduino tiene dos tipos de interrupciones: “externas” y “cambio de pin”. Dentro de la gama de interrupciones con Arduino están las interrupciones programadas y las interrupciones externas. La filosofía es la misma, poder ejecutar un código cuando sucede un evento concreto.
Interrupciones Externas (Hardware)
Interrupciones HW o externas: Estas son asíncronas a la ejecución del procesador, es decir, se pueden producir en cualquier momento independientemente de lo que esté haciendo el CPU en ese momento. Para las interrupciones externas o hardware, solo hay dos pines que las soportan en los ATmega 328 (p.e. Arduino UNO), son las INT0 y INT1 que están mapeadas a los pines 2 y 3. Las interrupciones de hardware, también conocidas como INT0 e INT1, llaman a una rutina de servicio de interrupción cuando algo sucede con uno de los pines asociados. Estas interrupciones se pueden configurar con disparadores en RISING o FALLING para flancos o en nivel LOW.
Lea también: Explora la importancia cultural de la Casa 6 en Campeche
Los pines específicos con interrupciones, y su correlación con las interrupciones número varía en cada tipo de tarjeta. Aquí un resumen de los pines de interrupción para diferentes placas Arduino:
| Placa Arduino | Pin Digital | Número de Interrupción (INT) |
|---|---|---|
| Arduino UNO | 2 | 0 (INT0) |
| Arduino UNO | 3 | 1 (INT1) |
| Arduino Mega | 2 | 0 (INT0) |
| Arduino Mega | 3 | 1 (INT1) |
| Arduino Mega | 18 | 5 (INT5) |
| Arduino Mega | 19 | 4 (INT4) |
| Arduino Mega | 20 | 3 (INT3) |
| Arduino Mega | 21 | 2 (INT2) |
| Arduino Due | Todos los pines digitales | Número de pin directo |
| Arduino Zero | Todos los pines digitales (excepto pin 4) | Número de pin directo |
Interrupciones por Cambio de Pin (Pin Change Interrupts)
Los microcontroladores AVR pueden tener una interrupción ante un cambio en cualquier pin, es lo que denomina pin change interrupt. Estas interrupciones no son soportadas directamente por Arduino y necesitan ser accedidas a través de una librería adicional. Las interrupciones de cambio de pin pueden habilitarse en más pines. Para los ATmega 328, pueden habilitarse en cualquiera de los pines de señal disponibles. Estas son disparadas igual en flancos RISING o FALLING, pero depende del código de la interrupción configurar el pin que recibe la interrupción y determinar qué ha pasado.
Las interrupciones de cambio de pin están agrupadas en 3 puertos de la MCU, por lo tanto hay 3 vectores de interrupciones para todo el conjunto de pines. Si necesita más pines para interrupciones, hay un mecanismo para generar una interrupción cuando se cambia cualquier pin en uno de los puertos de 8 bits. No sabes pin, sino sólo en qué puerto. En primer lugar, los indicadores de habilitación de interrupción de cambio de pin deben ajustarse en el registro PCICR. Estos son los bits PCIE0, PCIE1 y PCIE2 para los grupos de pines PCINT7..0, PCINT14..8 y PCINT23..16 respectivamente. Los pines individuales se pueden activar o deshabilitar en los registros PCMSK0, PCMSK1 y PCMSK2. En el circuito Arduino, en combinación con la hoja de datos Atmel Atmega328, se puede ver que el pin PCINT0 corresponde al pin 0 en el puerto B (llamado PB0). Este es el pin 8 en el Arduino Uno.
También disponemos de la PinChangeInt Library. PinChangeInt implementa interrupciones de cambio de pin para el entorno Arduino. Esta biblioteca fue diseñada para el Arduino Uno / Duemilanove, y funciona bien en el Nano.
Otros tipos de Interrupciones
- Interrupciones SW: Las interrupciones por software son aquellas generadas por un programa en ejecución. Para generarlas, existen distintas instrucciones en el código máquina que permiten al programador producir una interrupción.
- Interrupciones por Temporizador (Timer): Un evento programado o Timer. Arduino tiene tres timers. Crearemos una interrupción interna utilizando la interrupción de un temporizador. Para ello necesitaremos una librería adecuada para el temporizador que usemos.
- Excepciones: Son aquellas que se producen de forma síncrona a la ejecución del procesador típicamente causada por una condición de error en un programa.
Uso de la función attachInterrupt()
Para configurar una interrupción en Arduino se usa la función attachInterrupt(). La ventaja es que Arduino tiene una simple rutina de configuración para conectar la rutina de servicio de interrupción al evento: attachInterrupt (). Para poder utilizar interrupciones con Arduino no necesitamos incorporar ningún tipo de librería a nuestro programa. Únicamente necesitamos hacer referencias a ciertas funciones o métodos.
Lea también: ¿Qué son las instituciones sociales?
Sintaxis
La sintaxis general de la función attachInterrupt() es la siguiente:
attachInterrupt(interrupt, ISR, mode)
Para placas como Arduino Due y Zero, también se permite:
attachInterrupt(pin, ISR, mode) (Arduino Due solamente)
Parámetros
- interrupt: Este parámetro representa el número de la interrupción (int). El primer parámetro para attachInterrupt() es un número de interrupción. Para saber qué número de interrupción está asociado a un pin, debemos usar la función digitalPinToInterrupt(pin). Este es el método recomendado por Arduino para pasar este parámetro, en lugar de colocar un número de interrupción directamente en su boceto. Por ejemplo, si queremos utilizar el pin 2 llamaríamos a la función digitalPinToInterrupt(2). Esto nos devolverá un 0.
- pin: Número del pin (solo para Arduino Due y Zero). Para estas placas, puedes especificar directamente el número del pin en attachInterrupt().
- ISR: La ISR (Interrupt Service Routine) es la función que se llama cuando ocurre la interrupción. Esta función no tiene parámetros y no devuelve nada. Es una abreviatura de Interrupt Service Routine y no es más que la función o método que se llama cuando se produce la interrupción. Es de un tipo particular ya que no admite parámetros y tampoco devuelve ningún valor.
- mode: Define cuándo debe ser disparada la interrupción. Cuatro constantes están predefinidas como valores válidos:
- LOW: para activar la interrupción siempre que el pin esté en estado bajo.
- CHANGE: para activar la interrupción siempre que el pin cambie de valor (de bajo a alto o de alto a bajo).
- RISING: para activar cuando el pin pasa de bajo a alto.
- FALLING: para cuando el pin pasa de alto a bajo.
- HIGH: para activar la interrupción siempre que el pin esté en estado alto.
Si attachInterrupt() nos permite configurar un pin como interrupción, el método detachInterrupt() anula esa configuración.
Consideraciones al Usar Rutinas de Servicio de Interrupción (ISRs)
La función de callback asociada a una interrupción se denomina ISR (Interrupt Service Routine). Los ISR son tipos especiales de funciones que tienen algunas limitaciones únicas que la mayoría de las otras funciones no tienen. En general, un ISR debe ser lo más corto y rápido posible. Si su programa usa múltiples ISR, solo uno puede ejecutarse a la vez, otras interrupciones se ejecutarán después de que finalice la actual en un orden que depende de la prioridad que tengan. Dos ISR no pueden ejecutarse de forma simultánea. Recuerda que se trata de ejecutar un código lo más rápido posible.
Limitaciones dentro de un ISR
- delay(): Dentro de la función llamada desde la interrupción, la función delay() no funciona. delay() requiere interrupciones para trabajar, por lo que no funcionará si se le llama en el interior de una ISR.
- millis(): El valor devuelto por millis() no aumenta dentro de un ISR. millis() se basa en las interrupciones para contar, por lo que nunca se incrementará dentro de un ISR.
- micros(): La función micros() funciona dentro del ISR pero después de 1-2 ms se empieza a comportar de forma extraña.
- delayMicroseconds(): Esta función no usa ningún contador y funcionará correctamente dentro del ISR.
- Comunicación Serie: Los datos serie recibidos mientras la función está activa se pueden perder.
Uso de Variables Volátiles
Para poder modificar una variable externa a la ISR dentro de la misma debemos declararla como “volatile”. Declarar una variable volátil es una directiva para el compilador. Específicamente, dice al compilador que cargue la variable desde la RAM y no desde un registro de almacenamiento, que es una ubicación de memoria temporal donde se almacenan y manipulan las variables del programa. Una variable debe ser declarada volátil siempre que su valor pueda ser cambiado por algo más allá del control de la sección de código en la que aparece, como un subproceso que se ejecuta simultáneamente. El indicador “volatile” indica al compilador que la variable tiene que ser consultada siempre antes de ser usada, dado que puede haber sido modificada de forma ajena al flujo normal del programa (lo que, precisamente, hace una interrupción). Al indicar una variable como Volatile el compilador desactiva ciertas optimizaciones, lo que supone una pérdida de eficiencia. Normalmente, las variables globales se utilizan para pasar datos entre un ISR y el programa principal.
Lea también: descubre los sitios patrimonio cultural de México
Habilitar y Deshabilitar Interrupciones
Las interrupciones permiten a ciertas tareas importantes que se ejecuten en segundo plano y por defecto las interrupciones están habilitadas. Habilita las interrupciones (antes han debido ser inhabilitadas con noInterrupts()). Deshabilita las interrupciones. Algunas funciones no funcionarán si se deshabilitan las interrupciones y las comunicaciones entrantes serán ignoradas, también podrán modificar ligeramente la temporización del código. La función delay() no deshabilita las interrupciones, por lo tanto los datos recibidos por el serial Rx son guardados, los valores PWM funcionan y las interrupciones externas funcionan.
Registros de Interrupción
Cada interrupción está asociada con dos bits, un bit de indicador de interrupción (flag) y un bit habilitado de interrupción (enabled). El bit de indicador de interrupción se establece siempre que se produce el evento de interrupción, independientemente de que la interrupción esté o no habilitada. El bit activado por interrupción se utiliza para activar o desactivar una interrupción específica. Básicamente se le dice al microcontrolador si debe o no responder a la interrupción si se dispara. Para el microcontrolador AVR de 8 bits, este bit se encuentra en el registro de estado de E/S (SREG). Hay muchos I/O registros en el microcontrolador asociados a las interrupciones externas.
tags: #arduino #attachinterrupt #no #fue #declarada #solución
