Felix Maocho

Para quien le interese lo que a nosotros nos interesa

Arduino – Manejo de interrupciones

Por Félix Maocho
6/8/2013

El post que traduzco de Atelier JMC nos va a instruir en el manejo de las interrupciones. Las interrupciones 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 vehículo para que detecte a distancia los posibles obstáculos 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 obstáculo y de encontrarlo, cambiar la trayectoria del vehículo 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 función “delay” que hace  detener el funcionamiento del bucle por un tiempo, igual que hacíamos 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 obstáculo en ese tiempo, tiempo más que suficiente para que el vehículo choque con el obstáculo. ¿Cómo resolvemos el problema?.

Para resolver este tipo de problemas existe varios medios, pero el más sencillo es el previsto para el manejo de interrupciones y para ello Arduino Uno tiene dos de los pines específicos, el Pin 2 y el Pin 3, (como se explicó en el capitulo 5 de la Introducción a Arduino , capaces de detectar interrupciones en cualquier instante, independientemente de lo que en ese momento esté haciendo la función principal “loop”,  Cuando Arduino detecta un cambio en el estado de uno de esos pines puede parar el funcionamiento del bucle “loop”donde se encuentre y lanzar una función asociada a esa interrupción inmediatamente, (en el modelo Arduino UNO, para continuar posteriormente el proceso del bucle “loop” desde donde lo parara.

Así pues en nuestro vehículo, podemos asociar el detector de obstáculos al Pin2, (o al Pin3), y tan pronto como se detecte un obstáculo y cambie el estado del Pin2, se puede lanzar una función que por ejemplo haga retroceder al vehículo y cambie el rumbo con el fin de eludir el obstáculo.

Como he indicado el hay dos pines capaces de detec tar en cualquier momento una interrupción los pines 2 y 3 y lanzar instantaneamente una tarea diseñada para hacerla frente. Por poner otro ejemplo, en un proyecto de vigilancia domótica, puedes poner un sensor en la puerta de la calle, asociado al Pin2 y un detector de humo, asociado al Pin3 y según se abran la puerta de la calle o se detecte humo, lanzar dos tareas diferentes. En tarjetas de la familia Arduino más potentes existen mas pines que controlan interrupciones, por lo que en nuestro caso podrías 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 botón, o detectar una circunstancia de peligro. Os dejo con el post de Atelier JMC

El mandato Delay () y las interrupciones

¡Es casi una cuestión filosófica! .¿Eso de que mientras Arduino está ocupado en atender  una instrucción delay(), se ocupa todavía de manejar las interrupciones?. El documento oficial dice sí, pero un comentario en la página oficial francesa dice lo contrario … ¿Quién tiene razón? Aquí un pequeño programa que nos va a permitir conocer la respuesta:

/* ¿Las interrupciones están activas cuando se ejecuta un delay???
¡Responder que sí!
Cuando pulsamos el botón:
Cambia el estado del Pin 2 (pasa a baja)
La función de interrupción se activa
El estado cambia
El LED cambia de estado y se apaga
Cuando suelte el botón:
El Pin 2 cambia de estado
(Pasa a alta debido la efecto de recordar a más, el pull-up)
La función de interrupción se activa
El estado cambia
El LED cambia de estado y se enciende
* /

// Pin correspondiente al botón
int  bzero = 2, // El Pin 2  corresponde al botón
// Pin correspondiente al LED
int  ledPin = 4;
// La variable tipo volatile  que comunica entre la
// función de interrupción y bucle principal (loop)
volátile int etat = HIGH ;

void  setup () {

// El pin conectado al botón se abre en modo INPUT
pinMode (Bzero, INPUT)  // Pin en modo entrada
// Activación de la resistencia interna pull-up
digitalWrite (Bzero, HIGH );
// El pin está preparado para encender  el LED
pinMode (ledPin, OUTPUT );
// La interrupción  0 monitorea los cambios de estado del pin 2.
attachInterrupt(0, inter0, CHANGE);

} // fin de setup

void  loop () {

// No hacemos nada más 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 botón 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 tensión adecuada. Los hilos verdes están 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 Capítulo Cuatro de la Introduccion a Arduino que se refiere específicamente 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 botón- Salvado ese escollo entremos en lo que es nuevo en este post

Como vemos gestionar una interrupción 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 sección del código en el que aparece, o sea en una función concurrente. asociadas a interrupciones, tambien llamadas rutina de servicio de interrupción (interrupt service routine).

La sintaxis de “volatile” es:

Volatile definición_de_variable

  • “volatile” indica que la variable que definimos a continuación la cambie siempre que se produzca una interrupción
  • “definición de variable” la definimos como es habitual (tipo nombre = valor inicial)
  • En este ejemplo:                            volatile int etat = HIGH                   (tipo entero corto)
  • En el ejemplo del 5 capítulo :     volatile long nb_chgt = 0                 (tipo entero largo)

attachInterrupt

En segundo lugar hay que informar a Arduino que utilizaremos la interrupción lo que hacemos como es habitual en función “setup” con la instrucción “attachInterrupt”. Este mandato especifica la función a invocar cuando se produce una interrupción externa. La mayoría de las placas Arduino tienen dos interrupciones externas: Las número 0 (en el pin digital 2) y la 1 (en el pin digital 3). Arduino Mega tiene otras cuatro: Las número 2 (pin 21), 3 (pin 20), 4 (pin 19) y 5 (pin 18).

La sintaxis de “attachInterrupt” es:

attachInterrupt(nint, nfuncion, modo)

  • “attachInterrupt” (adjuntar interrupción), avisa a Arduino que vamos a utilizar  interrupciones
  • “nint” número de interrupción. 0 si utilizamos el pin2, 1 si utilizamos el pin 3 “
  • “nfuncion” nombre de la función que invocamos.
  • “modo” es el acontecimiento que provoca la interrupción: Los acontecimientos contemplados son:
    • LOW (bajo)disparara la interrupción cuando el pin pase a valor bajo (LOW).
    • CHANGE (cambio) disparara la interrupción cuando el pin cambia de valor .
    • RISING (aumentar) dispara la interrupción cuando el pin pasa de LOW a HIGH
    • FALLING (caer) dispara la interrupción cuando el pin pasa de HIGH a LOW
  • En este ejemplo :   attachInterrupt(0, inter0, CHANGE)
  • En el ejemplo del 5 capítulo attachInterrupt(0,gere_int0,CHANGE)

interrupt service routine

Por último tendremos que definir lo que se hace cuando se dispara la interrupción, o sea la rutina de servicio de interrupción o en inglés interrupt service routine.

Esta rutina no admite ni parámetros de entrada ni devuelve nada pues los valores que pudiéramos pasarla a no ser de tipo volatile no están presentes en la memoria RAM del procesador cuando comienza inesperadamente su actuación y por otra parte no tendría sentido entregar parámetros que fuera a utilizar alguien fura de esta función 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 “loop” que tampoco tienen parámetros 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 función “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 envían datos serie desde el exterior, por ejemplo del PC a Arduino, cuando se encuentra “ocupado” con una interrupción, pueden perderse, pues el ordenador está entonces volcado a hacer “otra cosa”, lo que mande hacer la interrupción

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 confórmese 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 expresión 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 además soy mucho menos elegante escribiría algo que hace lo mismo y es mucho más largo, (aunque quizá un poco más claro):

if /etat == high) {
etat = LOW
}
else {
etat = HIGH
}

Esto demuestra que en informática casi siempre hay varias soluciones que hacen lo mismo pero eso no quiere decir que todas sean iguales, unas son más elegantes que otras y no es solo cuestión de elegancia, suelen ser más rápidas, utilizar menos recursos, y con procesadores menos potentes, es decir son más económicas, algo que en los comienzos de la informática se valoró mucho y luego con la caída 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 según la interrupción detecte que hemos apretado el botón solo falta encenderle o apagarlo con el último mandato de la función que hemos definido para ejecutar con la interrupción

En el ejemplo del 5 capítulo https://felixmaocho.wordpress.com/2013/07/23/arduino-atelier-jcm-capitulo-cinco/   la función del servicio de interrupción definida es la siguiente;

void gere_int0() {  // Gestión de la interrupción Contador de interrupciones
nb_chgt = nb_chgt + 1 ;
}

Simplemente cada vez que se produce un cambio en el estado de pin controlado por la interrupción incrementamos en uno el contador nb_chgt definido como “volatile”

Felix Maocho

Articuklo ariginal  Delay() et les interruptions

 

5 agosto 2013 - Posted by | Robotica | , , , , ,

13 comentarios »

  1. Es posible hacer una interrupción para un sensor con datos analógicos? Es decir en vez de hacer la interrupción para un pulsador (que solo envía HIGH o LOW), hacerla para un LDR o un ultrasonidos.

    Excelente trabajo!

    Comentarios por carlos | 7 agosto 2013 | Responder

    • Yo no se hacer interrupciones analógicas y tampoco estoy seguro de que se pueda hacer, pues las ínicas entradas que conozco que admiten interrupciones en Arduino UNO son pin2 y pin3 que son digitales. Ello no quiere decir que no se pueda hacer necesariamente, sino que simplemente YO NO LO SE HACER pero yo solo soy un principiante y descom nozco muchisimas cosas de Arduino..

      SI yo tuviera que hacer frente a un problema de eses tipo, utilizaría la tecnicque simula multitareas a basde de dedicar los ciclos pares a controlar el sensor analógico y los impares a todo lo demas. Ello obliga a no utilizar la instruccion «delay» y otras que alarguen en exceso el tiempo de cálculo de alguno de los dos tipos de ciclos, pues si se paraliza en exceso el bucle que la contiene, se pierde el «efecto multitarea».

      Tengo un ejemplo de como hacer esto en el post «Programar multitareas, controlar una intermitencia y simultáneamente vigilar la pulsación de un botón» https://felixmaocho.wordpress.com/2013/02/27/arduino-programar-multitareas-controlar-una-intermitencia-y-simultaneamente-vigilar-la-pulsacion-de-un-boton/
      donde resulevo como hacer algo similar a interrupciones para entradas digitales como un botón, pero igual se podría hacer para entradas analógicas como la temperatura de un termómetro o la frecuencia de un sonido.,

      Si le queda alguna duda de como aplicar esta técnica, hagamelo saler con un comentario y si por casualidad se la respuesta se la daré encantado. Suerte. .

      Comentarios por felixmaocho | 13 agosto 2013 | Responder

      • Muy interesante tu pos.
        Tengo una duda sobre un programa que estoy realizando.
        Cuabo esta el prograna en el (delay) justo en este espacio de tiempo quiero parar todo el programa como si fuera una parada de emergencia.
        Podrias pasarme un ejemplo de como programarlo.
        Llevo varios dias encallado en este punto y seria de mucha ayuda.
        Saludos…

        Comentarios por jaume | 28 agosto 2014

      • Creo que la solución de tu problema es el manejo de interrupciones. Las interrupciones son sucesos que pueden ocurrir en cualquier momento, pero no podemos prever cuando van a ocurrir. Por ejemplo, un sensor en el frente de un vehículo detecta a distancia los posibles obstáculos que encuentre en su camino y lo informe a la tarjeta cambiando el estado de un Pin. Cuando eso ocurra tomaremos las medidas que consideremos oportuno, como por ejemplo parar el vehículo, dar marcha atrás un poco y cambiar de dirección etc.

        El problema es que no sabemos en que momento va a ocurrir que el detector detecte el obstáculo, por lo que si la lectura del pin del sensor está en un punto determinado del loop de nuestro sketch y el loop tarda un tiempo apreciable en ejecutarse, porque tiene un «delay» por medio, podría pasar que en ese periodo de tiempo que dura el loop nos estrelláramos, antes de volver a pasar nuevamente por el punto de control de obstáculos.

        Por esa razon se utilizan interrupciones, que no son mas que partes de programa paralelas al loop que se ejecuta cuando se detecte la interrupción, este el loop en el punto que esté.

        Tengo un capítulo dedicado a interrupciones que creo que aclara mejor este concepto. Arduino – Manejo de interrupciones https://felixmaocho.wordpress.com/2013/08/05/arduino-manejo-de-interrupciones/ Ahí hay ejemplos de como programarlas.

        Un saludo

        Comentarios por felixmaocho | 28 agosto 2014

  2. Perdonen, encontré la página en Google vi luz y entré, quizás sirva mi comentario; una interrupción disparada por una señal analógica debe acondicionarse, por ejemplo. Se puede interrumpir en un cruce por cero, un mínimo valor, un máximo, cuando delta v sea de tal valor (variación de valor), y esto dispare la interrupción y se lea o no el valor analógico. Para esto necesitamos comparar siempre la señal analógica con un valor de referencia, fijo o variable dependiendo de nuestros requerimientos, pero arduino por si solo no es tan fácil, por no decir imposible.

    Comentarios por Marce | 4 septiembre 2014 | Responder

  3. Gracias por su aclaración, ya me parecía a mi, (sin saber mucho sobre el tema), que hace interrupciones con sensores analógicos me parecía dificii. Yo creo mas sencillo establecer un ci ciclo que corra rápido es dcir que no tenga «delays»· y controlarlo en el loop el valor del sensor que intentar hacerlo por interrupciones. Muchas gracias

    Comentarios por felixmaocho | 5 septiembre 2014 | Responder

  4. Hola. Tengo una duda: Es posible apagar el sistema directamente desde una interrupción?

    Comentarios por Cristian Javier Ruiz Alvarez | 3 abril 2015 | Responder

    • Por lo que yo sé, (que no es mucho), la función LOOP se repite mientras llegue electricidad a Arduino, por ello la única forma de apagarlo es desenchufar Arduino, lo mas que se puede es hacer como si no funcionara llevando el programa a un bucle que no haga nada, pero realmente Arduino sigue trabajando.

      Comentarios por felixmaocho | 3 abril 2015 | Responder

  5. Habrá algo como halt, en asm de algunos micros lo ponía en bajo consumo y detenía el programa, casi lo mismo que apagarlo, pero en arduino tenes el Usb funcionando, podes enviar una señal de apagado a un transistor que trabaje en saturación alimentando el arduino. En fin es cuestión de imaginación.

    Comentarios por Marce | 7 abril 2015 | Responder

    • Si claro, puedes utilizar un relé controlado por Arduino que actue como un interruptor de corriente que alimenta Arduino, o sea que desenchufe físicameente el circuito de ali,entacion de Arduino, con ello efectivamente paras Arduino definitivamente.
      Entonces el problema se te plantéa al reves, con el Arduino apagado, ¿cómo emites un mandato para reactivar la conexión?

      Comentarios por felixmaocho | 7 abril 2015 | Responder

  6. Hola que tal como vas…
    necesito hacer lo siguiente…
    accionar un codillo o selector (no pulsador) a diferencia de los pulsadores estos codiilos o selectores quedan accionados osea no vuelven a su estado normal como el pulsador. que quiero hacer? al accionar el pulsador se debe accionar un ventilador por 3 segundos luego se debe apagar e inmediatamente encenderse otro ventilador.
    OJO no puede estar los 2 al mismo tiempo!!
    aL MOMENTO DE VOLVER EL CODILLO A SU ESTADO ORIGINAL SE DEBE APAGAR TODO.

    Comentarios por Nicolas Aragon | 4 diciembre 2015 | Responder

    • Hola precisamente estoy preparando un post sobre ese punto, tenga un poco de paciencia, le anticipo solo que debe hacer tres circuitos uno para saber que el codillo esta presionado que se inicie en el pin 5V pase por el codillo tenga ua derivacion a un pin digital por ejemplo el 7 en INPUT y la otra derivacion acabe en tierra GMD, (montaje Pull Down), que le controle cuando está activo el codillo, y dos pines de salida por ejemplo el 11 y el 13 para poner en marcha los ventiladores y controlar todo por programa. Saludos

      Comentarios por felixmaocho | 4 diciembre 2015 | Responder


Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.