debounce-interrupciones-arduino

Leer un pulsador con Arduino con interrupciones y debounce

En la entrada anterior vimos qué son las interrupciones y cómo usarlas para responder a eventos de hardware en pins.

También dejamos claro que los dispositivos físicos, como pulsadores, detectores ópticos, etc, presentan un efecto rebote que interfiere con el uso de interrupciones, y que necesitamos eliminarlo o no podremos usar interrupciones con estos dispositivos.

El proceso de eliminación de este rebote se llama “debounce”. En esta entrada aprenderemos qué es el rebote y aprenderemos a eliminarlo con debounce por hardware y por software.

¿Qué es el debounce?

Los dispositivos electrónicos al cambiar de estado generan una señal que, sin ser perfectamente cuadrada, en general es más o menos “limpia”. Veamos, por ejemplo, la señal que genera Arduino al cambiar el estado de una salida digital de HIGH a LOW.

arduino-sin-rebote

Sin embargo el mundo real no es tan bonito. Muchos dispositivos físicos habitualmente generan ruido en los flancos de señal. Como ejemplo, veamos la variación de tensión que ocurre cuando el cambio de estado se genera por un pulsador.

arduino-rebote

Observar la cantidad de ruido ocurrido tras el flanco. En esencia, en el rango de unos microsegundos la señal es puro ruido. Todos esos picos pueden provocar disparos múltiples de una interrupción.

Probando el rebote

Para probar el rebote, simplemente vamos a emplear un cable para conectar el Pin 2 y Ground (también podéis usar un pulsador o un interruptor).

arduino-debounce-esquema

Activamos la resistencia interna de Pull UP en el Pin 2 y definimos una interrupción al evento de bajada en el PIN, y en la función ISR asociada simplemente incrementamos un contador.

En el bucle principal, comprobamos si el contador, y si este ha sido modificado mostramos su valor por el puerto serie.

const int intPin = 2;
volatile int ISRCounter = 0;
int counter = 0;

void setup()
{
  pinMode(intPin, INPUT_PULLUP);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(intPin), debounceCount, FALLING);
}

void loop()
{
  if (counter != ISRCounter)
  {
    counter = ISRCounter;
    Serial.println(counter);
  }
}

void debounceCount()
{
  ISRCounter++;
}

Pruébalo online

Al probar nuestro montaje y poner en contacto el PIN 2 a GROUND, esperaríamos que la variable se incrementara de uno en uno. Pero veremos que en realidad salta varios números cada vez (incluso varias decenas).

Este es el efecto del rebote. El ruido de la señal está generando múltiples interrupciones cada vez que conectamos el cable.

Eliminando el rebote

Disponemos de dos formas de aplicar el debounce. Añadiendo dispositivos electrónicos que filtren la señal (debounce por hardware) o modificando nuestro código para eliminar el rebote (debounce por software).

Debounce por hardware

Aplicar un debounce por hardware tiene la ventaja de no incrementar el tiempo de ejecución de nuestro código. Además, en general, es una solución más robusta. Por contra, tiene la desventaja de aumentar la complejidad de nuestro montaje.

La forma más sencilla de aplicar un debounce por hardware es colocar un condensador en paralelo con el dispositivo (pulsador, interruptor, sensor…). Un condensador del orden de 1uF debería ser suficiente para filtrar la mayoría del ruido.

El esquema de conexión es el siguiente.

arduino-debounce-hardware

En general siempre es conveniente añadir un filtro por hardware cuando empleamos entradas físicas con interrupciones.

Debounce por software

El debounce por software tiene la ventaja de no requerir componentes adicionales. Resolvemos el rebote únicamente modificando el código de nuestro programa.

Como desventaja, incrementa levemente el tiempo de ejecución y la complejidad del código. Además, si no aplicamos el código correctamente podemos ignorar interrupciones “verdaderas”.

La forma más sencilla de aplicar un debounce por software es comprobar el tiempo entre disparos de la interrupción. Si el tiempo es inferior a un determinado umbral de tiempo (threshold) simplemente ignoramos la interrupción. En definitiva, hemos definido una “zona muerta” en la que ignoramos las interrupciones generadas.

Para aplicar el debounce por software, modificamos la función ISR de la siguiente forma.

const int timeThreshold = 150;
const int intPin = 2;
volatile int ISRCounter = 0;
int counter = 0;
long startTime = 0;

void setup()
{
  pinMode(intPin, INPUT_PULLUP);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(intPin), debounceCount, FALLING);
}

void loop()
{
  if (counter != ISRCounter)
  {
    counter = ISRCounter;
    Serial.println(counter);
  }
}

void debounceCount()
{
  if (millis() - startTime > timeThreshold)
  {
    ISRCounter++;
    startTime = millis();
  }
}

Pruébalo online

Un tiempo de 100-200ms es correcto para un pulsador pero en otros casos deberemos ajustar el tiempo de forma que eliminemos el rebote, pero no ignoremos dos posibles eventos cercanos “verdaderos”.

En un montaje real, lo mejor es emplear una combinación de ambos sistemas, a la vez que ajustamos correctamente el valor del condensados y los tiempos del filtro por software para adaptarlos a nuestro sistema.

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github. github-full