Tabla de contenidos
¿Qué son las interrupciones?
Son personas que de forma intermitente te impiden hacer tu trabajo actual. ¡Ja! Bueno, tal vez… pero lo que realmente queremos saber es qué son en el contexto de la electrónica embebida y los microprocesadores.
Así que preguntémoslo de nuevo: ¿qué es una interrupción? En pocas palabras, es un método por el cual un procesador puede ejecutar su programa normal mientras vigila continuamente algún tipo de evento, o interrupción. Hay dos tipos de interrupciones:
● Interrupciones de hardware – Se producen en respuesta a un evento externo, como la activación o desactivación de un pin.
● Interrupciones de Software – Estas ocurren en respuesta a una instrucción de software.
En general, la mayoría de los microcontroladores AVR de 8 bits (es decir, los Arduinos) no son capaces de realizar interrupciones por software, por lo que para los fines de este tutorial, nos centraremos en las interrupciones por hardware.

¿Cómo funciona una interrupción?
Cuando se produce el evento o la interrupción, el procesador toma nota inmediatamente, guarda su estado de ejecución, ejecuta un pequeño trozo de código (a menudo llamado manejador de interrupciones o rutina de servicio de interrupción) y luego vuelve a lo que estaba haciendo antes.
El programador define el código que debe ejecutarse cuando se produce una interrupción concreta dentro del propio programa. En Arduino, utilizamos una función llamada attachInterrupt () para hacer esto y la sintaxis recomendada es similar a la salida que se muestra a continuación.
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)
Esta función toma tres parámetros:
●Primer parámetro (es decir, digitalPinToInterrupt(pin)) – Número de pin de la interrupción, que indica al microprocesador qué pin debe supervisar. El pin depende del microcontrolador que se utilice.
●Segundo parámetro (i.e. ISR) – La ubicación del código que queremos ejecutar si esta interrupción se dispara.
●Tercer parámetro (i.e.mode) – Le dice qué tipo de disparo (trigger) debe buscar: un alto lógico, un bajo lógico o una transición entre los dos.
Para más información sobre qué pines están reservados para las interrupciones y algún código de ejemplo, consulta la página de attachInterrupt () de Arduino.
Vamos a ver un ejemplo sencillo para entender mejor las interrupciones y su funcionamiento
Conecta el LED al pin 13 y el botón al pin 2, como se ve en el diagrama de Fritzing de abajo:

Si te fijas bien en lo que acabas de conectar, te darás cuenta de que el LED es en realidad
redundante. Podríamos utilizar simplemente el LED incorporado en el pin 13, pero para fines visuales, hemos añadido el LED externo.
Ejemplo: Interrupción simple
Ahora que tenemos nuestro hardware conectado, veamos un ejemplo sencillo que envía continuamente una señal de “Off” a un LED. Conectaremos una interrupción al pin 2; este pin monitorizará un botón que enviará una señal de “Encendido” al LED cuando sea pulsado e incrementará un contador.
La mayoría de los Arduinos tienen 2 interrupciones externas incorporadas: interrupt0 (en el pin digital 2) e interrupt1 (en el pin digital 3). Algunas placas tienen más (como la Arduino Mega 2560) – consulta el manual de usuario o la hoja de datos para obtener más información sobre lo que soporta tu placa específica. Arduino también tiene más detalles sobre un puñado de placas en su página attachInterrupt (). Como estamos usando una RedBoard aquí, este ejemplo usa el pin 2 para monitorear las interrupciones.
Ejemplo simple de interrupción 1
Selecciona la placa y el puerto COM para la RedBoard. Luego cargue lo siguiente.
/* Simple Interrupt Example 1
by: Jordan McConnell
SparkFun Electronics
created on 10/29/11 */
int ledPin = 13; // LED en pin 13
int x = 0; // variable actualizada por interrupción
void setup() {
//habilita interrupción 0 (pin 2)
pinMode(ledPin, OUTPUT);
//salta a la función increment cuando el botón es presionado
attachInterrupt(0, increment, RISING);
Serial.begin(9600); //inicia comunicación serial
}
void loop() {
digitalWrite(ledPin, LOW);
delay(3000); //fingir que se hace algo útil
Serial.println(x, DEC); //imprimir x en el monitor de serie
}
// Rutina de servicio de interrupción para la interrupción 0
void increment() {
x++;
digitalWrite(ledPin, HIGH);
}
El bucle principal de este programa envía una señal “OFF” el LED cada 3 segundos. Mientras tanto, este programa vigila el pin digital 2 (que corresponde a la interrupción 0) en busca de un flanco ascendente. En otras palabras, busca un cambio de voltaje que vaya de un nivel lógico bajo (0V) a un nivel lógico alto (5V), lo que ocurre cuando se pulsa el botón. Cuando esto ocurre se llama a la función incremento. El código dentro de esta función se ejecuta, la variable x se incrementa, y el LED se enciende. Entonces el programa vuelve a donde estaba en el bucle principal.
Si juegas con él, te darás cuenta de que el LED se mantiene encendido durante cantidades de tiempo aparentemente aleatorias, pero nunca más de 3 segundos. El tiempo que el LED permanece encendido depende del punto en el que se interrumpió el código en el bucle principal. Por ejemplo, si la interrupción se dispara justo en la mitad de la función de retardo, el LED permanecerá encendido durante aproximadamente 1,5 segundos después de pulsar el botón.

