bclose

Arduino y las interrupciones

Gestionando excepciones

Objetivos

 

 

    • Presentar el concepto de Interrupcion en Arduino.
    • Conocer los tipos de interrupciones.
    • Mostrar los Flags de disparo.
    • Consideraciones generales.

 

Material requerido.

Imagen de Arduino UNOArduino UNO o equivalente.
BreadboardconexionesUna Protoboard más cables.
componenteUna resistencia de 330Ω.
boton contactorUn pulsador.

 

 

Las Interrupciones

 

Nunca he sabido muy bien que tiene esto de las interrupciones, que hacer temblar a programadores curtidos como si fueran unos novatos.

Recuerdo que en una época en que nos dedicábamos a los microprocesadores (cuando los dinosaurios dominaban la tierra) y comenzábamos a jugar con las interrupciones, había un porcentaje de técnicos, ingenieros electrónicos o informáticos, que aun  comprendiendo la idea de las interrupciones, parecía que su cerebro no podía abarcarlas y sencillamente las ignoraban elegantemente.

Mentarle las interrupciones a muchos Arduineros ya fogueados, en muchos casos supone que recordarán inmediatamente la necesidad de salir urgentemente a hacer algo. Nunca he sabido porque pasa esto, pero vamos a intentar ponerlo remedio inmediatamente.

¿Qué es una interrupción hardware?

A un nivel básico, una interrupción es una señal que interrumpe la actividad normal de nuestro microprocesador y salta a atenderla. Hay tres eventos que pueden disparar una interrupción:

 
  • Un evento hardware, previamente definido.
  • Un evento programado, o Timer
  • Una llamada por software.
 

Cuando un evento dispara una interrupción, la ejecución normal del micro se suspende (ordenadamente para poder volver) y salta a ejecutar una función especial que llamamos Interrupt Service Handler o ISH (Servicio de gestión de interrupción).

Cuando el ISH finaliza, el procesador vuelve tranquilamente al punto donde lo dejó y sigue con lo que estaba como si no hubiese pasado nada.

 
  •  El concepto de interrupción nace de la necesidad imperiosa de reaccionar de forma inmediata en respuesta a un acontecimiento electrónico fulgurante, que no admite demora. Bien sea por la urgencia del suceso o porque algo se podría perder de forma irrecuperable sino reaccionamos con suficiente presteza.

 

Pero ¿Qué hay tan urgente que no pueda esperar? ¿Es que nuestros Duinos no son lo suficientemente rápidos para ver cada poco si hay una señal de alarma? ¿Por qué complicarnos la vida con una cosa tan extravagante?

La respuesta como siempre es… depende. Nuestro Arduino puede estar liado y solo leerá la señal de un pin de tanto en tanto. Y si la señal que aparece se desvanece antes de que hayamos ido a ver, ni siquiera lo sabremos, porque aunque los Duinos son rápidos una señal electrónica lo es varios millones de veces más. Este es otro motivo por el que usar delays tiene mucho peligro.

 
  •  En la jerga técnica, a pasar de vez en cuando a ver como está el asunto, se le llama Polling.

 

Por otro lado las interrupciones nos ofrecen una ventaja enorme a la hora de organizar nuestro programa. Se define la función que se ejecutará al recibir una interrupción dada y se ejecuta limpiamente cuando ocurre, no hay que comprobar si se da o no una situación.

Simplemente te olvidas y se ejecutará única y exclusivamente cuando se alce la interrupción. No me digáis que no es elegante (SI, es una obsesión).

En realidad, nosotros funcionamos por interrupciones habitualmente, en respuesta a sucesos no previstos que nos sacan de la rutina habitual.

Imagínate que estás viendo tu serie favorita en la tele y estas esperando a tu colega, amigo o novia.

Hay dos maneras de abrirle la puerta. Una es pasar a ver si ha llegada cada, digamos dos minutos, para ver si esta con cara de pánfilo/pánfila en la puerta esperando a que le abramos.

La otra es establecer una interrupción, para eso están los timbres. Cuando tu compi llega, pulsa el timbre. Tu paras tu capitulo tranquilamente, dejas el refresco en la mesa y vas a abrirle.

Cuando vuelves con él, reanudas tu peli y recoges el refresco. ¿Qué tienen de raro las interrupciones? ¿Qué me decís del teléfono o de los Whatsapp? Es la misma idea. Y lo mismo pasa con tu Arduino.

¿Por qué voy a renunciar a las interrupciones y dedicarme a pasar por la puerta cada poco? Es absurdo. Las interrupciones no tienen nada de extraño ni de incognoscible. Dedícale un poco de tiempo y te encontrarás una herramienta magnifica que te resolverá limpiamente más de un problema.

 

Tipos de interrupciones

 

De los tres  sucesos que pueden disparar una interrupción

 
  • Un evento hardware,
  • Un evento programado, o Timer
  • Una llamada por software.
 

Nos encontramos con que Arduino no soporta las interrupciones por software y punto.

¿Y entonces porque hemos hablado de ellas? Pues, porque otros entornos de programación las aceptan y no será raro que en el futuro Arduino también.

Los eventos programados o Timers, son muy interesantes y tendrán una sesión monográfica propia en un futuro próximo. Pero por ahora vamos a meternos con las interrupciones disparadas por hardware.

 

