Interrupciones en arduino
Que es una interrupcin?
Las interrupciones se refieren al paro inmediato del funcionamiento comn del
sistema. Esto es muy til para hacer que las cosas sucedan automticamente en
programas que sean sensibles a estados, tambin pueden ayudar con problemas de
temporizadores. Tambin puede servir para detener completa o parcialmente el
sistema en caso de que algn evento especifico suceda.
Un ejemplo de como utilizar estas interrupciones sera el de un lector de datos por
ejemplo un sensor de temperatura. Suponiendo que al inicio del programa se toma
lectura de la temperatura despus se llama una funcin que cree la estadsticas, se
empaqueta, se manda por algn protocolo seguro y despus hace otra lectura.
Ahora bien si llegamos a tener una aumento de temperatura inesperado queremos
que tome esa temperatura y la enve, pero esto es un problema ya que el proceso
de creacin de estadstica, empaquetado y envi es tarado, ms sin embargo si se
le asigna una interrupcin a la lectura de temperaturas el programa entero
es interrumpido y se ejecuta la lectura para despus enviar los datos que son ms
importantes.
Tambin si tenemos un sistema que este enviando datos cualesquiera para
monitorearlos, podemos asignar una interrupcin que se accione con un botn para
detener el programa y poder utilizar los datos, o para dejar de recibir ms datos,
segn sea el caso.
Cmo usar una interrupcin con el arduino?
La mayora de las placas arduino tienen dos interrupciones externas.
Interrupcion externa 0 : ( pin digital 2 )
Interrupcion externa 1: ( pin digital 3 )
Esto para el arduino UNO.
Para usar interrupciones tenemos la funcin:
void attachInterrupt( interrupcion, funcion, modo )
Dicha funcin especifica que funcin se invoca cuando se produce una interrupcion
externa, si ya existia una funcin adjuntada al pin se substituye por la nueva.
Los parmetros de esta funcin son:
interrupcion: el nmero de la interrupcin ( Es un int y puede ser 0 o 1
esto para asignar el pin a usar ).
funcion: Es la funcin que se invocara cuando la interrupcin suceda, esta
funcion NO debe tener parametro NI tampoco devolver nada. Comunmente se le
conoce como rutina de interrupcin de servicio.
modo: Esto define el criterio de la interrupcin es decir cuando se
considerara como interrupcin. Existen 4 constantes predefinidas para poder
usarlas como modos.
LOW Se dispara la interrupcin cuando el pin tenga un valor bajo ( Cero lgico o
LOW ).
CHANGE Se dispara la interrupcin cuando el pin se tenga un valor alto ( Uno
lgico o HIGH ).
RISING Se dispara la interrupcin cuando el pin pase de valor bajo a alto ( LOW a
HIGH | 0 a 1 ).
FALLING Se dispara la interrupcin cuando el pin pase de valor alto a bajo ( HIGH
a LOW | 1 a 0 ).
Como dato queda decir que tambin se cuenta con funciones para activar o
desactivar el uso de las interrupciones. Con tan solo usar la
funcin noInterrupts() se asegura de que no se interrumpa la porcin
de cdigo que le sigue a esta funcin, tambin esta la funcininterrupts() que
hace lo contrario, ya que permite las [Link] puede ser til dependiendo
el caso y se maneja similar a los hilos y candados.
Podramos tener una porcin de cdigo as:
void loop() {
noInterrupts();
FuncionCritica();
interrupts();
funcionComun();
funcionComun2();
}
Ahora vemos como seria el diagrama de conexiones para realizar una prueba.
Basicamente es un push button, un led y un par de resistencias acomodadas de
manera que se puedan utilizar con el siguiente codigo.
De no utilizar las interrupciones, el led tendria un comportamiento un poco raro, ya
que pudiese no captar bien las seales que se le envian.
Esto es muy familiar respecto a los threads y los candados.
Manejo de Interrupciones con Arduino
El artculo 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?
Una de las formas comunes de interactuar con cosas que suceden "fuera" de nuestro
control es por ejemplo monitorear el estado de las lneas. Dentro de nuestro "loop"
principal podemos leer continuamente el valor de una lnea de entrada y ejecutar por
medio de una condicional un cdigo especifico en caso de que detectemos un cambio.
El problema de este mtodo es que el cdigo se vuelve sumamente complejo cuando
tenemos que monitorear muchas cosas o cuando nuestro loop principal simplemente
tiene que hacer cosas que toman demasiado tiempo.
En esta entrada vamos a comprender como utilizar las interrupciones externas del
Arduino. Esto nos ser de mucha utilidad para interactuar ya sea con el usuario o con
otros dispositivos de hardware que emitan "seales" que nos permitan saber que un
evento ha sucedido.
Antes de continuar preparen su display de 7 segmentos porque en esta entrada lo
vamos a re-utilizar.
Una cosa a la vez...
Los Arduino basados en los microcontroladores Amtel AVR pueden ejecutar una sola
secuencia de instrucciones a la vez. El problema de esto es por ejemplo, si ustedes
estn enviando nmeros a su display y luego el usuario presiona un botn. Si nuestro
ciclo toma mucho tiempo en ejecutarse puede que pase mucho tiempo antes de que
podamos leer la lnea de nuestro usuario y perdamos el evento, o tal vez simplemente
estamos ocupando un ciclo en hacer algo interesante. Esto es una "desventaja" de
microcontroladores sencillos y es algo con lo que las computadoras han tenido que
convivir prcticamente desde que fueron inventadas.
Como manejar eventos externos?
Afortunadamente los diseadores de microcontroladores pensaron en este problema
hace bastante tiempo y de ah surgieron las famosas "interrupciones". Las
interrupciones (omitiendo muchos detalles) funcionan de manera muy sencilla: Existe
una lista de eventos internos o externos que genera ya sea el microcontrolador o una
lnea externa y al detectarse estos eventos el microcontrolador detiene la secuencia de
comandos que est ejecutando. Luego de esto, llama al cdigo definido previamente
que se encarga de "manejar" esa interrupcin en especfico y luego, cuando ha
terminado de "atender la interrupcin" regresa a ejecutar la secuencia principal que
haba dejado pausada.
Tal vez no es una solucin perfecta pero nos permite "atender" cosas que
consideramos importantes por ejemplo cuando un dispositivo externo enva datos va
serial o cuando una lnea de interrupcin cambia su valor de 0 a 1 o viceversa.
Hablando un poco ms sobre las interrupciones
No hay un "estndar" de como se manejan o cuales on las interrupciones disponibles.
Cada fabricante implementa diferentes tipos de interrupciones dependiendo del
microcontrolador que han diseado y el uso que se le piensa dar. La ventaja con los
Arduinos es que como comparten un microprocesador comn los desarrolladores han
creado unas bibliotecas que permiten manejar interrupciones de una manera muy
sencilla.
Nota aclaratoria: Las funciones de manejo de interrupciones de la referencia del
Arduino son muy, pero muy bsicas. 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 caractersticas de cada
microprocesador en particular. Para aplicaciones ms avanzadas, si desean ms
informacin de como programar estas interrupciones, pueden revisar la
documentacin de AVR Libc en la seccin dedicada almanejo de interrupciones.
En Arduino podemos asociar el cdigo de las interrupciones a travs de la
funcinattachInterrupt(). Las interrupciones o eventos que podemos asociar a las
lneas mediante esta funcin en el Arduino Mega son las siguientes:
1.
Cuando el nivel lgico de la lnea es 0 (LOW)
2.
Cuando el nivel lgico de la lnea cambia independientemente de su
estado lgico (CHANGE)
3.
Cuando el nivel lgico cambia de 0 a 1 (RISING)
4.
Cuando el nivel lgico cambia de 1 a 0 (FALLING)
Antes de continuar, vamos a hacer un pequeo circuito que nos permita cambiar los
valores lgicos de las lneas de interrupcin:
Al circuito anterior se le conoce como "resistencia pull-up", su funcin es mantener un
nivel lgico en la compuerta aunque el circuito se encuentre abierto. Para nuestro caso
mantendr un 1 lgico permanente con el pulsador abierto y un 0 lgico cuando est
cerrado.
Luego, para capturar las interrupciones la tenemos un poquito ms fcil, simplemente
replicamos este circuito para cuantos interruptores querramos capturar y lo
conectamos a las lneas correspondientes. Para este ejemplo utilizaremos las lneas 18,
19 y 20 que corresponden a las interrupciones 3, 4 y 5 del Arduino.
Esta es una pequea foto de como se ven los pulsadores armados en la breadboard:
Notarn que uso resistencias de 10K. Usualmente se recomienda el uso de
resistencias de 4.7K, lstimosamente no tena a la mano a la hora de armar el circuito,
sin embargo, en la prctica 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, adems de algunas variables "volatiles".
En general, utilizaremos el modificador "volatile" para indicarle al compilador que
nuestra variable podra ser accedida desde distintas ubicaciones en el cdigo 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 interrupcin.
volatile long lastInt;
Las constantes de nombre "BTN" representan mis botones y las constantes "D" las
lneas a donde tengo conectado el decodificador a 7 segmentos.
Definiendo las funciones que modificarn los valores
Vamos a definir un par de funciones "ayudantes" extra que se encargarn de
incrementar y decrementar el contador.
// Incrementa y escribe el nmero al display
void increment() {
inc++;
inc = (inc < 10) ? inc : 0;
writeTo7Seg(inc);
}
// Decrementa y escribe el nmero al display
void decrement() {
inc--;
inc = (inc >= 0) ? inc : 9;
writeTo7Seg(inc);
}
El siguiente paso es definir el cdigo que se encargar de manejar las interrupciones,
vamos a realizar funciones muy simples, un botn incrementara el contador, el otro lo
disminuira y el ltimo encender y apagar el display de 7 segmentos. Con las
funciones anteriores el cdigo se vuelve muy fcil de entender:
// Codigo para la interrupcion 5
void int5() {
increment();
}
// Codigo para la interrupcion 4
void int4() {
decrement();
}
// Codigo para la interrupcion 3
void int3() {
onOff = onOff^1;
if(onOff) {
writeTo7Seg(15);
} else {
writeTo7Seg(inc);
}
}
Por defecto no hay ningn cdigo asignado para manejar las interrupciones, por ello
es necesario que definamos la asociacin de las interrupciones en el "setup".
void setup() {
// Establecemos las lneas 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 interrupcin a nuestro cdigo
attachInterrupt(5,int5,RISING); // lnea 18
attachInterrupt(4,int4,RISING); // lnea 19
attachInterrupt(3,int3,RISING); // lnea 20
//Colocamos un 0 en nuestro display de 7 segmentos
writeTo7Seg(inc);
Tal vez en este momento se estn haciendo la siguiente pregunta: Por qu uso RISING
y no otro tipo de interrupcin? La respuesta a esta pregunta realmente tiene mucho
que ver con lo que estemos programando y como querramos que funcione. En este
caso, quera que el contador se incrementara cuando el usuario dejara de presionar el
botn, como nuestro botn genera un 0 lgico cuando esta presionado y un 1 lgico
cuando esta sin presionar si queremos incrementar nuestro contador justo despues
de que se ha presionado el botn el evento RISING es el ms adecuado. Si en cambio
estuvieramos programando algo como un control de arcade posiblemente
deberamos usar la interrupcin FALLING para que el usuario no perciba un "retardo"
a la hora de presionar el botn y ver una respuesta en el juego.
Qu hacemos luego con el loop principal?
Pues nada, lo dejamos vaco ya que no lo utilizaremos en este ejemplo:
// Loop vaco
void loop() {
}
Bonus: Eliminando el Rebote va software.
Si toman el cdigo que puse anteriormente junto con el circuito que coloqu, se van a
dar cuenta que el contador funciona de manera errtica como se muestra en el
siguiente video:
[Link]
Este no es un problema de software sino ms bien de hardware. Es el famoso "rebote"
de interruptores.
Uno de los grandes problemas al trabajar con circuitos digitales e interruptores, es que
los interruptores simplemente no son perfectos. Dependiendo del diseo de los
mismos, estos pueden dar lugar a este molsto fenmeno.
El rebote literamente significa que los contactos dentro del interruptor rebotan
brevemente, esto sucede tan rpido que simplemente no nos damos cuenta, pero el
circuito digital en vez de recibir un solo pulso recibe varios.
Cmo corregir el rebote?
Existen diferentes mtodos va hardware para eliminacin de rebote de interruptores,
sin embargo si su aplicacin no necesita tiempos de respuesta "crticos". Pueden
utilizar esta sencilla solucin que les propongo a continuacin.
Primero, Recuerdan la variable "lastInt" que definimos al principio? Lo que vamos a
hacer para eliminar el rebote es "filtrar" nuestras interrupciones. La lgica que
utilizaremos es muy simple, si nuestra interrupcin 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 caratersticas fsicas 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 perfeccin.
Para hacer funcionar este eliminador de rebote por software simplemente vamos a
usar una condicional y antes de salir de la funcin guardaremos el valor de millis() y
vamos a comparar para ver si ha pasado tiempo suficiente.
Mucho
Cuidado!!! el
valor de millis() se reinicia automticamente
aproximadamente cada 70 das. Esto significa que si deciden usar este mtodo y
piensan tener funcionando su arduino sin parar durante ms de 70 das, tienen que
capturar la excepcin de reinicio del contador de milisegundos porque si no, llegara un
momento que nunca se cumplir la condicin y se ignorarn todas las interrupciones
que usen este mtodo. As que: "advertidos estn".
Una vez dada esta advertencia modifiquemos un poco nuestro cdigo 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 cdigo de interrupciones, esto en la
prctica significa que no solo estoy eliminando el rebote sino tambin la posibilidad de
presionar dos botones diferentes en menos de 200ms. Una posible solucin a este
bug es utilizar un marcador de tiempo diferente para cada interrupcin (latInt1,
lastInt2, lastInt3), de esta manera podramos eliminar efectivamente el rebote para
cada interruptor de manera independiente, sin bloquear la ejecucin del cdigo en
caso de recibir una interrupcin de una lnea diferente.
Probando Todo
Pueden descargar el cdigo 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 cdigo.
Si todo funciona bien deber de funcionar de manera similar a como se muestra en el
siguiente video:
[Link]
Conclusiones
Las interrupciones resultan sumamente tiles para ejecutar cdigo de manera
asncrona, es decir que nuestro cdigo 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 lnea de
interrupcin, sin embargo esto lo podemos dejar para una entrada posterior.
Ya solo nos queda un artculo ms para esta serie de tres entradas, en el ltimo
artculo leeremos datos de la Raspberry e intentaremos crear una aplicacin
interesante con nuestro mdulo de pruebas.
El post que traduzco de Atelier JMC nos va a instruir en el manejo de
las interrupciones. Lasinterrupciones son sucesos que pueden ocurrir en cualquier
momento por lo que no podemos prever exactamente cuando detectarlas. Por ejemplo,
podemos colocar un sensor en el frente de un vehculo para que detecte a distancia los
posibles obstculos que encuentre en su camino y lo informe a la tarjeta cambiando el
estado de un Pin.
En principio, podemos poner en determinado punto del bucle, leer el estado del pin, de
forma que el cambio de estado nos delatara la existencia del obstculo y de
encontrarlo, cambiar la trayectoria del vehculo para esquivarlo. Si en recorrer todo el
bucle se tarda milisegundos, la cosa funcionar muy bien, pero imaginemos que en un
punto del bucle hemos puesto una funcin delay que hace detener el funcionamiento
del bucle por un tiempo, igual que hacamos en el programa blinck.
Ocurre entonces que el bucle no se recorre en un corto periodo de tiempo, sino que
puede tardar a lo mejor un minuto. Como Arduino tarda un minuto en volver a leer el
estado del pin, no se enterar de la existencia del obstculo en ese tiempo, tiempo ms
que suficiente para que el vehculo choque con el obstculo. Cmo resolvemos el
problema?.
Para resolver este tipo de problemas existe varios medios, pero el ms sencillo es el
previsto para el manejo de interrupciones y para ello Arduino Uno tiene dos de los pines
especficos, el Pin 2 y el Pin 3, (como se explic en el capitulo 5 de la Introduccin a
Arduino , capaces de detectar interrupciones en cualquier instante, independientemente
de lo que en ese momento est haciendo la funcin principal loop, Cuando Arduino
detecta un cambio en el estado de uno de esos pines puede parar el funcionamiento del
bucle loopdonde se encuentre y lanzar una funcin asociada a esa interrupcin
inmediatamente, (en el modelo Arduino UNO, para continuar posteriormente el proceso
del bucle loop desde donde lo parara.
As pues en nuestro vehculo, podemos asociar el detector de obstculos al Pin2, (o al
Pin3), y tan pronto como se detecte un obstculo y cambie el estado del Pin2, se puede
lanzar una funcin que por ejemplo haga retroceder al vehculo y cambie el rumbo con
el fin de eludir el obstculo.
Como he indicado el hay dos pines capaces de detec tar en cualquier momento una
interrupcin los pines 2 y 3 y lanzar instantaneamente una tarea diseada para
hacerla frente. Por poner otro ejemplo, en un proyecto de vigilancia domtica, puedes
poner un sensor en la puerta de la calle, asociado al Pin2 y un detector de humo,
asociado al Pin3 y segn se abran la puerta de la calle o se detecte humo, lanzar dos
tareas diferentes. En tarjetas de la familia Arduino ms potentes existen mas pines que
controlan interrupciones, por lo que en nuestro caso podras por otras alarmas, como
para inundaciones, viento etc.
El aprender a manejar las interrupciones facilita mucho como resolver los problemas en
los que se producen hechos que se desconocen el instante en que van a producirse,
sea apretar un botn, o detectar una circunstancia de peligro. Os dejo con el post de
Atelier JMC
El mandato Delay () y las interrupciones
Es casi una cuestin filosfica! .Eso de que mientras Arduino est ocupado en
atender una instruccin delay(), se ocupa todava de manejar las interrupciones?. El
documento oficial dice s, pero un comentario en la pgina oficial francesa dice lo
contrario Quin tiene razn? Aqu un pequeo programa que nos va a permitir
conocer la respuesta:
/* Las interrupciones estn activas cuando se ejecuta un delay???
Responder que s!
Cuando pulsamos el botn:
Cambia el estado del Pin 2 (pasa a baja)
La funcin de interrupcin se activa
El estado cambia
El LED cambia de estado y se apaga
Cuando suelte el botn:
El Pin 2 cambia de estado
(Pasa a alta debido la efecto de recordar a ms, el pull-up)
La funcin de interrupcin se activa
El estado cambia
El LED cambia de estado y se enciende
*/
// Pin correspondiente al botn
int bzero = 2, // El Pin 2 corresponde al botn
// Pin correspondiente al LED
int ledPin = 4;
// La variable tipo volatile que comunica entre la
// funcin de interrupcin y bucle principal (loop)
voltile int etat = HIGH ;
void setup () {
// El pin conectado al botn se abre en modo INPUT
pinMode (Bzero, INPUT) // Pin en modo entrada
// Activacin de la resistencia interna pull-up
digitalWrite (Bzero, HIGH );
// El pin est preparado para encender el LED
pinMode (ledPin, OUTPUT );
// La interrupcin 0 monitorea los cambios de estado del pin 2.
attachInterrupt(0, inter0, CHANGE);
} // fin de setup
void loop () {
// No hacemos nada ms que esperar
delay (30000);
} // fin del bucle
void inter0 () { // Se ejecuta cuando se detecta un cambio de estado del Pin 2
etat =! etat // Cambia el estado. De HIGH a LOW y de LOW a HIGH
igitalWrite (ledPin, etat) / / encender o apagar el LED
} / / fin inter0
El montaje comporta dos resistencias de 270 ohmios, un al lado del botn pulsador (as
si se pone por error el pin 2 OUPUT, no habr desastres), la otra es la resistencia en
serie con el LED que le permite funcionar a la tensin adecuada. Los hilos verdes estn
conectados a tierra (GND).
Nota del traductor
Quiz no recuerde en qu consiste un montaje de un Intyerrptor, en forma urtilizando la
resistencia interna en forma Pull Down. Een ese caso lo mejor es que lea el Captulo
Cuatro de la Introduccion a Arduino que se refiere especficamente a ese tema y a
al uso de la resitencia interna que tiene Arduino, que facikita enormemente la creacion
de circuitos que detecten cuando pulsamos un botn- Salvado ese escollo entremos en
lo que es nuevo en este post
Como vemos gestionar una interrupcin exige;
volatile
En primer lugar definir previamente una variable del tipo volatile . Esta variable se
caracteriza por que su valor pueda ser modificado por algo fuera de la seccin del
cdigo en el que aparece, o sea en una funcin concurrente. asociadas a interrupciones,
tambien llamadas rutina de servicio de interrupcin (interrupt service routine).
La sintaxis de volatile es:
Volatile definicin_de_variable
volatile indica que la variable que definimos a continuacin la cambie siempre que
se produzca una interrupcin
definicin de variable la definimos como es habitual (tipo nombre = valor inicial)
En este ejemplo:
corto)
En el ejemplo del 5 captulo :
largo)
volatile int etat = HIGH
(tipo entero
volatile long nb_chgt = 0
(tipo entero
attachInterrupt
En segundo lugar hay que informar a Arduino que utilizaremos la interrupcin lo que
hacemos como es habitual en funcin setup con la instruccin
attachInterrupt. Este mandato especifica la funcin a invocar cuando se produce
una interrupcin externa. La mayora de las placas Arduino tienen dos interrupciones
externas: Las nmero 0 (en el pin digital 2) y la 1 (en el pin digital 3). Arduino Mega
tiene otras cuatro: Las nmero 2 (pin 21), 3 (pin 20), 4 (pin 19) y 5 (pin 18).
La sintaxis de attachInterrupt es:
attachInterrupt(nint, nfuncion, modo)
attachInterrupt (adjuntar interrupcin), avisa a Arduino que vamos a utilizar
interrupciones
nint nmero de interrupcin. 0 si utilizamos el pin2, 1 si utilizamos el pin 3
nfuncion nombre de la funcin que invocamos.
modo es el acontecimiento que provoca la interrupcin: Los acontecimientos
contemplados son:
LOW (bajo)disparara la interrupcin cuando el pin pase a valor bajo (LOW).
CHANGE (cambio) disparara la interrupcin cuando el pin cambia de valor .
RISING (aumentar) dispara la interrupcin cuando el pin pasa de LOW a
HIGH
FALLING (caer) dispara la interrupcin cuando el pin pasa de HIGH a LOW
En este ejemplo : attachInterrupt(0, inter0, CHANGE)
En el ejemplo del 5 captulo attachInterrupt(0,gere_int0,CHANGE)
interrupt service routine
Por ltimo tendremos que definir lo que se hace cuando se dispara la interrupcin, o sea
la rutina de servicio de interrupcin o en ingls interrupt service routine.
Esta rutina no admite ni parmetros de entrada ni devuelve nada pues los valores que
pudiramos pasarla a no ser de tipo volatile no estn presentes en la memoria RAM del
procesador cuando comienza inesperadamente su actuacin y por otra parte no tendra
sentido entregar parmetros que fuera a utilizar alguien fura de esta funcin sin saber si
van a existir o no. Por tanto debe ser del tipo void nfuncion () como lo son por ejemplo
las funciones setup y loopque tampoco tienen parmetros ni devuelven nada.
Tambien hay que tener al programarla que los mandatos relacionados con el transcurso
del tiempo, no funcionan, pues el ordenador de forma virtual ha interrumpido
precisamente el paso del tiempo para congelar la funcin loop. Por ello las
interrupciones son esencialmente atemporales, y dentro de ellas no funcionan
correctamente ni el mandato delay que para el programa durante el tiempo que se
le indique, ni el mandato millis que devuelve el tiempo en milisigundos en que ha
estado funcionando Arduino desde que arranco por ltima vez.
Tambien puede ocurrir y hay que tener en cuenta, que si se envan datos serie desde el
exterior, por ejemplo del PC a Arduino, cuando se encuentra ocupado con una
interrupcin, pueden perderse, pues el ordenador est entonces volcado a hacer otra
cosa, lo que mande hacer la interrupcin
Explicaremos en otro sitio, (quiz dediquemos un post a ello dada la importancia del
tema), para estudiar con detalle como se definen funciones hechas por el usuario. Debe
momento confrmese con ver estos dos ejemplos.
En este ejemplo
void inter0 () { // Se ejecuta cuando se detecta un cambio de estado del Pin 2
etat =! etat // Cambia el estado. De HIGH a LOW y de LOW a HIGH<
digitalWrite (ledPin, etat) / / encender o apagar el LED
} / / fin inter0
El autor utiliza un medio muy elegante para cambiar de la variable del sistema HIGH a
LOW. Como solo son posibles estos dos estados de los pines la expresin etat =! etat
(estado distinto estado) obliga a la variable de tipo volatile,(y por tanto siempre
presente en el procesador) a cambiar de lo que es ahora a otra cosa que solo puede ser
lo contrario. Yo que no se tanto y adems soy mucho menos elegante escribira algo que
hace lo mismo y es mucho ms largo, (aunque quiz un poco ms claro):
if /etat == high) {
etat = LOW
}
else {
etat = HIGH
}
Esto demuestra que en informtica casi siempre hay varias soluciones que hacen lo
mismo pero eso no quiere decir que todas sean iguales, unas son ms elegantes que
otras y no es solo cuestin de elegancia, suelen ser ms rpidas, utilizar menos
recursos, y con procesadores menos potentes, es decir son ms econmicas, algo que
en los comienzos de la informtica se valor mucho y luego con la cada de precios de
los ordenadores no tanto pues costaba menos comprar ordenadores grandes y potentes
que tener programadores finos, (y mejor pagados), pero que ahora que volvemos a
tener esta vez hardware de consumo poco potente, vuelve a estar en alza.
Cambiado el valor que puede tener el pin4 que queremos encender y p apagar segn la
interrupcin detecte que hemos apretado el botn solo falta encenderle o apagarlo con
el ltimo mandato de la funcin que hemos definido para ejecutar con la interrupcin
En el ejemplo del 5
captulo [Link] la funcin del servicio de interrupcin definida es la siguiente;
void gere_int0() { // Gestin de la interrupcin Contador de interrupciones
nb_chgt = nb_chgt + 1 ;
}
Simplemente cada vez que se produce un cambio en el estado de pin controlado por la
interrupcin incrementamos en uno el contador nb_chgt definido como volatile
Arduino, tiene dos fuentes de interrupciones externas, los pines 2 y 3. Eso quiere decir que al
producirse un evento(configurable) en alguno de estos pines, se interrumpir al Atmega y se
ejecutar el cdigo asociado a esa interrupcin.
Para declarar una interrupcin lo haremos en el cuerpo del setup, de la siguiente forma:
/* attachInterrupt(A, B, C)
* A
can be either 0 or 1 for interrupts on pin 2 or 3.
*
* B
Name of a function you want to execute while
* in interrupt A.
*
* C
Trigger mode of the interrupt pin. can be:
*
LOW
a low level trigger
*
CHANGE
a change in level trigger
*
RISING
a rising edge of a level trigger
*
FALLING
a falling edge of a level trigger
*
* In all but the IDLE sleep modes only LOW can be used.
*/
Por ejemplo,
attachInterrupt(0, ArCanInterrupt, FALLING);
El primer parmetro define el uso de la interrupcin 0, la que est en el Pin 2. Cuando en el
Pin 2 se detecta un flanco de bajada(3 parmetro), es decir, pasa de 1 a 0, entonces
ejecutar el cdigo que definamos en ArCanInterrupt().
Por ejemplo,
void ArCanInterrupt()
{
//Almacenamos el mensaje en el buffer_rx
ArCan.get_message_rx_buffer()
}
En las prxima versin de la librera estar incluido el uso de interrupciones.
Consejo: El cdigo que se ejecuta en una interrupcin debe ser lo ms rpido posible.
Gestin de interrupciones
Publicado el 24 agosto, 2012
Cuando queremos gestionar entradas/salidas con nuestro arduino,
normalmente asociamos una accin a una entrada, pero si el software es
grande o lento, puede que no tengamos el resultado esperado.
Hay dos formas de gestionar la entrada/salida de datos:
consulta de estado
Interrupciones
La consulta de estado puede ser un simple if, es una parte del cdigo
que corre dentro de la rutina (o funcin) principal. Esto hace que
peridicamente, lleguemos a esta parte del cdigo y acceda a la
consulta, veamos un ejemplo:
1
// set pin numbers:
const int buttonPin = 2; // the number of the pushbutton pin
const int ledPin = 13; // the number of the LED pin</code>
4
5
6
// variables will change:
int buttonState = 0; // variable for reading the pushbutton status
7
8
9
10
11
12
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
}
13
14
void loop(){
15
// read the state of the pushbutton value:
16
buttonState = digitalRead(buttonPin);
17
18
// check if the pushbutton is pressed.
19
// if it is, the buttonState is HIGH:
20
if (buttonState == HIGH) {
21
// turn LED on:
22
digitalWrite(ledPin, HIGH);
23
24
25
26
}
else {
// turn LED off:
digitalWrite(ledPin, LOW);
27
28
}
}
29
Esto est bien para cdigos pequeos, o que no necesiten realizar otras
tareas pero, qu pasa si el cdigo es muy largo, o muy lento?
1
// set pin numbers:
const int buttonPin = 2; // the number of the pushbutton pin
const int ledPin = 13; // the number of the LED pin</code>
4
5
6
// variables will change:
int buttonState = 0; // variable for reading the pushbutton status
7
8
9
10
11
12
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
}
13
14
void loop(){
15
// read the state of the pushbutton value:
16
buttonState = digitalRead(buttonPin);
17
18
// check if the pushbutton is pressed.
19
// if it is, the buttonState is HIGH:
20
if (buttonState == HIGH) {
21
// turn LED on:
22
digitalWrite(ledPin, HIGH);
23
24
25
26
27
28
29
else {
// turn LED off:
digitalWrite(ledPin, LOW);
}
delay(50000); //50 segundos de espera
}
30
Cuanto tardar en llegar otra vez la comprobacin? Una eternidad si lo
que queremos es una respuesta rpida. Y si encima, se nos pasa, pues
ya
Pero bueno, podemos decirle al procesador que espere que pulsemos el
botn:
1
// set pin numbers:
const int buttonPin = 2; // the number of the pushbutton pin
const int ledPin = 13; // the number of the LED pin</code>
4
5
6
// variables will change:
int buttonState = 0; // variable for reading the pushbutton status
7
8
9
10
11
12
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
}
13
14
void loop(){
15
// read the state of the pushbutton value:
16
buttonState = digitalRead(buttonPin);
17
18
19
// check if the pushbutton is pressed.
20
// if it is, the buttonState is HIGH:
21
while (buttonState == LOW) {
22
// turn LED off:
23
digitalWrite(ledPin, LOW);
24
25
digitalWrite(ledPin, HIGH);
26
Pero otro de los problemas de la consulta de estado es que durante el
tiempo que est esperando que se produzca, el procesador no est
haciendo nada til.
Para solucionar esto, las interrupciones funcionan de la siguiente forma,
una vez recibida la interrupcin, el procesador deja lo que estaba
haciendo y se va pitando a procesarla.
Un ejemplo muy bsico para ver qu son las interrupciones y la consulta
de estado:
Estamos leyendo un libro y en media hora hemos de hacer una llamada
telefonica, podemos hacer 2 cosas;
consulta de estado
vamos mirando a cada prrafo el reloj
Interrupciones
ponemos una alarma al reloj y vamos leyendo
Es mucho ms eficiente poner la alarma, no?
Arduino UNO (ms bien el Atmega 168 y 328) dispone de dos niveles de
interrupcin por Hardware, 0 y 1 asociados a los pines 2 y 3, estos
definen la prioridad de la interrupcin. La interrupcin por hardware
significa que son eventos que se producen fuera de nuestro cdigo y que
el procesador las debe tratar. Las interrupciones por software se les
llama excepciones y son interrupciones a nuestro cdigo debido
generalmente a errores en nuestro cdigo que las provoca (buffer
overflows, divisiones por 0 ), estas excepciones no las voy a tratar
aqu, pero hay que tenerlas en cuenta para depurar nuestro cdigo
correctamente.
Un ejemplo muy cutre de una interrupcin por software sera que
estamos leyendo el libro y nos viene un apretn poco ms que decir
haced el debugging y listo xD
En cuanto a las interrupciones por hardware, nuestro programa principal
va ejecutando un cdigo, en el cual el flujo de trabajo es contnuo, y las
interrupciones marcan una serie de pautas extras que pueden afectar o
no a nuestro cdigo, pero puede que afecte al comportamiento del
sistema.
1
// set pin numbers:
const int buttonPin = 2; // the number of the pushbutton pin
const int ledPin = 13; // the number of the LED pin</code>
4
5
6
// variables will change:
int buttonState = 0; // variable for reading the pushbutton status
7
8
9
10
11
12
13
14
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
attachInterrupt(0, enciendeLed, HIGH);
attachInterrupt(0, apagaLed, LOW);
}
15
16
void loop(){
17
//aqui puedes poner el codigo que quieras
18
19
20
delay(50000);
}
21
22
23
24
25
26
void enciendeLed(){
digitalWrite(ledPin, HIGH);
}
void apagaLed(){
digitalWrite(ledPin, LOW);
}
27
Aqu un ejemplo prctico para usar una interrupcin, una maqueta de un
tren, en la cual, el Arduino controla la velocidad desde un potencimetro
y mediante interrupcin, controla su direccin mediante un rel:
1
int pulsadorDir = 2;
int dirTren = 8;
int pwmTren = 3;
int potenciometroTren= 0;
5
6
int state = LOW;
int velocidadTren = 0;</code>
7
8
9
void setup(){
[Link](9600);
10
//configuracion de pines
11
12
13
pinMode(pulsadorDir, INPUT);
pinMode(dirTren, OUTPUT);
pinMode(pwmTren, OUTPUT);
14
15
attachInterrupt(0, direccionTren, FALLING); //(nivel int, funcion, en que estado
16
17
18
19
void loop(){
20
21
22
23
24
//Si el switch no esta pulsado, gira en una direccion, si no en la contraria
velocidadTren = analogRead(potenciometroTren); // Lectura del valor del potencio
velocidadTren = 800 + (velocidadTren/6); // Para establecer la velocidad del tre
analogWrite(pwmTren, velocidadTren);
digitalWrite(ledPin, HIGH);
}
25
26
27
void direccionTren(){
state = !state;
28
digitalWrite(dirTren, state); // Establece la direccin del tren
29
30
El circuito de control de la maqueta es bastante simple, el Arduino lee el
valor desde un potencimetro y por PWM imprime la velocidad para que
la locomotora se mueva. El cdigo de la interrupcin aade el sentido en
que tiene que circular la locomotora para evitar que, al llegar cerca del
extremo de la va, si la siguiente lectura del pulsador llega tarde, el tren
pueda descarrilar.
La sintaxis de la interrupcin es bastante sencilla de escribir:
en setup tenemos que definir que vamos a utilizar una interrupcin, en
qu nivel, una funcin (llamada rutina de servicio de interrupcin) que se
encargara de tratarla y en qu evento se dispara.
1
attachInterrupt(0, direccionTren, FALLING);
El evento en que queremos disparar la interrupcin tiene 4 modos,
debido a los 4 eventos que se pueden producir en una seal elctrica:
LOW lanza la interrucin cuando el pin est a nivel bajo
CHANGE lanza la interrupcin cuando el pin cambia de valor
RISING lanza la interrupcin cuando el pin pasa de nivel bajo a
nivel alto
FALLING lanza la interrupcin cuando el pin pasa de nivel alto a
nivel bajo
y luego definimos una rutina (que podemos utilizar como una funcin
normal y corriente si es necesario).
1
void direccionTren(){
state = !state;
digitalWrite(dirTren, state); // Establece la direccin del tren
Y aqu esta el resultado:
[Link]
Tutorial cap.4 Interrupciones externas:
[Link]
Tutorial Cap. 5 interrupciones internas:
[Link]
Ejemplo seguidor solar con interrupciones:
[Link]