Código, Tips y Programas Varios - Manejo de Interrupciones Con Arduino
Código, Tips y Programas Varios - Manejo de Interrupciones Con Arduino
Tips y Programas Varios: Manejo de Interrupciones con Arduino
Código, Tips y Programas Varios
Pequeño blog aderezado con trocitos de código.
19 DICIEMBRE 2012
ENLACES
Manejo de Interrupciones con Arduino La Tienda de TeUbi.co
El artículo anterior conectamos un simple display de 7 segmentos a nuestro Arduino y programamos
un sencillo contador. Pero... ¿Qué tal si queremos interactuar con eventos externos? PÁGINAS DATOS
PERSONALE
Fuente S
Una de las formas comunes de interactuar con cosas que suceden "fuera" de nuestro control es por Abierta
ejemplo monitorear el estado de las líneas. Dentro de nuestro "loop" principal podemos leer Directorio de
continuamente el valor de una línea de entrada y ejecutar por medio de una condicional un código Software
especifico en caso de que detectemos un cambio. MiniSXF
DRGPIO
El problema de este método es que el código se vuelve sumamente complejo cuando tenemos que Mario
In English
Gómez
monitorear muchas cosas o cuando nuestro loop principal simplemente tiene que hacer cosas que
Seguir 376
toman demasiado tiempo.
Ver todo mi
En esta entrada vamos a comprender como utilizar las interrupciones externas del Arduino. Esto nos perfil
será de mucha utilidad para interactuar ya sea con el usuario o con otros dispositivos de hardware
que emitan "señales" que nos permitan saber que un evento ha sucedido. ARCHIVO DEL BLOG
► 2015 (5)
Antes de continuar preparen su display de 7 segmentos porque en esta entrada lo vamos a reutilizar.
► 2014 (9)
► 2013 (39)
Una cosa a la vez... ▼ 2012 (17)
▼ diciembre (11)
Los Arduino basados en los microcontroladores Amtel AVR pueden ejecutar una sola secuencia de El Muro de la Vergüenza
instrucciones a la vez. El problema de esto es por ejemplo, si ustedes están enviando números a su
DNS Dinámico en la
display y luego el usuario presiona un botón. Si nuestro ciclo toma mucho tiempo en ejecutarse puede Raspberry PI
que pase mucho tiempo antes de que podamos leer la línea de nuestro usuario y perdamos el
Haciendo un "puente
evento, o tal vez simplemente estamos ocupando un ciclo en hacer algo interesante. Esto es una wireless" para la
"desventaja" de microcontroladores sencillos y es algo con lo que las computadoras han tenido que Raspberry PI...
convivir prácticamente desde que fueron inventadas. Instalando Lighttpd+PHP
5+Sqlite en la RasPI
¿Como manejar eventos externos? Manejo de Interrupciones
con Arduino
Afortunadamente los diseñadores de microcontroladores pensaron en este problema hace bastante Fabricando un contador
tiempo y de ahí surgieron las famosas "interrupciones". Las interrupciones (omitiendo muchos simple con Arduino y
un dis...
detalles) funcionan de manera muy sencilla: Existe una lista de eventos internos o externos que
genera ya sea el microcontrolador o una línea externa y al detectarse estos eventos el Conectando la Raspberry
PI al Arduino vía Serial
microcontrolador detiene la secuencia de comandos que está ejecutando. Luego de esto, llama al
código definido previamente que se encarga de "manejar" esa interrupción en específico y luego, Redimensionando
Imágenes con PHP y
cuando ha terminado de "atender la interrupción" regresa a ejecutar la secuencia principal que había
GD
dejado pausada.
Convirtiendo Gráficos
Vectoriales (SVG) en
Tal vez no es una solución perfecta pero nos permite "atender" cosas que consideramos importantes PNG pro...
por ejemplo cuando un dispositivo externo envía datos vía serial o cuando una línea de interrupción
¡Hello World! en Java
cambia su valor de 0 a 1 o viceversa. (Estricto al paradigma
OO)
Hablando un poco más sobre las interrupciones Variando la intensidad de
un LED en Arduino con
PW...
No hay un "estándar" de como se manejan o cuales on las interrupciones disponibles. Cada
fabricante implementa diferentes tipos de interrupciones dependiendo del microcontrolador que han ► noviembre (2)
diseñado y el uso que se le piensa dar. La ventaja con los Arduinos es que como comparten un
► octubre (4)
microprocesador común los desarrolladores han creado unas bibliotecas que permiten manejar
interrupciones de una manera muy sencilla.
http://fuenteabierta.teubi.co/2012/12/manejodeinterrupcionesconarduino.html 1/8
18/11/2015 Código, Tips y Programas Varios: Manejo de Interrupciones con Arduino
Nota aclaratoria: Las funciones de manejo de interrupciones de la referencia del Arduino son muy,
pero muy básicas. Los microprocesadores Amtel AVR tienen una larga lista de diferentes tipos
interrupciones disponibles. Sin embargo el manejar estas interrupciones no resulta "tan trivial" y
depende mucho de las características de cada microprocesador en particular. Para aplicaciones más
avanzadas, si desean más información de como programar estas interrupciones, pueden revisar la
documentación de AVR Libc en la sección dedicada al manejo de interrupciones.
En Arduino podemos asociar el código de las interrupciones a través de la función attachInterrupt().
Las interrupciones o eventos que podemos asociar a las líneas mediante esta función en el Arduino
Mega son las siguientes:
1. Cuando el nivel lógico de la línea es 0 (LOW)
2. Cuando el nivel lógico de la línea cambia independientemente de su estado lógico (CHANGE)
3. Cuando el nivel lógico cambia de 0 a 1 (RISING)
4. Cuando el nivel lógico cambia de 1 a 0 (FALLING)
Antes de continuar, vamos a hacer un pequeño circuito que nos permita cambiar los valores lógicos
de las líneas de interrupción:
Al circuito anterior se le conoce como "resistencia pullup", su función es mantener un nivel lógico en
la compuerta aunque el circuito se encuentre abierto. Para nuestro caso mantendrá un 1 lógico
permanente con el pulsador abierto y un 0 lógico cuando esté cerrado.
Luego, para capturar las interrupciones la tenemos un poquito más fácil, simplemente replicamos este
circuito para cuantos interruptores querramos capturar y lo conectamos a las líneas correspondientes.
Para este ejemplo utilizaremos las líneas 18, 19 y 20 que corresponden a las interrupciones 3, 4 y 5
del Arduino.
Esta es una pequeña foto de como se ven los pulsadores armados en la breadboard:
Notarán que uso resistencias de 10K. Usualmente se recomienda el uso de resistencias de 4.7K,
lástimosamente no tenía a la mano a la hora de armar el circuito, sin embargo, en la práctica no hay
demasiada diferencia ya que su único trabajo es evitar que cuando presionemos el pulsador se
produsca un corto circuito.
Programando las Interrupciones
Lo primero que tenemos que hacer es definir algunas constantes que nos indiquen las lineas que
utilizaremos como interrupciones, además de algunas variables "volatiles". En general, utilizaremos el
modificador "volatile" para indicarle al compilador que nuestra variable podría ser accedida desde
http://fuenteabierta.teubi.co/2012/12/manejodeinterrupcionesconarduino.html 2/8
18/11/2015 Código, Tips y Programas Varios: Manejo de Interrupciones con Arduino
distintas ubicaciones en el código de nuestro programa. Esto obliga a asignar espacio de RAM a
nuestra variable en vez de guardarla en la memoria de datos.
// Botones
const int BTN1 = 18;
const int BTN2 = 19;
const int BTN3 = 20;
// Display 7‐Segmentos
const int D0 = 4;
const int D1 = 5;
const int D2 = 6;
const int D3 = 7;
// Contador
volatile int inc;
// Encendido y Apagado del display
volatile int onOff;
// Tiempo en que se realizo la última interrupción.
volatile long lastInt;
Las constantes de nombre "BTN" representan mis botones y las constantes "D" las líneas a donde
tengo conectado el decodificador a 7 segmentos.
Definiendo las funciones que modificarán los valores
Vamos a definir un par de funciones "ayudantes" extra que se encargarán de incrementar y
decrementar el contador.
// Incrementa y escribe el número al display
void increment() {
inc++;
inc = (inc < 10) ? inc : 0;
writeTo7Seg(inc);
}
// Decrementa y escribe el número al display
void decrement() {
inc‐‐;
inc = (inc >= 0) ? inc : 9;
writeTo7Seg(inc);
}
El siguiente paso es definir el código que se encargará de manejar las interrupciones, vamos a
realizar funciones muy simples, un botón incrementara el contador, el otro lo disminuira y el último
encenderá y apagará el display de 7 segmentos. Con las funciones anteriores el código se vuelve
muy fácil de entender:
// Codigo para la interrupcion 5
void int5() {
increment();
}
// Codigo para la interrupcion 4
Este sitio emplea cookies de Google para prestar sus servicios,
void int4() {
para personalizar anuncios y para analizar el tráfico. Google recibe
decrement();
información sobre tu uso de este sitio web. Si utilizas este sitio web,
}
se sobreentiende que aceptas el uso de cookies.
MÁS INFORMACIÓN ENTENDIDO
// Codigo para la interrupcion 3
void int3() {
onOff = onOff^1;
if(onOff) {
writeTo7Seg(15);
http://fuenteabierta.teubi.co/2012/12/manejodeinterrupcionesconarduino.html 3/8
18/11/2015 Código, Tips y Programas Varios: Manejo de Interrupciones con Arduino
} else {
writeTo7Seg(inc);
}
}
Por defecto no hay ningún código asignado para manejar las interrupciones, por ello es necesario
que definamos la asociación de las interrupciones en el "setup".
void setup() {
// Establecemos las líneas del display como salida
pinMode(D0,OUTPUT);
pinMode(D1,OUTPUT);
pinMode(D2,OUTPUT);
pinMode(D3,OUTPUT);
// Inicializamos nuestras variables
inc = 0;
lastInt = 0;
onOff = 1; //Displa encendido al encender
// Asociamos la interrupción a nuestro código
attachInterrupt(5,int5,RISING); // línea 18
attachInterrupt(4,int4,RISING); // línea 19
attachInterrupt(3,int3,RISING); // línea 20
//Colocamos un 0 en nuestro display de 7 segmentos
writeTo7Seg(inc);
}
Tal vez en este momento se estén haciendo la siguiente pregunta: ¿Por qué uso RISING y no otro
tipo de interrupción? La respuesta a esta pregunta realmente tiene mucho que ver con lo que
estemos programando y como querramos que funcione. En este caso, quería que el contador se
incrementara cuando el usuario dejara de presionar el botón, como nuestro botón genera un 0 lógico
cuando esta presionado y un 1 lógico cuando esta sin presionar si queremos incrementar nuestro
contador justo despues de que se ha presionado el botón el evento RISING es el más adecuado. Si
en cambio estuvieramos programando algo como un control de arcade posiblemente deberíamos
usar la interrupción FALLING para que el usuario no perciba un "retardo" a la hora de presionar el
botón y ver una respuesta en el juego.
¿Qué hacemos luego con el loop principal?
Pues nada, lo dejamos vacío ya que no lo utilizaremos en este ejemplo:
// Loop vacío
void loop() {
}
Bonus: Eliminando el Rebote vía software.
Si toman el código que puse anteriormente junto con el circuito que coloqué, se van a dar cuenta que
el contador funciona de manera errática como se muestra en el siguiente video:
Este no es un problema de software sino más bien de hardware. Es el famoso "rebote" de
http://fuenteabierta.teubi.co/2012/12/manejodeinterrupcionesconarduino.html 4/8
18/11/2015 Código, Tips y Programas Varios: Manejo de Interrupciones con Arduino
interruptores.
Uno de los grandes problemas al trabajar con circuitos digitales e interruptores, es que los
interruptores simplemente no son perfectos. Dependiendo del diseño de los mismos, estos pueden
dar lugar a este molésto fenómeno.
El rebote literamente significa que los contactos dentro del interruptor rebotan brevemente, esto
sucede tan rápido que simplemente no nos damos cuenta, pero el circuito digital en vez de recibir un
solo pulso recibe varios.
¿Cómo corregir el rebote?
Existen diferentes métodos vía hardware para eliminación de rebote de interruptores, sin embargo si
su aplicación no necesita tiempos de respuesta "críticos". Pueden utilizar esta sencilla solución que
les propongo a continuación.
Primero, ¿Recuerdan la variable "lastInt" que definimos al principio? Lo que vamos a hacer para
eliminar el rebote es "filtrar" nuestras interrupciones. La lógica que utilizaremos es muy simple, si
nuestra interrupción ocurre en menos del tiempo umbral especificado entonces simplemente la
ignoraremos.
¿Cuanto es el umbral que deseamos definir? La respuesta no es sencilla ya que depende de las
caraterísticas físicas interruptor que estemos usando. En mi circuito, luego de experimentar con un
poco de prueba y error, un umbral de 200 milisegundos parece funcionar a la perfección.
Para hacer funcionar este eliminador de rebote por software simplemente vamos a usar una
condicional y antes de salir de la función guardaremos el valor de millis() y vamos a comparar para
ver si ha pasado tiempo suficiente.
¡¡¡Mucho Cuidado!!! el valor de millis() se reinicia automáticamente aproximadamente cada 70 días.
Esto significa que si deciden usar este método y piensan tener funcionando su arduino sin parar
durante más de 70 días, tienen que capturar la excepción de reinicio del contador de milisegundos
porque si no, llegara un momento que nunca se cumplirá la condición y se ignorarán todas las
interrupciones que usen este método. Así que: "advertidos están".
Una vez dada esta advertencia modifiquemos un poco nuestro código de manejo de interrupciones:
void int5() {
if((millis()‐lastInt)>200) {
increment();
lastInt = millis();
}
}
void int4() {
if((millis()‐lastInt)>200) {
decrement();
lastInt = millis();
}
}
void int3() {
if((millis()‐lastInt)>200) {
onOff = onOff^1;
if(onOff) {
writeTo7Seg(15);
} else {
writeTo7Seg(inc);
}
lastInt = millis();
}
}
Nota: Si notan, comparto "lastInt" con todo el código de interrupciones, esto en la práctica significa
que no solo estoy eliminando el rebote sino también la posibilidad de presionar dos botones
diferentes en menos de 200ms. Una posible solución a este bug es utilizar un marcador de tiempo
diferente para cada interrupción (latInt1, lastInt2, lastInt3), de esta manera podríamos eliminar
http://fuenteabierta.teubi.co/2012/12/manejodeinterrupcionesconarduino.html 5/8
18/11/2015 Código, Tips y Programas Varios: Manejo de Interrupciones con Arduino
efectivamente el rebote para cada interruptor de manera independiente, sin bloquear la ejecución del
código en caso de recibir una interrupción de una línea diferente.
Probando Todo
Pueden descargar el código y cargarlo a su arduino. Si utilizan otra tableta diferente a la Mega solo
revisen la lista de interrupciones y ajusten los pines de salida a su conveniencia en el código.
Si todo funciona bien deberá de funcionar de manera similar a como se muestra en el siguiente video:
Conclusiones
Las interrupciones resultan sumamente útiles para ejecutar código de manera asíncrona, es decir que
nuestro código reaccione a eventos externos sin necesidad de estarlos monitoreando continuamente.
En esta entrada he "abusado" un poco de las interrupciones externas. Jugando un poco con circuitos
digitales podemos crear una especie de controlador de interrupciones que nos permita monitorear
varios eventos utilizando una sola línea de interrupción, sin embargo esto lo podemos dejar para una
entrada posterior.
Ya solo nos queda un artículo más para esta serie de tres entradas, en el último artículo leeremos
datos de la Raspberry e intentaremos crear una aplicación interesante con nuestro módulo de
pruebas.
No habiendo nada más que decir por ahora... ¡Hasta la próxima!
Publicado por Mario Gómez en 11:13 p. m.
Etiquetas: Arduino, ArduinoBoardMega, Interrupciones
9 comentarios:
Moises Alm dijo...
Saludos muy bueno el articulo, soy nuevo en el mundo de los micotroladores. Me gustaria
saber si las interrupciones se limitan solo a las entredas digitales I/O, por que estoy
necesitando una interrrupcion para un ADC. Estoy utilizando un ARDUINO NANO Ver.3.
Gracias.
20 de agosto de 2013, 21:13
Mario Gómez dijo...
¡Hola Moises!
Según leo en la hoja técnica del ATMega328 (Pag #65), si es posible agregar código de
interrupción para detectar el "final de conversión" (22 0x002A ADC ADC Conversion
Complete).
Ahora, si lo que quieres es lanzar una interrupción cuando exista un cambio en el voltaje,
existe otra interrupción (24 0x002E ANALOG COMP Analog Comparator).
Estas interrupciones no se pueden configurar con la instrucción "attach" de Arduino,
tendrías que hacerlo a mano utilizando un código similar al que se muestra en este link:
http://www.uchobby.com/index.php/2007/11/24/arduinointerrupts/
Voy a tratar de hacer de hacer tiempo para escribir un tutorial sobre usos avanzados de
http://fuenteabierta.teubi.co/2012/12/manejodeinterrupcionesconarduino.html 6/8
18/11/2015 Código, Tips y Programas Varios: Manejo de Interrupciones con Arduino
interrupciones.
¡Saludos!
23 de agosto de 2013, 9:38
miguel aguilar dijo...
Buen dia buen articulo pero falta la escritura en 7 segmentos podrias pasarmela para revisar
9 de octubre de 2013, 4:26
Homar Lopez dijo...
Hola, gracias por tu aporte, pero me podrias expliccar donde ubico los difrentes codigos? ya
esta claro que el loop queda vacio, pero el resto donde va? al inicio? en el setup??
gracias..
23 de enero de 2014, 6:31
Mario Gómez dijo...
¡Hola Homar!
Las interrupciones se ejecutan bajo "demanda" es decir, cuando se recibe un cambio en la
línea de la interrupción. Por ello no se colocan en el loop principal.
La forma que Arduino te permite asociar un código específico a una interrupt es la función
attachInterrupt donde especificas:
attachInterrupt(,,);
Por ejemplo, digamos que tienes el código siguiente que quieres que se ejecute con el
cambio en la interrupción 3:
void cambioInt3() {
Serial.println("Cambio detectado");
}
Para asociarlo al cambio de línea, en el setup tendrías que colocar:
attachInterrupt(3, cambioInt3, FALLING);
FALLING indica que la interrupción se ejecutará cuando el nivel lógico de la interrupción 3
cambie de 1 a 0.
Espero haberte ayudado. ¡Saludos!
23 de enero de 2014, 7:42
Javi dijo...
Hola Moises.
Antes de nada agradecerte esta publicación. A despejado muchas dudas, aunque como
pasa siempre, abierto otras.
Te cuento, estoy intentando controlar mediante RC (emisorareceptor) un coche, robot o
quad (aun no lo he hecho, estoy en fase aprendizaje), pero he visto que por problemas de
tiempo de eventos (lo que tu bien explicas) no puedo utilizar PulseIn(), sino que tengo que
utilizar las interrupciones.
Mi preguntas son:
a)Cómo leo 6 canales o entradas con un Arduino Uno que tiene disponibles dos
interrupciones
b) No entiendo bien lo de las interrupciones asociadas a un pin. Significa eso que solo
puedo hacer la interrupción de ese pin en concreto.
Muchas gracias de nuevo.
Un saludo,
Javi
6 de mayo de 2014, 3:49
http://fuenteabierta.teubi.co/2012/12/manejodeinterrupcionesconarduino.html 7/8
18/11/2015 Código, Tips y Programas Varios: Manejo de Interrupciones con Arduino
eduardoz barea escobar dijo...
buenas, tengo una mini pro compatible jymcu y supuestamente deberia tener el
attachinterrupt0 en el pin 2 pero no me funciona.
he estado buscando informacion pero no encuentro nada.
Se puede asignar el attachinterrupt el pin que yo defina??
muchas gracias
20 de mayo de 2014, 8:00
Juan Carlos Durán dijo...
Hola disculpa ¿no sabrás como realizar las interrupciones en un periodo de tiempo?
Por ejemplo, necesito contar cuantas interrupciones se dan en 10 segundos... ¿Tendrás
alguna idea o referencia de cómo hacerlo?
Sería de gran ayuda, agradezco de antemano.
1 de diciembre de 2014, 18:04
Cristian Veloso dijo...
Excelente articulo, justo estaba pensando en redactar un articulo referido a lo mismo.
se les interesa les comparto mis notas
http://www.electrontools.com/2014/08/registrodedesplazamiento74hc595.html
espero que les agrade.
saludos!
14 de julio de 2015, 12:25
Publicar un comentario en la entrada
Suscribirse a: Enviar comentarios (Atom)
Plantilla Picture Window. Con la tecnología de Blogger.
http://fuenteabierta.teubi.co/2012/12/manejodeinterrupcionesconarduino.html 8/8