Manejo del rebote
Un problema común con las interrupciones es que a menudo pueden dispararse múltiples veces para un solo evento. Si miras la salida en serie del código del ejemplo 1, te darás cuenta de que aunque pulses el botón sólo una vez, x se incrementará muchas veces. Para explorar por qué sucede esto, tenemos que echar un vistazo a la propia señal. Si tomamos un osciloscopio para monitorear el voltaje del pin en el momento en que presionamos el botón, se vería algo así:

Aunque la transición principal del pin es de bajo a alto, durante el proceso se producen varios picos que pueden provocar múltiples interrupciones. Esto se conoce como ruido o rebote. La pulsación de un botón puede parecer un solo paso, pero en realidad las piezas mecánicas de ese botón entran en contacto varias veces antes de establecerse en un estado determinado. Hay varias maneras de remediar esto. A menudo se pueden solucionar los problemas de rebote con el hardware añadiendo un filtro RC adecuado para suavizar la transición. Otra opción es abordarlo en el software ignorando temporalmente otras interrupciones durante un pequeño lapso de tiempo después de que se dispare la primera interrupción. Volviendo a nuestro antiguo ejemplo, vamos a añadir una corrección que permita que la variable x sólo se incremente una vez cada vez que se pulse un botón en lugar de varias veces.
Ejemplo de Interrupción Simple 2
Seleccione la placa y el puerto COM para la RedBoard si aún no lo ha hecho. Luego cargue lo siguiente.
/* Simple Interrupt example 2
by: Jordan McConnell
SparkFun Electronics
created on 10/29/11 */
int ledPin = 13; // El LED está conectado al pin digital 13
int x = 0; // variable a actualizar por la interrupción
//variables para mantener un registro de los tiempos de las interrupciones recientes
unsigned long button_time = 0;
unsigned long last_button_time = 0;
void setup() {
//activar la interrupción 0 que utiliza el pin 2
//saltar a la función de incremento en el flanco descendente
pinMode(ledPin, OUTPUT);
attachInterrupt(0, increment, RISING);
Serial.begin(9600); //activar la comunicación en serie
}
void loop() {
digitalWrite(ledPin, LOW);
delay(3000); // pretender hacer algo útil
Serial.println(x, DEC); //imprimir x en el monitor de serie
}
// Rutina de servicio de interrupción para la interrupción 0
void increment() {
button_time = millis();
//comprobar si increment() fue llamado en los últimos 250 milisegundos
if (button_time - last_button_time > 250)
{
x++;
digitalWrite(ledPin, HIGH);
last_button_time = button_time;
}
}
Veamos de nuevo la salida en serie al pulsar el botón. Abra el monitor serial configurado a 9600 baudios. Observe que el incremento sólo es llamado una vez por cada pulsación del botón. Este arreglo funciona porque cada vez que el manejador de interrupción es ejecutado, compara el tiempo actual recuperado por la función millis() con el tiempo en que el manejador fue llamado por última vez. Si está dentro de una ventana de tiempo definida, en este caso un cuarto de segundo, el procesador vuelve inmediatamente a lo que estaba haciendo. Si no, ejecuta el código dentro de la sentencia if actualizando la variable x, encendiendo el LED y actualizando la variable last_button_time para que la función tenga un nuevo valor con el que comparar cuando se active en el futuro.
Niveles de prioridad de las interrupciones
¿Qué sucede cuando dos interrupciones ocurren al mismo tiempo? La mayoría de los AVRs no soportan lo que llamamos niveles de prioridad de interrupción. Si dos interrupciones ocurren simultáneamente o hay dos o más interrupciones esperando en una cola, la prioridad se determina por el orden de sus direcciones de vector. Las direcciones de vector más bajas se atienden primero, el Reset tendrá prioridad sobre todas las peticiones de interrupción. De nuevo, su hoja de datos tendrá más información sobre su placa específica.
Ejemplo: Interrupción de una secuencia de LEDs
Las interrupciones también pueden ser útiles cuando se trata de secuencias largas de cosas. Veamos otro ejemplo sencillo con LEDs: digamos que vamos a utilizar el LED RGB incorporado en un LilyPad USB Plus para recorrer una secuencia de colores, encendiendo y apagando cada color. El tiempo del ciclo de desvanecimiento para cada color es de 10 segundos y tenemos varias de estas placas cosidas en trajes en el escenario. ¿Qué ocurre si uno de estos trajes se desincroniza?
En lugar de tener que esperar a que el ciclo termine y tratar de reiniciar la placa en sincronía con las otras placas, podemos añadir una interrupción al pin 10 (es la interrupción 0 en la placa LilyPad USB Plus). Cuando se pulsa el botón, la interrupción se dispara y pasamos al siguiente color. Esto nos permite sincronizar más rápidamente el traje ofensivo y el espectáculo puede continuar.
Hagamos esto por nosotros mismos. Si quieres seguirnos, coge un LilyPad USB Plus. Necesitará los botones, los puentes y la fuente de alimentación del experimento anterior. También necesitará unos cuantos cables de pinza de banana a caimán para conectarlos a las lengüetas de costura de LilyPad.
Conéctalo todo como ves aquí:

