Manejo de retardos con Interrupciones en Arduino

Standard

Que tal amigos lectores, me da gusto redactar nuevamente. Esta vez traigo ante ustedes algo muy breve, pero sin duda muy práctico. ¿Alguna vez han manejado Interrupciones en programación?, ¿Les parecen un pequeño dolor de cabeza?, ¿Alguna vez han tratado de evadirlas y al final terminan dándose cuenta que son realmente necesarias?. Si alguna de tus respuestas fue un si, tal vez esta información te pueda ayudar mucho. Si no tienes idea de lo que son las interrupciones te recomiendo dar un vistazo en cualquier sitio web de programación y podrás empaparte perfectamente de este concepto.

Antes de continuar quiero aclarar que no profundizaré mucho con la teoría y me brincaré directamente a la práctica, ya que, lo que te voy a compartir, me permitió solucionar un conflicto físico aprovechando la flexibilidad que nos permite el desarrollo de software. En concreto necesitaba leer la señal del encoder de un motor de CD, sin embargo los resultados obtenidos eran un tanto desconcertantes e incluso llegué a percatarme que mientras el motor se encontraba completamente detenido en ocasiones obtenía lecturas parásitas que por supuesto, me indicaron que la señal recibida no era muy “limpia”.

Esto es realmente un problema cuando manejamos interrupciones por hardware, las cuales son las utilizadas en la plataforma Arduino, ya que, dependiendo del modo de disparo, éstas se pueden activar. Para conocer mas detalles sobre los modos de disparo de las interrupciones en Arduino, te invito a leer esta documentación en su sitio oficial. ¿Cómo comprobé realmente mi teoría en relación a la señal? Afortunadamente en ese momento tuve acceso a un osciloscopio y claramente se pudo apreciar la distorsión en la señal que se supone debería ser cuadarada. Aunque no me preocupé por guardar las imágenes en ese momento, la señal era muy parecida a la les muestro en la siguiente imagen:

ruido

Para la aplicación del motor de CD con encoder, la lógica del programa consta en disparar la interrupción cada que exista un cambio en la señal. Entonces por supuesto que todos los disturbios originados en la señal iban a disparar la interrupción, generando lecturas poco confiables. A este efecto se le conoce como “rebote” o “bouncing” y la alternativa más recomendable cuando trabajas con esta plataforma es diseñar e implementar un filtro utilizando un poco más de hardware. Sin embargo si te encuentras imposibilitado de aplicar este recurso (como a mí me pasó), se puede utilizar un “retardo” para que cuando se dispare por primera vez la interrupción, esta se ejecute y se ignore parte de la señal ruidosa hasta que realmente cambie de estado. Es una especie de filtrado de señal digital por así decirlo. Dicho retardo se tiene que calcular en función de la frecuencia de la señal que se desea leer.

Por ejemplo, supongamos que dicho encoder debería arrojar una señal cuadrada limpia de 60Hz, es decir que cada 0.01666 segundos o 16.66 milisegundos existe un cambio de estado. Entonces para este ejemplo el retardo que necesitamos es de 16.66 milisegundos para que se ejecute nuevamente la interrupción. MOMENTO, cuando estamos ejecutando un interrupción TODOS LOS TIMERS NO FUNCIONAN, por lo tanto la función delay(), la cual funciona utilizando timers es completamente inútil. Colocar esta función fuera de la interrupción significa un acto suicida en cuanto a las inconsistencias que se obtendrán en las lecturas, ya que congela su función es congelar el procesador. Sin embargo no significa que no se pueda utilizar un “retardo”. Lo que se tiene que hacer es comprobar el tiempo transcurrido y decidir si se toma la acción o no. Veamos el ejemplo:

volatile int contador = 0;   // Por preescripción médica
int n = contador ;
long lastMillis = 0 ;  // Variable global para tiempo

void setup()
   {    
        pinMode(2, INPUT);
        Serial.begin(9600); 
        attachInterrupt( 0, signal, CHANGE );
   } 
void loop()
   {  
      if (n != contador)
           {   
               Serial.println(contador);
               n = contador ;
           }
   }
void signal()
   {
       if ( millis() > lastMillis  + 16.66)
          {   
              contador++ ;
              lastMillis = millis();
          }
    }

Aquí tendremos que aprovechar el potencial de la función millis() la cual se encarga de capturar el tiempo en milisegundos desde que la placa Arduino se enciende. Esta función también funciona utilizando un timer. ESPERA, ¿un timer dentro de una interrupción?. SI, y les repito SI. Cuando la interrupción se está ejecutando efectivamente esta función no continua con su funcionalidad, se encuentra congelada, sin embargo no significa que no se pueda LEER su último valor almacenado, por lo tanto haciendo válido ese recurso para nuestro propósito FUNCIONA PERFECTAMENTE. En la interrupción lo que hacemos es que comprobamos si el valor actual de millis es mayor a la última vez que pasó por la interrupción mas 16.66 ms. Si no es así lo consideramos un rebote y lo ignoramos completamente. Por el contrario si ha pasado un tiempo prudencial incrementamos contador. La grandísima ventaja que representa realizar este tipo de comparación es que no congelamos el procesador con un delay, si no que le dejamos seguir con su trabajo, atendiendo otras interrupciones, por ejemplo.

Y bueno amigos esto ha sido todo de mi parte, espero y este publicación les sea de mucha utilidad en la aplicación que así lo requieran, me despido de ustedes. Hasta la próxima!

Leave a Reply

Your email address will not be published. Required fields are marked *