Las interrupciones por hardware

 

Estas interrupciones hardware, se diseñaron por la necesidad de reaccionar a suficiente velocidad en tiempos inimaginablemente cortos a los que la electrónica trabaja habitualmente y a los que ni siquiera el software era capaz de reaccionar.

La idea que debéis  que tener en mente es que vamos a definir una función que se ejecutará de forma asíncrona, sin planificación, cuando se ocurra un cierto suceso electrónico.

Para definir una interrupción necesitamos tres cosas:

 
  • Un pin de Arduino que recibirá la señal de disparo
  • Una condición de disparo
  • Una función que se ejecutará, cuando se dispare la interrupción (Llamada call back function).
 

Lo primero es un pin de Arduino donde conectaremos el “timbre” de llamada. Dependiendo del modelo Arduino que tengamos, tendremos varias posibilidades:

Modelo ArduinoInt 0Int 1Int 2Int 3Int 4Int 5
UNOPin 2Pin 3
MEGA2321201918
DUETodos los pines del DUE pueden usarse para interrupciones.
Leonardo32017

 

Esto quiere decir que el Arduino UNO puede definir dos interrupciones hardware llamadas 0 y 1, conectadas a los pines 2 y 3 (Para que no sea fácil).

El Mega como es habitual en él, acepta nada menos que 6 interrupciones diferentes. Y el DUE, muy sobrado, exhibe su poderío.

En cuanto a la condición de disparo puede ser:

 
  • LOW, La interrupción se dispara cuando el pin es LOW.
  • CHANGE, Se dispara cuando pase de HIGH a LOW o viceversa.
  • RISING, Dispara en el flanco de subida (Cuando pasa de LOW a HIGH).
  • FALLING, Dispara en el flanco de bajada (Cuando pasa de HIGH a LOW).
  • Y una solo para el DUE: HIGH se dispara cuando el pin esta HIGH.
 

Si nuestra call back function se llama Funcion1 (),  para activar la interrupción usaremos:

attachInterrupt(interrupt, ISR, mode)

Donde Interrupt es el número de la interrupción, ISR será Funcion1 y mode es una de las condiciones que hemos visto arriba. Así en un Arduino UNO podría ser:

attachInterrupt(0, Funcion1, RISING) ;

Suponiendo que hemos enchufado la señal de interrupción al pin 2 de Arduino. Vamos a ver algún ejemplo de interrupciones.

 

Esquema de conexiones

 

Es una especie de costumbre en Arduino, usar un pulsador para ilustrar el concepto de Interrupción, así que nos plegáramos a ello. Vamos a utilizar un típico circuito para leer un pulsador con un pullup.

Esquema de conexión

 

Hasta ahora habríamos escrito el programa para leerlo así,

void setup()
   {   pinMode(2, INPUT);
       Serial.begin(9600);
   }

void loop()
   {   bool p = digitalRead(2);
       Serial.println(p);
   }

El resultado seria normalmente 1, por el pull up y la lectura bajaría a 0, al pulsar el botón. Nada nuevo en esto.

Pero vamos a reescribir el programa para  establecer una interrupción en el pin 2 (Interrupción 0) .El programa quedara más o menos así:

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++ ;
   }

En primer lugar fijaros que hemos eliminado la definición de entrada del pin 2, porque no vamos a usarlo como input estrictamente. Con definir la interrupción es suficiente.

En segundo lugar usamos attachInterrupt() pasándole como parámetros la interrupción 0, que es el pin2 de Arduino UNO. SI fuese la Interrupción 1, la conectaríamos al pin 3 (Anda que…)

Le pasamos el nombre de la función CallBack ServicioBoton, que es de lo más sencilla. Un variable global contador, guarda el número de pulsaciones. Lo único que hace la función de servicio es aumentar contador en uno cada vez que se pulsa y volver.

Y por último el trigger es FALLING porque el estado es normalmente HIGH y baja a LOW al pulsar el botón,  utilizaremos el disparo con el flanco de bajada, o sea FALLING o LOW.

 
  • Un disparador en inglés es trigger y es el nombre que encontrareis en la jerga técnica.

 

El loop comprueba si el número de pulsaciones ha cambiado y si es así lo imprime, pero puede dedicarse a hacer cualquier cosa, porque no vamos a perder ninguna pulsación.

Os puede parecer una manera extravagante de hacer las cosas pero no me digáis que no es elegante. De hecho todos los lenguajes modernos de alto nivel para Windows, Mac o Linux utilizan la programación por eventos que es algo básicamente igual a esto (Salvando las distancias claro).

Cuando veamos la salida en la consola tendremos una sorpresa esperada:

Botón por interrupción

Cuando pulsamos el botón, el número que aparece no aumenta de uno en uno si no a golpes. ¿Por qué?

Pues como ya vimos en su día, se debe a los rebotes del pulsador. Decíamos en la sesión “Condicionales y botones”, que para eliminar el rebote de los botones, tenemos que hacer el debouncing y allí lo hacíamos con un delay de 250 ms.

Pero vamos a tener un problema. No podemos usar un delay dentro de una interrupción. No funciona. ¿Cómo dice?