Seleccione la placa y el puerto COM para el LilyPad USB Plus. A continuación, cargue el código siguiente.
/*
Example: Interrupting an LED sequence
SparkFun Electronics
Follow the tutorial at:
https://learn.sparkfun.com/tutorials/processor-interrupts-with-arduino#example-interrupting-an-led-sequence
This code is released under the MIT License (http://opensource.org/licenses/MIT)
******************************************************************************/
// Cycling through a series of colors using the built-in LED on the LilyPad USBPlus. Using an interrupt to switch quickly between colors
// The built-in LED:
int RGB_red = 12;
int RGB_green = 13;
int RGB_blue = 14;
int x = 0; // variable to be updated by the interrupt
//Fade variables
int ledMode = 0; //color mode to control LEDs
int colorSwitch = 0; //compare to current_FadeVal to know whether or not to switch colors yet
int prev_FadeVal = 0;
int current_FadeVal = 0;
boolean increasing = true;
//variables to keep track of the timing of recent interrupts
unsigned long button_time = 0;
unsigned long last_button_time = 0;
void setup() {
// Make all of our LED pins outputs:
pinMode(RGB_red, OUTPUT);
pinMode(RGB_green, OUTPUT);
pinMode(RGB_blue, OUTPUT);
attachInterrupt(0, increment, CHANGE);
Serial.begin(9600); //turn on serial communication
}
void loop()
{
// In this code we'll step through seven rainbow colors (primary, secondary, tertiary).
// Unlike digitalWrite, which can be only HIGH (on) or LOW (off),
// analogWrite lets you smoothly change the brightness from 0 (off) to 255 (fully on).
// When analogWrite is used with the RGB LED, you can create millions of colors!
FadeColor();
}
// Interrupt service routine for interrupt 0
void increment() {
button_time = millis();
//check to see if increment() was called in the last 250 milliseconds
if (button_time - last_button_time > 250)
{
//increment counter
x++;
//turn led off
analogWrite(RGB_red,0);
analogWrite(RGB_green,0);
analogWrite(RGB_blue,0);
Serial.println(x, DEC); //print x to serial monitor
delay(10000);
//set the last button time to the current button time
last_button_time = button_time;
//Switch colors
if (ledMode < 7){
ledMode++;
}
else {
//start over with Red
ledMode = 1;
}
//reset fade values
prev_FadeVal = 0;
current_FadeVal = 0;
}
}
void FadeColor() {
switch (ledMode) {
case 1://FADE RED
analogWrite(RGB_green, 0);
analogWrite(RGB_blue, 0);
analogWrite(RGB_red, prev_FadeVal);
break;
case 2://FADE YELLOW
analogWrite(RGB_red, prev_FadeVal);
analogWrite(RGB_green, prev_FadeVal);
analogWrite(RGB_blue, 0);
break;
case 3://FADE GREEN
analogWrite(RGB_red, 0);
analogWrite(RGB_green, prev_FadeVal);
analogWrite(RGB_blue, 0);
break;
case 4://FADE CLEAR BLUE
analogWrite(RGB_red, 0);
analogWrite(RGB_green, prev_FadeVal);
analogWrite(RGB_blue, prev_FadeVal);
break;
case 5://FADE BLUE
analogWrite(RGB_red, 0);
analogWrite(RGB_green, 0);
analogWrite(RGB_blue, prev_FadeVal);
break;
case 6://FADE MAGENTA
analogWrite(RGB_red, prev_FadeVal);
analogWrite(RGB_green, 0);
analogWrite(RGB_blue, prev_FadeVal);
break;
default:
analogWrite(RGB_red, prev_FadeVal);
analogWrite(RGB_green, prev_FadeVal);
analogWrite(RGB_blue, prev_FadeVal);
break;
}
delay(100);
if (increasing == true) {
current_FadeVal += 5;
}
else { //decreasing
current_FadeVal -= 5;
}
if (current_FadeVal > 255) {
increasing = false;
prev_FadeVal -= 5;//undo addition
current_FadeVal = prev_FadeVal;
}
else if (current_FadeVal < 0) {
increasing = true;
prev_FadeVal += 5;//unto subtraction
current_FadeVal = prev_FadeVal;
}
prev_FadeVal = current_FadeVal;
if(current_FadeVal == colorSwitch)
{
if (ledMode < 7){
ledMode++;
}
else {
//start over with Red
ledMode = 1;
}
}
}
Obsérvese que cada vez que se pulsa el botón, se pasa al siguiente color. Tal vez no sea el caso de uso más común, pero es visualmente más obvio cómo las interrupciones abordan las necesidades inmediatas.

