Como usar las interrupciones en arduino
Una interrupción es básicamente la alteración del flujo natural de un programa, si pensamos en este como secuencial. Existen varios tipos de interrupciones posibles pero vamos a enumerar las dos principales, las restantes son solo derivaciones de estas.
Antes de entrar en detalle quiero comentar que este es un tema amplio de estudio, es imposible detallar todos los conceptos que el tema implica, con lo cual solo nos vamos a detener en contar de que se trata y para que se utiliza, dando algunos ejemplos se entiende mejor.
También comentar que las interrupciones no son solo propias de arduino, estas se encuentran disponibles en todos los microprocesadores comerciales.
Tipos de interrupciones
Interrupciones por Timer
Estas interrupciones son disparadas cuando se cumple un cierto tiempo que nosotros mismos definimos, son muy usadas para controlar y leer sensores cada determinado tiempo y también para crear señales PWM para alimentar motores de corriente continua.
Como estas interrupciones se ejecutan cada un determinado tiempo, somos dependientes del cristal y del tiempo de cada pulso de clock de nuestro arduino. Pero gracias a que utilizamos librerías no tenemos que hacer ninguna cuenta, es importante saber que somos dependientes de eso, es decir no es lo mismo tener un cristal de 32 que uno de 16 MHZ. Pero como todos los arduinos traen un cristal de 16MHZ no nos preocupemos demasiado.
Los timers tienen algo que se conoce como prescaler, son divisores de la frecuencia de clock, cuando dividimos la frecuencia ganamos en tiempo pero perdemos en precisión, la frecuencia del cristal se puede dividor por 1,8,64,256
El siguiente ejemplo ejecuta una interrupcion por timer cada 250ms y prende y apaga un led cada vez que se la atiende.
#include <TimerOne.h> const int led = 13; // the pin with a LED int ledState = LOW; // El LED empieza apagado volatile unsigned long blinkCount = 0; // La definimos como volatile void setup(void) { pinMode(led, OUTPUT); Timer1.initialize(250000); // Dispara cada 250 ms Timer1.attachInterrupt(ISR_Blink); // Activa la interrupcion y la asocia a ISR_Blink Serial.begin(9600); } void ISR_Blink() { ledState = !ledState ; blinkCount++ ; // Contador veces se enciende el LED } void loop(void) { unsigned long N; // Haremos copia del blinkCount digitalWrite(led, ledState); // Asignamos el valor del // status a la salida noInterrupts(); // Suspende las interrupciones N = blinkCount; interrupts(); // Autoriza las interrupciones Serial.print("Ciclos = "); Serial.println(N); delay(100); }
Interrupción por hardware
Estas interrupciones son muy utilizadas en rebotica, se utilizan para detectar eventos externos. Por ejemplo si queremos que cada vez que alguien apriete un botón suceda alguna acción tenemos dos opciones. La primera alternativa y sin duda la peor de todas es quedarnos esperando que alguien apriete el botón, claramente no podríamos hacer otra cosa que esperar y estaríamos gastando toda la capacidad del microprocesador en no hacer nada, simplemente esperando que alguien apriete el botón. La segunda opción es usar las interrupciones de arduino (o del micro controlador que estemos usando).
Usar las interrupciones nos da como ventaja que podemos hacer cualquier otra cosa mientras esperamos que el evento suceda, simplemente cuando el evento se produce se acciona una bandera de interrupción que detiene y altera el flujo natural del programa para atender esa interrupción, básicamente es un orden de prioridades.
Como comente recién, manejar los eventos externos mediante interrupciones es la manera correcta de hacerlo, las ventajas están a la vista, gastamos pocos recursos del micro procesador y cumplimos el mismo objetivo.
Pero bueno, vamos al detalle. A la hora de usar las interrupciones en arduino necesitamos lo siguiente.
- Conocer que arduino estamos usando: No todos tienen los mismo pines de interrupción.
- Pin de arduino que reciba la condición de disparo.
- Una condición de disparo
- Una función que se ejecute cuando la interrupción sea disparada.
La condición de disparo se refiere a si la interrupción se va a ejecutar cuando detecte el paso del pin de interrupción de un estado alto a un estado bajo FALLING o cuando detecte el paso de un estado bajo a uno alto RISING.
Cuando eso ocurra arduino debe saber que hacer, para eso tenemos que definir una función que sera la encargada de atender la interrupción.
En el siguiente ejemplo se muestra como al presionar un botón vamos contando y enviando el dato por puerto serie para visualizarlo en pantalla, la interrupción es detectada y disparada cuando el pin de interrupción pasa de un estado alto a uno bajo FALLING.
int contador = 0; int n = contador ; void setup() { Serial.begin(9600); attachInterrupt( 0, ServicioBoton, FALLING); } void loop() { if (n != contador) { Serial.println(contador); n = contador ; } } void ServicioBoton() { contador++ ; }
Para usar las interrupciones y no generar conflictos con otras lógicas, y no alterar demasiado el flujo del programa, la atención de la interrupción debe ser rápida, lo mas rápida que se pueda, no es para nada recomendable hacer un bucle dentro de la función que atiende la interrupción.
En el programa de ejemplo no seria conveniente hacer el print en pantalla dentro de la interrupción, estaríamos generando demasiada demora en la atención de la interrupción.
Es importante entender que cuando la interrupción se esta atendiendo todo lo demás queda en pausa, el stack pointer por ejemplo, la función millis() queda en pausa hasta que salga de la atención de la interrupción.
En resumen es importante tener presente lo siguiente.
- Cuando se esta atendiendo una interrupción, todas las demás interrupciones son ignoras hasta que no se termine de atender la interrupción presente.
- Es importante no estar mucho tiempo atendiéndola, hay que hacer todo rápido y salir.
- La función millis() queda congelada hasta que finalice la atención.
- No hay que definir variables dentro de la interrupción, si se necesita usar variables deben ser definidas como globales fuera de ella.
- Jamas hacer un Seriel.println dentro de una interrupción, primero por que tardaría demasiado y segundo por que es posible que ni siquiera funcione dependiendo de algunas variables.
Como siempre digo, espero que les sea de utilidad y respondo todos sus comentarios, todo lo que quieran aportar sera bienvenido y ayuda a mejorar.
No dudes en dejar tu opinión.
-
-
buenos dias. soy pablo de argentina.
estoy usando un arduino mega y el ide de arduino (no recueerdo la version en este momento pero es una de las últimas )
en mi codigo habilito el timer1 para q imterrumpa cada 1 segundo. el problema esta en que cuado dejo de usarlo necesitaría poder desactivar la interrupcion del mismo y luego volver a habilitarla cuando sea necesario. eso es posible?
toque un par de registro pero hasta el momento sin exito. gracias-
Pablo, Si se tiene que poder , yo no soy aún experto con arduino (soy de la vieja escuela), pero si se tiene que poder, seguí buscando en internet, info porque sé que se puede, seguramente se tiene que activar o desactivar un bit en algún registro, y con eso lo gobernás, es decir, activás o desactivás el timer.
Fijate un poco en el artículo de este Señor, usa casi las cosas en assemble, con las instrucciones cli y sei, habilita o deshabilita las interrupciones.
https://jaimedearcos.github.io/arduino/2017/01/04/Timers/
Saludos
-
-
buenas noches, estoy haciendo un banner (leds en desplazamiento). Y me piden con un interruptor externo detener el movimiento.
Es decir si mi banner dice "Hola mundo" y el movimiento va de derecha a izquierda y presiono el pulsador en la L ... ya me habría pasado Hol.. Y se quedaría mostrando la 'L'.. hasta que decida que avance. .
Como podría hacer ese código de interrupción externa. Ayuda por favor 🙁
Deja un comentario
Soy aficionado a la programación en C. La explicación que has dado no puede estar mas clara.
Gracias por la aportación, muy interesante