Hay varias consideraciones a tener en cuenta con las interrupciones:

 
  • Haz lo que quieras pero no te demores. Acaba cuanto antes y lárgate.
  • Hay cosas que no funcionan, como las funciones delay (), millis () y cualquier cosa que dependa de interrupciones o timers.
  • Ni se te ocurra meter un Serial.Nada en una interrupción, son muy lentos (Y además tampoco funcionan porque también dependen de interrupciones).
  • Debes entender que una interrupción es como un estado de excepción, que se puede usar sin reparos, pero entendiendo que hay que hacer el trabajo y salir cuanto antes.
  • De hecho, una ISR o función CallBack, no puede devolver parámetros ni tampoco recibirlos
 

Además cuando definimos una variable global como contador, de la que depende una función ISR, se recomienda definirla como volatile y no como una global normal.

 
  • Estrictamente hablando, volatile no es un tipo de variable, sino una directiva para el compilador.
  • Eso significa que la variable en cuestión, debe ser almacenado de un cierto modo que evite algunos problemas raros que pueden surgir cuando una variable puede ser cambiada por el ISR o por el programa (algo que no ocurre en nuestro ejemplo).
  • Bajo ciertas circunstancias puede surgir un conflicto y volatile lo evita y por eso se recomineda hacerlo así siempre.
 

Si necesitas un retraso para algo, o sea , un delay, siempre es mejor, comprobar el tiempo transcurrido y decidir si se toma la acción o no. Veamos otro ejemplo

volatile int contador = 0;   // Somos de lo mas obedientes
int n = contador ;
long T0 = 0 ;  // Variable global para tiempo

void setup()
   {    pinMode(2, INPUT);
        Serial.begin(9600); 
        attachInterrupt( 0, ServicioBoton, LOW);
   } 
void loop()
   {   if (n != contador)
           {   Serial.println(contador);
               n = contador ;
           }
   }
void ServicioBoton()
   {
       if ( millis() > T0  + 250)
          {   contador++ ;
              T0 = millis();
          }
    }

En primer lugar definimos contador como volátil, por prescripción médica, y definimos otra variable global T0 para almacenar el tiempo a partir del cual contaremos.

En la ISR la diferencia es que comprobamos si el valor actúan de millis es mayor en 250 ms a la última vez que paso por la interrupción. Si no es así, lo consideramos un rebote y lo ignoramos olímpicamente. Por el contrario si ha pasado un tiempo prudencial incrementamos contador.

La ventaja d este sistema es que no congelamos el procesador con un delay, si no que le dejamos seguir con su trabajo, atendiendo otras interrupciones, por ejemplo.

Pero….Un momento… No habíamos dicho que millis() no funciona en las interrupciones.

Así es. Mientras una interrupción esta activa millis está congelada y su valor no cambiará, pero sigue pudiéndose leer.

 
  • Mientras estas dentro de una interrupción, todas las demás interrupciones, son ignoradas, por eso, nada que dependa de otras interrupciones funciona.
  • Por eso es importante salir pronto, para garantizar que no nos perdemos nada de interés.
  • Mientras una interrupción está activa, millis() y micros()se congelan. Eso quiere decir que si tienes unos cuantos miles de interrupciones por segundo (Como si estas midiendo la frecuencia de una onda de audio) tu medida de tiempo con millis o micros se puede ver distorsionada.
 

Por ultimo os conviene saber que existen algunas otras instrucciones relativas a las interrupciones:

 
  • noInterrupts(), Desactiva la ejecución de interrupciones hasta nueva orden.
  • Interrupts(), Reinicia las interrupciones definidas con attachInterrupt().
  • detachInterrupt( num Interrupt), Anula la interrupción indicada.
 

 

Resumen de la sesión

 

 

    • Hemos conocido el motivo y el concepto de las interrupciones.
    • Hemos empezado por estudiar las interrupciones hardware, dejando los Timers para futuras sesiones.
    • Vimos que en Arduino UNO solo disponemos de 2 interrupciones llamadas 0 y 1 en los pines 2 y 3 respectivamente.
    • Vimos los posibles disparos de una interrupción por flanco de subida, bajado o LOW.

 

 

 