¿Ventajas de usar interrupciones?
Llegados a este punto te preguntarás: “¿Por qué utilizar una interrupción? ¿Por qué no usar ocasionalmente un digitalRead() en el pin 2 para comprobar su estado? ¿No hará eso lo mismo?”
La respuesta depende de la situación. Si sólo te interesa saber cuál es el estado del pin en un momento determinado de tu código o del tiempo, entonces un digitalRead() probablemente será suficiente. Si quisieras monitorizar continuamente el pin, podrías sondear el pin frecuentemente con digitalRead()’s. Sin embargo, podrías perder fácilmente datos entre las lecturas. Esta información perdida podría ser vital en muchos sistemas de tiempo real. Por no mencionar que cuanto más a menudo estés sondeando los datos, más tiempo del procesador se desperdicia haciendo eso en lugar de ejecutar código útil.
Veamos el sistema que monitoriza y controla el antibloqueo de frenos de un coche como ejemplo de tiempo crítico. Si un sensor detecta que el coche empieza a perder tracción, realmente no te importa qué parte del programa se está ejecutando en ese momento, porque hay que hacer algo sobre esta situación inmediatamente para asegurar que el coche conserva la tracción y, con suerte, evita un accidente o algo peor. Si sólo estuvieras sondeando el sensor en esta situación, el sensor podría ser sondeado demasiado tarde y el evento podría perderse por completo. La belleza de las interrupciones es que pueden impulsar la ejecución inmediatamente, cuando es necesario.
Jordandee.Ell C. Processor interrupts with Arduino. Sparkfun. https://learn.sparkfun.com/tutorials/processor-interrupts-with-arduino