¿Que es una interrupción?
Una interrupción es una señal externa o interna que le indica al microcontrolador que un proceso debe ser atendido inmediatamente, y dispara un tipo único de función llamada rutina de servicio a interrupciones (Interrupt Service Routine ISR). Cuando se activa la interrupción, la ISR será llamada no importando lo que esté haciendo el microcontrolador.
Las interrupciones juegan un papel fundamental, en especial en la operación de dispositivos E/S. Sin ellas el sistema debería chequear constantemente los dispositivos para comprobar su actividad,
En todas las placas Arduino hay múltiples interrupciones que pueden ser controladas, sin embargo su manejo corresponde a funciones avanzadas. En la placa Arduino UNO solo tenemos dos interrupciones (externas) disponibles con el conjunto de instrucciones básicas. Estas interrupciones se encuentran en los pines digitales 2 y 3, y se llaman interrupción 0 y 1 respectivamente.
Las interrupciones externas (INT0 e INT1) de Arduino pueden ejecutarse porque la señal de entrada de un pin:
- Está en nivel bajo (tiene el valor de 0) LOW
- Ha cambiado de estado (tanto para flanco de subida como de bajada) CHANGE
- Cambia a nivel alto (flanco de subida) RISING
- Cambia a nivel bajo (flanco de bajada) FALLING
Funciones para el manejo de interrupciones
Hay 4 funciones básicas para el uso de interrupciones, y estas son:
interrupts()
Vuelve a habilitar las interrupciones (después que han sido inhabilitadas por noInterrupts()). Las interrupciones están habilitadas por defecto.
nointerrupts()
Deshabilita las interrupciones (puedes volver a habilitarlas con interrupts()).
Ejemplo 1 – Habilitar y deshabilitar las interrupciones
void setup() {} void loop() { noInterrupts(); //Deshabilita las interrupciones // Código critico a ejecutarse sin interrupciones interrupts(); //habilita las interrupciones // más código aquí }
attachInterrupt()
Habilita la interrupción indicando cual pin digital es el usado, cual es la función ISR y el modo de trabajo. La sintaxis es la siguiente:
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode); donde pin: Es el número de pin que se usara para la interrupción (pin 2 o pin 3) ISR: La función ISR a llamar cuando la interrupción ocurra; esta función no tiene parámetros ni devolverá ningún valor. Se le llama por su nombre. Mode: Indica cómo se activara la interrupción (LOW, CHANGE, RISING o FALLING)
detachInterrupt()
Desactiva la interrupción. La sintaxis es la siguiente:
detachInterrupt(digitalPinToInterrupt(pin)); donde pin: Es el número de pin que se usara para la interrupción (pin 2 o pin 3)
¿Como funcionan las ISR en Arduino?
Las ISR son un tipo especial de funciones las cuales tienen algunas limitaciones, en principio estas no tienen parámetros y tampoco devuelven ningún valor. Generalmente, una ISR debe ser tan corta como sea posible, si nuestro programa usa varias ISR solo una podrá correr a la vez. La función millis() no cambiara su valor mientras este en la función, tampoco delay() funcionara, micros() funcionara bien siempre que no pasemos de 2ms, y delaymicroseconds() funcionara bien.
Típicamente las variables globales son usadas para intercambiar datos entre una ISR y el programa principal, y para mantener estas variables correctamente actualizadas debemos declararlas como “volatile”.
Ejemplo 2 – Un Led cambia de estado cada vez que pulsamos un boton
const byte ledPin = 13; //declaramos una constante con el número de pin const byte interruptPin = 2; //declaramos una constante con el pin de la interrupción volatile byte estadop = LOW; //Declaramos un variable global tipo byte “volatile” void setup() { pinMode(ledPin, OUTPUT); //configuramos el pin como salida pinMode(interruptPin, INPUT_PULLUP); //configuramos el pin 2 como entrada attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE); //configura la interrupción } void loop() { digitalWrite(ledPin, estadop); //se enciende el led de acuerdo al valor de la variable estadop } void blink() { //se declara la función ISR estadop = !estadop; //instrucción a ejecutar en la ISR }
Ejemplo 3
int contador = 0; //define una variable global void setup() { Serial.begin(9600); //inicializa el monitor serial pinMode(2,INPUT_PULLUP); //configura el pin 2 como entrada con Resistencia pullup attachInterrupt(digitalPinToInterrupt(2),ServicioBoton,FALLING); //configura la interrupción } void loop() { } void ServicioBoton() //función ISR de atención de la interrupción { contador++; //incrementa el contador Serial.println(contador); //imprime el valor de la variable contador }
Ejercicios
Ejercicio 1: Pulsador por interrupción
Crear un programa que incremente el valor de un contador y lo muestre en el monitor serial, el contador iniciara en 0 y se incrementara hasta 255, cambiando su valor cada 0,5 segundos. Adicionalmente configurar una interrupción en el pin 2, para que cuando se presione el botón conectado al pin2 el contador se reinicie (vuelva a cero).
Ejercicio 2: Contador
Crear un programa que muestre el valor de una variable en el monitor serial cada 1 segundo, y configurar una interrupción en el pin 2, que incremente el valor de la variable en dos unidades, cada vez que se presione el pulsador.
Probablemente te hayas dado cuenta que algunas veces que presionas el pulsador el valor se incrementa correctamente y otras veces cuenta mas de lo debido. Esto se debe a un comportamiento particular de los interruptores mecánicos que tiene como consecuencia un «rebote» de la señal al momento de la transición. Nada mejor que una imagen para ilustrarlo.
Como podemos apreciar la transición entre el nivel bajo y nivel alto no esta perfectamente definida por un tiempo (este tiempo puede oscilar entre unos 50 microsegundos hasta unos 10 milisegundos dependiendo del tipo de pulsador), y puede provocarnos errores cuando usamos una entrada digital para realizar operaciones de conteo, o cuando llamamos a una interrupción como en nuestro caso.
Hay dos formas de lidiar con el rebote de los contactos mecánicos, una es por hardware y la otra es por software. A continuación se presenta la solución por hardware que consiste en agregar un par de componentes, un capacitor y una resistencia, esta configuración filtra la señal del pulsador mejorando la transición entre los niveles altos y bajos. La solución por software cuando se lee el pulsador desde una ISR, no es la mejor opción pues involucra tiempos de espera, lo cual es contradictorio con lo que hablamos que debe ser una ISR.
Modificar el circuito del pulsador y verificar que funciona el circuito anti-rebote.
Ejercicio 3 – Configurar dos interrupciones
Usaremos dos pulsadores para modificar el valor de una variable, El pulsador 1 incrementa la variable en uno, y el pulsador 2 la decrementa en 1 también, después que se ha presionado alguno de los dos pulsadores se imprime el valor de la variable en el monitor serie. La variable se inicializa en 1.
En próximas entregas hablaremos de las soluciones por software para eliminar el rebote.