(107) Comments

  • Hola, Te queria preguntar si de casualidad no tienes un tutorial de RTOS en arduino.

    • Hola Andres, no tengo nada porque hasta la fecha no he dedicado tiempo al tema, pero reconozco que es de lo mas interesante

    • Sergio

    Hola buenas, ¿qué pines se pueden usar para interrupciones en el Nano?
    Muchas gracias.

    • Holoa Sergio, el nano usa los pines 2 y 3 para las interrupciones. Un saludo.

    • Buena tarde.
      Excelente tutorial. Gracias por compartir.
      Se me presenta el siguiente inconveniente al contar los pulsos de un pulsador por interrupciones. He usado falling como parámetro para la interrupción. En Setup, defino const int boton = 2; , y luego activo las pull up internas mediante pinMode(boton, INPUT_PULLUP); coloco el tiempo de enmascaramiento dentro de la interrupción en 250 ms, y he ensayado con valores desde 100 hasta 1200 ms.
      Cuando oprimo el botón y lo suelto rápidamente efectivamente cuenta y retorna al programa normal. Pero cuando lo mantengo oprimido algún tiempo (tal vez mas de un segundo), vuelve a contar al soltarlo duplicando la cuenta y presentando números errados.
      ¿Me podeis dar alguna luz de por que ocurre esta situación?
      Gracias de antemano…

      • Hola Giovanny, en primer lugar no debes hacer delay dentro de las interrupciones porque simplemente no funcionan como esperas. No use delays dentro de las rutinas de servicio de las interrupciones vale.

        En cuanto al tema e que cuenta de mas, te recomiendo hacer algunas pruebas con disparo por Falling y rising porque a veces puedes tener una idea equivocada de lo que pasa (Nos pasa a todos) pero despues de hacer pruebas de este estilo puede que veas algo de luz ¿Vale?

        Pruebalo y me dices

  • Muchas gracias por tu rápida respuesta!!, (por cierto, era la primera vez que participaba en un foro)
    Me refiero al programa incluido como ejemplo en el IDE de arduino, dentro de wire, el programa slave_receiver, de las comunicaciones i2c.
    Copio el “receiveEvent” de este programa:
    void receiveEvent(int howMany) {
    while (1 < Wire.available()) { // loop through all but the last
    char c = Wire.read(); // receive byte as a character
    Serial.print(c); // print the character
    }
    int x = Wire.read(); // receive byte as an integer
    Serial.println(x); // print the integer
    }
    Entiendo que lo que hace el esclavo es una interrupción de su loop cuando recibe la orden del maestro, y que esa interrupción no debería utilizar el "serial.print" y, por lo que he leído, no conviene declarar variables (en este caso se declara "char c" e "int x"). El serial.print dentro de la interrupción podría provocar congelaciones aleatorias??
    Me gustaría saber que se puede hacer y que no en la interrupción.
    Y yendo un poco mas lejos (aunque esto no sería un comentario sobre interrupciones sino sobre i2c) como puedo evitar que se solapen interrupciones, he visto en tu sesion sobre time que es algo que no debería ocurrir, pero como eviarlo en las interrupciones de las comunicaciones i2c?. Concretamente el maestro manda una orden al esclavo, el esclavo la escucha y abandona lo que está haciendo para hacer la interrupción correspondiente pero… el maestro sigue trabajando ¿no? puede ocurrir que el maestro de una nueva orden sin que el esclavo haya terminado de ejecutar la anterior. ¿que se puede hacer para que el maestro sepa que el esclavo esta ocupado en una orden y que espere antes de enviar la siguiente?.
    Lo dicho, muchas gracias y FELIZ AÑO!!

    • Victor

    Hola muchas gracias por el tutorial, está muy bien escrito, es muy didáctico lo podemos entender también los que sabemos poco de esto.
    Tengo algunas dudas sobre lo que se puede hacer y lo que no dentro de las interrupciones. Concretamente:
    1. declarar variables? (el ejemplo incluido en el IDE slave.receiver lo hace con int y char, además ¿¿no estaba recomendado volatile byte??)
    2. serial.print? idem comentario del ejemplo del slave receiver, lo hace dentro de interrupción
    3. delay()
    4. otras???
    En el programa que estoy intentando hacer de vez en cuando el programa se cuelga y lo tiene que reanimar un watchdog. Parece que ocurre en la comunicaciones entre maestro-esclavo, concretamente PERO SOLO algunas veces, cuando el maestro manda ordenes marcha paro al esclavo, el problema es que no ocurre siempre, solo de vez en cuando en el mismo punto. Desde mi inexperiencia tras revisar los foros estoy intentando “limpiar” todo lo posible lo que hace el esclavo, pero encuentro contradicciones entre las recomendaciones y los ejemplos del IDE.
    MUCHAS GRACIAS

    • Buenos dias Victor,
      Vamos por partes, Puedes declarar las variables que neesites dentro de una funcion de servicio de interrupcion, pero se perderan para la proxima vez que vuelvas salvo que las declares volatile, pero no es imprescindible.

      No estoy seguro de que programa es el que me mencionas y pero la idea que tengo es que dentro de una interrupcion no funcionan ni el delay ni las funciones serial ya que ambas dependen a su vez de otras interrupciones por lo que aun cuando puediesen funcionar (Que creo que no) no seria interesante de usar

  • Hola buenas, estoy empezando con arduino y tengo el siguiente problema:
    tengo un contador en la interrupción similar al del ejemplo, según el valor del contador se ejecuta una secuencia de leds u otra.
    Sólo incremento el contador en la interrupción, pero cuando cada una de las secuencias lleva unos cuantos ciclos el valor de contador aumenta solo, sin pulsar el pulsador de la interrupción.
    Un saludo y muchas gracias de antemano.

    • Hombre, habria que ver el programa y como disparas la interrupcion para darte una opinion

  • Hola… me ha gustado la explicación y es justo lo que estoy buscando… tengo un problema a la hora de usar el “attachInterrupt” y es que estoy usando un kit para impresoras 3D (arduino mega 2560 R3, ramps 1.4 y full graphic smart controller) y estoy intentando controlar el encoder que viene con la pantalla(full graphic smart controller), ya he comprobado que los pines del encoder sean los correctos (CLK->33, DT->31, SW->35), tengo el siguiente código pero cuando muevo el encoder no se ejecuta la función de la interrupción.
    He probado más códigos usando “attachInterrupt” o “attachPinChangeInterrupt” y más simples pero igual no me funciona. Alguien podría ayudarme?

    #include
    #include “U8glib.h”
    #include
    #include

    U8GLIB_ST7920_128X64_1X u8g(23, 17, 16); //SPI Com: SCK = en = 18, MOSI = rw = 16, CS = di = 17

    int encoderPinA = 33;
    int encoderPinB = 31;
    int encoderBttn = 35;

    volatile int lastEncoded = 0;
    volatile long encoderValue = 0;

    long lastencoderValue = 0;

    int lastMSB = 0;
    int lastLSB = 0;

    volatile int pos = 0;
    int nA = LOW;
    int nB = LOW;
    void setup() {
    pinMode (encoderPinA,INPUT);
    pinMode (encoderPinB,INPUT);
    pinMode (encoderBttn,INPUT);
    Serial.begin (9600);
    digitalWrite(encoderPinA, HIGH); //turn pullup resistor on
    digitalWrite(encoderPinB, HIGH); //turn pullup resistor on
    digitalWrite(encoderBttn, HIGH); //turn pullup bttn on

    //call valorPos() when any high/low changed seen
    //on interrupt 0 (pin 33), or interrupt 1 (pin 31)
    PCintPort::attachInterrupt(encoderPinA, valorPos, CHANGE);
    PCintPort::attachInterrupt(encoderPinB, valorPos, CHANGE);

    }

    void loop() {
    if(digitalRead(encoderBttn)){
    //button is not being pushed
    }else{
    Serial.println(“bttn ON”);
    }

    Serial.println(pos);
    delay(500);
    nA = digitalRead(encoderPinA);
    nB = digitalRead(encoderPinB);

    while((nA==1)&&(nB==1)){
    //Serial.print (“pinA: “);
    //Serial.println (nA);
    //Serial.print (“pinB: “);
    //Serial.println (nB);
    Serial.println(pos);
    nA = digitalRead(encoderPinA);
    nB = digitalRead(encoderPinB);
    };
    }

    void valorPos(){
    Serial.println(“dentro del metodo”);
    nA = digitalRead(encoderPinA);
    nB = digitalRead(encoderPinB);
    if(nA==0){
    pos–;
    }
    if(nB==0){
    pos++;
    }
    }

    • Hola Tony, es imposible probar tu codigo, pero puedes empezar por algo mas sencillo como comprobar con un programa que solo controla el encoder, que la interrupciion salta. No hay muchas opciones de que no salte y si aun asi no salta suele ser:
      No has anexado correctamente la calback function
      el pin al que conects la salida no acepta interrupciones (No todos lo aceptan)
      La condicion de disparo no se alcanza nunca por cualquier razon

      • Hola, o sea que en el arduino mega2560 no todos los pines pueden hacer uso de la librería PinChangeInt.h?? El mega tiene 53 pines de los cuales los que van conectados al encoder de la pantalla son el 31,33 y 35 (pines digitales); ya probe otro código (el de abajo) pero igual no me funciona son ninguno de los pines que estan conectados al encoder; no sé si tenga algo que ver con lo que encontré sobre los pines del mega : Interrupt External ->2 3 and 18-21 ; Interrupt Pin Change -> 10-15 and A8-A15 and SS, SCK, MOSI, MISO.

        Éste código es de los ejemplos de la librería y lo usé pero no pasaba nada cuando movía el encoder o pulsaba el encoder (usando: #define ARDUINOPIN 35 )

        #include

        // Modify this at your leisure.
        #define ARDUINOPIN 33

        // Notice that values that get modified inside an interrupt, that I wish to access
        // outside the interrupt, are marked “volatile”. It tells the compiler not to optimize
        // the variable.
        volatile uint16_t interruptCount=0; // The count will go back to 0 after hitting 65535.

        // Do not use any Serial.print() in interrupt subroutines. Serial.print() uses interrupts,
        // and by default interrupts are off in interrupt subroutines. Interrupt routines should also
        // be as fast as possible. Here we just increment a counter.
        void interruptFunction() {
        interruptCount++;
        }

        // Attach the interrupt in setup()
        void setup() {
        pinMode(ARDUINOPIN, INPUT_PULLUP); // Configure the pin as an input, and turn on the pullup resistor.
        // See http://arduino.cc/en/Tutorial/DigitalPins
        attachPinChangeInterrupt(ARDUINOPIN, interruptFunction, FALLING);
        Serial.begin(115200);
        Serial.println(“—————————————“);
        }

        // In the loop, we just check to see where the interrupt count is at. The value gets updated by the
        // interrupt routine.
        void loop() {
        delay(1000); // Every second,
        Serial.print(“Pin was interrupted: “);
        Serial.print(interruptCount, DEC); // print the interrupt count.
        Serial.println(” times so far.”);
        }

        • Hola Tony,

          Segun el manuela en el mega puedes usar los siguientes pine spara enlazar interrupciones

          Mega, Mega2560, MegaADK 2, 3, 18, 19, 20, 21

          Por lo que veo tu has definido ARduinopin para la interrupcion , como el pin 33 pero ese pin no reconoce interrupcione spor lo que no va a funcionar. Tienes que elegir uno de la lista

          • Ya, es que los firmware(que se usan para las impresoras 3D con este kit) usan esos pines en sus códigos, por eso mi duda… no he encontrado cómo hacen para que el encoder les funcione usando esos pines.

          • Imagen de perfil de admin

            Hola Tony, poner en marcha una interrupcion es de lo mas sencillo. SImplemente concta la salida del encoder al pin 2 0 3 de arduino (Los que aceptan la interrupcion) despues crea una funcion de servicio que incremente en 1 un contador cada vez que se dispare la interrupcion y eso es todo

            Desde l programa proncipal solo tienes que ver cuantas señales ha contado el encoder

    • Emilio Machado

    Muchísimas gracias por este tutorial!!!, excelente y bien sincero.
    Yo estoy luchando con interrups entre el MPU 6050 y pro mini 3,3. La rutina es sleep -wake-up con zeromotion interrups.
    Todo funciona bien, le di en la tecla con duración y thresfold de forma que cuando hay actividad del acc esta despierto y cuando no duerme.
    El problema que tengo es que cuando reseteo el arduino o lo reescribo el interrups funciona a revés, o sea que cuando tiene que estar activo descanasa y cuando debe estar descansando está activo.
    Esto pasa erraticamente lo reseteo nuevamente y funciona como debe.
    La función del interrups es:
    void doInt()
    {
    //———-ESTE FUNCIONA——
    motionDetected=!motionDetected;
    //—————————–
    }
    el void loop>
    algo antes
    Wire.beginTransmission(MPU);
    Wire.write(0x43);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU,4,true); //A diferencia del Acelerometro, solo se piden 4 registros
    GyX=Wire.read()<<8|Wire.read();
    GyY=Wire.read()< 20){
    if (montaa==0){
    StartTime=millis();
    etc,etc,etc
    }
    Le saqué la orden de ir a dormir hasta que solucione este problema
    desde ya much’isimas gracias nuevamente y 1000 disculpas por la molestia

  • Hola Charly,

    Es la primera vez que escribo. Primero mis impresiones y luego quiero comentar algo sobre mis resultados con las interrupciones y preguntaros si estoy en lo cierto.

    Me ha impresionado bastante el enfoque tan didáctico y ameno de este curso. Tengo (tenía) experiencia de programación y me quedé en Pascal, sin apenas programar en C. Calcula mi edad… He hecho aplicaciones y utilidades de sistemas para clientes y he programado PICs como hobby en código máquina, así que sé de sobra lo que es programar. Pero tenía un tema pendiente que era entender el porqué de la famosa programación orientada a objetos. Con tus breves pero clarificadoras lecciones lo he entendido a la perfección. !Muchas Gracias!

    Estoy siguiendo con todo detalle este curso y quisiera comentar una curiosidad que he observado con el tema de las interrupciones y los rebotes del pulsador.

    He realizado un montaje con vuestro KIT del curso, con 8 LEDs en secuencia de ir y venir, tipo coche fantástico. Mas un pulsador que cada vez que lo pulsas cambia alternativamente el delay para que el “movimiento” de luces sea más deprisa o más despacio.

    He seguido el código para evitar los rebotes de esta lección, pero tenia curiosidad por saber el número de rebotes y añadí un contador adicional de rebotes que siempre se incrementa en la rutina de interrupción, sin pasar por el “if”. Pues bien, cada vez que pulso lo más rápido que puedo el pulsador, el contador bueno se incrementa 1, como debe ser, pero el otro contador de rebotes, que cuenta las veces que se dispara la interrupción al pulsar el botón, se incrementa cada vez no unas cuantas veces sino del orden de 5.000 o 10.000 veces (cinco mil a diez mil veces). Al tercer o cuarto pulsado del botón, el contador de rebotes entero ya se ha desbordado y aparece negativo.

    No me podía imaginar que la rutina de interrupción entrase tantas miles de veces, con un simple rebote de pulsador. Lo que imaginé era que como la condición de interrupción programada era “LOW”, la interrupción se disparaba continuamente mientras el pin estuviese LOW, es decir el botón pulsado. Al cambiar la condición a “FALLING”, entonces el comportamiento es lo que yo esperaba. El numero de rebotes es uno o dos, como máximo, o incluso ninguno, pulsando con firmeza y/o delicadeza.

    Entonces parece ser que con la condición “LOW”, mientras se mantenga el pin LOW, se dispara continuamente la interrupción. Habría que programar algún código en la rutina de interrupción para deshabilitar esta interrupción, mientras se ejecuta la ISR. Y luego al final de la rutina de interrupción volver a habilitar la interrupción para estar preparado para la siguiente.

    Creo recordar que los PICs que programé hace años eso era automático, pero parece que con el PIC de este arduino no es así y mientras se mantenga la condición LOW, se dispara una y otra vez la interrupción a la máxima velocidad que permite el chip. ¿Estoy en lo cierto? ¿Podéis comentarlo si sabéis sobre este tema?

    En fin, Muchísimas gracias por este curso que me está haciendo disfrutar de nuevo después de tantos años…

    Un Fuerte Abrazo,

    Alberto

    • Hola ALberto, no recuerdo haber hecho la prueba que mencionas con las interrupciones pero por lo que comentas el LOW dispara la interrupcion de forma continua. Personalmente tengo la costumbre de usar la condicion de Rising o falling desde hace mucho tiempo para evitar este tipo de problemas. UN saludo y bienvenido al club

      • James

      Hola Alberto,
      Al igual que en los PICs (CCS por ejemplo), Arduino también posee funciones para habilitar y deshabilitar las interrupciones si así lo necesitamos:
      interrupts() y noInterrupts()

      Más info: https://www.arduino.cc/en/Reference/NoInterrupts

  • unsigned long start = 0;
    const int threshold = 400;
    const int analogPin = A0;
    int led = 6;

    void setup() {

    start = millis(); // se cargas a la variable start con el valor de millis(), o sea un tiempo t1 en mseg
    pinMode (led, OUTPUT);
    Serial.begin(9600);

    }

    void loop() {
    while (millis() – start threshold)
    digitalWrite(led, HIGH);
    else
    digitalWrite(led, LOW);
    // print the analog value:
    Serial.println(analogValue);

    } // fin del while
    }

  • HOLA A TODOS ,
    Tengo este codigo y quisiera agregarle un boton para que me pueda interrumpir el encendido de un led antes del tiempo establecido,
    la funcion del codigo es que utilizo un potenciometro, la cual me activa un led al llegar a (threshold = 400) al girar el potenciometro y llega al valor el
    led se me enciende , mientras no presione el boton el led debera enceder al llegar al valor , ahora si presiono el boton antes del tiempo 15seg el led no se active , importante es que se debe ejecutar una sola vez mientra el arduino este energizado . si reseteo o des energizo el arduino se debe ejecutar el codigo.

    unsigned long start = 0;
    const int threshold = 400;
    const int analogPin = A0;
    int led = 6;

    void setup() {

    start = millis(); // se cargas a la variable start con el valor de millis(), o sea un tiempo t1 en mseg
    pinMode (led, OUTPUT);
    Serial.begin(9600);

    }

    void loop() {
    while (millis() – start threshold)
    digitalWrite(led, HIGH);
    else
    digitalWrite(led, LOW);
    // print the analog value:
    Serial.println(analogValue);

    } // fin del while
    }

    • hola greber,sera mas facil que alguien te ayude si lo posteas en el foro

  • Buen dia
    Estoy desarrollando un proyecto de toma de datos por i2c, mi interrupcion es por el pin 3 del arduino uno, al momento que necesito desactivar la interrupcion para leer los datos deshabilito la interrupcion. despues de haber hecho la lectura de los datos necesito reiniciar la interrupción, pero aqui es donde tengo el problema, no me quiere reiniciar la interrupción.
    void loop()
    If (con==3){
    detachInterrupt(digitalPinToInterrupt(interruptPin));
    lectura de datos;
    interrupts();
    }
    Muchas gracias por la ayudita.

    • Andres
      Estas usando detach por lo que no vas a poder rehabilitarla sin un usar un nuevo attach

      Creo que lo que intentas hacer es bloquear las interrupcioines sin deshabiltarlas, usa nointerrupts y entonces podras usar interrupts()

    • Andres

    noInterrupts(),
    Interrupts(),
    detachInterrupt
    Buenas noches
    alguien que me expliqué la forma correcta de usar estos tres comandos
    muchas gracias

    • Con no Interrupts() lo que haces es desactivar todas las interrupciones que estén definidas con un attachInterrupt(), y para volver a activarlas utilizas la función Interrupts(), sin pasarles ningún argumento.

      Si lo que quieres es anular sólo una interrupción utilizarías la función detachInterrupt( num Interrupt), pasándole como parámetro el número de la interrupción, no el pin al que está asociada.

    • Hablo de memoria pero creo que es mas o menos asi:
      Nointerrups. –> anula todas las interrupciones
      Interrupts –> habilita las interrupciones. Es lo contrario del anterior
      Detachinterrupt –> inhabilita solo la interrupcion indicada pero no las demas

    • Jesus

    Hola.

    La verdad muchas gracias por tus tutoriales, tu web es un referente cuando buscas empezar con arduino y también cuando quieres dominarlo, en serio gracias.

    Ahora que sé que existen interrupciones te quería hacer una consulta:
    si tengo un arduino conectado por comunicación serie a otro dispositivo o a un moden (con un adaptador de red) y otro dispositivo iniciará una comunicación con el arduino ¿cómo me aseguro que el arduino recibirá dicha señal? ¿Usando interrupciones (se supone que no porque leer un serial demora mucho tiempo) o simplemente diseñando señales de comunicación para que ambos dispositivos al comunicarse se avisen que lo están e intercambiar la información? ¿Tienes algún tutorial en donde hables sobre diseñar un sistema ordenado de comunicación serial entre arduino y otro dispositivo?

    Lo pregunto porque mi idea es que arduino se conecte con otro dispositivo (computadora, raspberry, otro arduino) y que ambos dispositivos puedan emitir mensajes así como recibirlos de forma ordenada, lo único que se me ocurre para esto es poner los métodos de leerSerial() y enviarMensaje() seguidos uno del otro pero ¿qué sucede si caundo envío el primer mensaje para que el otro dispositivo me responda y este responde arduino está de nuevo en enviarMensaje()? Si arduino solo funcionara como servidor no habría problemas pero la idea es que también pueda enviar mensajes.

    Saludos.

    • Hola Jesus,
      Normalmernte todos los protocolos de comunicacion se basan en interrupciones d emodo que efectivamente se garantice que no perdemos ningun mensaje y los programas de bajo nivel son los que gestionan este tipo de problemas. EN el caso de la comunicacion serie que es lenta no deberia ser nada complicado hacerlo tu, pero siempre es mas comodo tirar de las librerias hechas que ademas se suelen apoyar (Como en el caso de las Comm de arduino) en interrupciones hardware de bajo nivel

      Para una comunicacion serie entre tu arduino y porejemplo un PC o Rasberry, usaria directamente la conexion USB y tendras un monton de capas de drives de bajo nivel asegurandose de que van recogiendo en un buffer lo que va entrando

      Lo unico que tienes que hacer es no demorarte demasiado con delays y demas y realizar el loop con cierta soltura ¿Valeee?

    • Tulio

    Buen día!

    Tengo un problema con un proyecto que me encuentro desarrollando pero aqui es mi problema:

    Una vez que activo mi señal de entrada,por medio de un potenciometro condiciono un tiempo de trabajo entre 100 ms y 1500 ms. El detalle que necesito que aunque la entrada siga en estado alto siga corriendo el tiempo de trabajo y al terminar el estado cambie a LOW. :smiley-confuse:

    Alguien que pueda aconsejarme que instruccion usar…a continuacion dejo mi codigo donde

    SCI es la entrada!
    CO es la salida!
    offtime es el mapeo de la entrada analógica

    [code]

    const int pinpot = 0; //A0
    const int LCO = 9;
    const int SCI = 12;
    int SCI = 0;
    const int CO = 11;
    int timewait = 0;  
    int tw = 0;  
    int p1;// Aux SCI
    int p2; //Aux C0

    unsigned long previousMillis = 0;        
    long OnTimeMin = 100;          
    long OnTime = 0;
    long OnTimeMax =1500;
    long OffTime = LOW;

    void setup() {
     // Incializa el pin del boton como entrada
     pinMode(pinpot, INPUT);  
     pinMode(SCI,INPUT);
     // Incializa el pin del led piloto y  salida
     pinMode(LCO, OUTPUT);
     pinMode(CO, OUTPUT);
     // Inicializa la comunicacion serial
     Serial.begin(9600);

    }

    void loop(){
     
     //Lee boton
     p1=digitalRead(SCI);
     //  Lee el valor del pot
     tw = analogRead(pinpot);
     //  Transforma  este valor en el valor que usaremos para el led
     //  Para ello usaremos la funcion map()
     OffTime = map(tw, 0, 1023, OnTimeMin, OnTimeMax);
     //  Utilizamos este valor para iluminar el led
     
     
      // check to see if it's time to change the state of the LED
     unsigned long currentMillis = millis();

     if((p1 == HIGH) && (currentMillis - previousMillis >= OnTime))
     {
       (CO , HIGH);  // Turn it off
       previousMillis = currentMillis;  
       digitalWrite(CO, HIGH);
     }
     else if ((p1 == LOW) && (currentMillis - previousMillis >= OffTime))
     {
         
       previousMillis = currentMillis;  
       digitalWrite(CO, LOW);  
     }
    }

    • Ed

    Vale Juanma!, muchas gracias!, ya lo revisaré, la pregunta era básicamente como controlar los dos bits del rotary(A y B) que te dan los 4 estados posibles con los que se calcula si se incrementa o se decrementa. Pero al estar esos dos pins asociados a la interrupción y no poder usarlos como valores ordinarios de entrada, tenía duda de como hacerlo, pero creo que será como dices tu.

    Muchas gracias de nuevo!

    • Ed

    Hola Juanma, muy chulo el post, estoy empezando en arduino y me ha gustado mucho, me has enganchado para leer el resto de entradas!
    Tengo una duda conceptual que querría aclarar, estaba mirando información de las interrupciones para un rotary encoder. la pregunta es, si pongo interrupciones a los dos pins que leen el estado A y B, como hago para leer sus valores cuando llamo al callback si los pines stán asociados a las interrupciones, de forma que no he podido definir variables para recuperarlos?

    Muchas gracias, un abrazo!

    • Hola Ed no entiendo bien la pregunta, pero veamos. Un rotary encoder te envia manda una señal cuadrada cada vez que la marca de control pasa por delante de ello. Puedes programar la interrupcion para que se dispare en el flanco de subida o de bajada, con lo que tendras una señal clara en cada pulso y eso lo usar para lanzar una funcion que incrementa el contador, eso es todo.
      Luego puedes leer el contador desde tu programa tranquilamente

    • Momelius

    Solo una cosa… estoy aprendiendo la leche.. y por otro lado… que asco me dais la gente como tú, es decir, gente que puede hacer cosas que yo desearía hacer (pero es en el sentido total y absolutamente egoísta y de respeto, espero que se entienda)

    • Hola Momelius, jajaja tranquilo que nos lo tomamos en el mejor sentido y te agradecemos el cariño que expresa.

      Me gustaría decirte, para que te animes que no haya qui nada que no puedas aprender tu también y basta con ganas y algo que se suele olvidar pero que es imprescindible, tiempo y meter horas.

      Recuerda que un experto, suele ser alguien que se ha leído el manual antes que tu.

Give a Reply

WordPress Anti-Spam by WP-SpamShield