¿Cómo puedo eliminar el rebote de un interruptor tanto en los bordes positivos como en los negativos?

Me gustaría conectar un interruptor mecánico a un pin de interrupción en un procesador Arduino Uno donde la interrupción está configurada para interrupciones de CAMBIO (es decir, se activa en una señal positiva o negativa).

He encontrado numerosos enfoques para la implementación de circuitos antirrebote para señales positivas o negativas, pero no he encontrado sugerencias para circuitos que puedan contrarrestar señales positivas o negativas.

Esencialmente, quiero usar un solo pin de interrupción que pueda detectar la apertura o el cierre de un interruptor externo, y me gustaría eliminar el rebote de la entrada usando un enfoque de hardware.


Para ser un poco más específico, el interruptor en cuestión se abrirá y cerrará hasta 200 veces por segundo y me gustaría determinar la cantidad de tiempo que permanece cerrado cada vez que pasa por un ciclo de encendido/apagado. Es decir, no estoy hablando de un botón presionado por el usuario.

Un filtro RC debería rebotar en ambos sentidos.
Use un límite de retención en los contactos con pullup RC=T> tiempo de rebote o 10 ms
Es posible que a los pines MCU IO no les guste cuando las señales cambian lentamente, como los botones filtrados RC pasan mucho tiempo en el área de nivel indeterminado, por lo que recomiendo usar un disparador schmitt para agregar histéresis y conducir la MCU con señales limpias. Sin embargo, tengo que preguntarme qué es tan importante que el botón de usuario necesita generar una interrupción, por lo que si la interrupción no es necesaria, el filtrado tampoco es necesario.
Para ser un poco más específico, el interruptor en cuestión se abrirá y cerrará hasta 200 veces por segundo y me gustaría determinar la cantidad de tiempo que permanece cerrado cada vez que pasa por un ciclo de encendido/apagado. Es decir, no estoy hablando de un botón presionado por el usuario.
¿Hay algún sistema físico que tenga posibilidades limitadas de cambiar el estilo operativo instantáneamente y podría modelarse como la razón de los cambios de estado del interruptor? Estimar lo que hace el sistema tal vez podría producir mejores resultados.
El comentario de @JimLuby user287001 es pertinente. Si tiene alguna información "a priori" que se pueda aplicar, es mejor incluir esa información que permanecer ciego a ella. Así es como se diseñan los filtros óptimos, de hecho. Eso incluye Wiener y Kalman y Kalman/Bucy. Además, tengo curiosidad. ¿Por qué exactamente necesita calcular el ciclo de trabajo efectivo? ¿Qué está pasando, exactamente? Esto parece importante. Abre tu mano y déjanos ver lo que estás apretando ahí.
Recibí una respuesta con varias soluciones, todas ilustradas con diagramas de CircuitLab antes de ver su edición. ¡Solo necesitaba cambiar algunos valores de capacitores para adaptarlos, pero CircuitLab arruinó y borró mis diagramas! Lo siento, no tengo tiempo para rehacerlos ahora...
publique un enlace a la hoja de datos del interruptor... ¿qué tipo de mecanismo opera el interruptor?
Entonces, ¿es un interruptor mecánico real? ¿Que puede abrirse y cerrarse hasta 200 veces por segundo? En otras palabras, ¿puede permanecer en un estado por tan solo 5 ms? Creo RC seguido de comparador con histéresis. O simplemente use un gatillo Schmitt (que es casi lo mismo). Pero tengo la sensación de que esto es muy extraño y posiblemente un problema XY. Además, algunos procesadores ARM tienen temporizadores que están diseñados específicamente para capturar el ciclo de trabajo de entrada o el ancho de pulso. para tu información
¿Está seguro de que la duración máxima de rebote es más corta que el tiempo máximo de activación o desactivación legítima? 5 ms es mucho tiempo, pero he leído que algunos interruptores pueden rebotar durante más de 5 ms.
Debe capturar la forma de onda analógica en un osciloscopio para saber a qué se enfrenta. Puede ser que la forma de onda que ve en realidad no se pueda procesar correctamente para hacer lo que desea, incluso cuando la revisa una persona inteligente. O tal vez es fácil. De cualquier manera, saber con qué estás tratando sería bueno.

Respuestas (5)

Para eliminar el rebote de cualquiera de los flancos de una señal cambiante, utilice la histéresis. Muchos algoritmos antirrebote asumen una señal activa alta o baja activa, pero necesita detectar ambas.

Aquí está la esencia del algoritmo de histéresis:

    bool input_state = digitalRead(INPUT_PIN);
    unsigned long current_ms = millis();

    edge = rise = fall = false;

    // Hysteresis:
    //   If there is no change, reset the debounce timer.
    //   Else, compare the time difference with the debounce delay.
    if (input_state == output_state)
    {
      last_ms = current_ms;
    }
    else
    {
      if ((current_ms - last_ms) >= DEBOUNCE_DELAY_ms)
      {
        // Successfully debounced, so update the outputs.
        is_debounced = true;
        rise = input_state && !output_state;
        fall = !input_state && output_state;
        edge = rise || fall;
        output_state = input_state;
      }
      else
      {
          is_debounced = false;
      }
    }

Esto podría llamarse desde una rutina de servicio de interrupción algo como esto:

ISR(TIMER0_COMPA_vect)
{
  static Debouncer button1(BUTTON_PIN, DEBOUNCE_DELAY_ms);
  static bool led_state = false;
  
  button1.UpdateISR();
  
  // Toggle the LED on either edge of the debounced signal.
  if (button1.Edge())
  {
    led_state = !led_state;
    digitalWrite(LED_PIN, led_state);
  }
}

Aquí hay un enlace con el código completo del antirrebote de histéresis en GitHub que usa el millis()temporizador, pero es posible que deba cambiarlo para usar el micros()temporizador y posiblemente aumentar la frecuencia de interrupción para una mayor precisión en su aplicación. También necesita averiguar la longitud de DEBOUNCE_DELAY. Use un osciloscopio para ver cómo se ve el ruido de rebote del interruptor.

Si se trata de un interruptor de bloqueo físico, generalmente uso este tipo de código en un bucle de 10 ms con filtrado RC en el pin (no es necesario). Por supuesto, puede usar una interrupción de cambio de pin, pero es un poco complicado, porque no puede perder/olvidarse de manejar ni siquiera un borde en la entrada. Por ejemplo, detecta un flanco ascendente en la rutina PCINT, luego comienza a activar el estado de rebote como contar algunos números de niveles positivos, pero mientras tanto puede apagar el botón para detectar el flanco negativo, es decir, cancelar el rebote positivo o al menos pensar en esta opción. Lo útil de PCINT cuando se maneja el interruptor de bloqueo es mantener el microcontrolador "inactivo" si no se produce una interrupción y si se produce una interrupción, habilite algo como mi rutina en main ().

if(SWITCHED) {

    switched_on_count++;  
    switched_off_count = 0;

    if(switched_on_count > 10) {
        switched_on_count = 0;

        if(BUTTON == OFF) {                                             
                BUTTON = ON;        
        }                                       
    }
                                                    
} else {

    switched_off_count++;           
    switched_on_count = 0;

    if(switched_off_count > 10) {
        switched_off_count = 0;

        if(BUTTON == ON) {                                              
                BUTTON = OFF;       
        }                   
    }

}
OP dijo que la entrada puede cambiar hasta 200x por segundo y OP quiere capturar la duración del pulso. 200x implica una duración de solo 5ms. Todo eso, en conjunto, parece requerir que el interruptor sea neutralizado externamente en el hardware para evitar sobrecargar el sistema con interrupciones. Por lo que puedo decir del OP, el sondeo a 10 ms no funcionará ni remotamente (aunque normalmente para los botones, el sondeo a 10 ms es un gran enfoque).

Una forma sencilla de hacer esto es con un algoritmo interactivo.

Es similar al algoritmo propuesto por @Michal Podmanický, sin embargo, no necesita mantener un contador para las posiciones ON y OFF. Simplemente usa una variable de iterador, que amortigua la entrada oscilante (es decir, el interruptor de rebote) y cambia el estado de lectura solo cuando se alcanza el umbral, ya sea cuando el iterador vuelve a 0 para un estado bajo, o cuando alcanza el umbral positivo para alto estado.

#define MAXIMUM (10) // set this value depending on how quicly your fct is called
// and how bouncy your hardware is
uint8_t iterator = 0; // starts low

bool debounce(bool input)
{
  bool now_debounced_input;
    if (input == 0)
    {
        if (iterator > 0)
            iterator--;
    }
    else if (iterator < MAXIMUM)
        iterator++;

    if (iterator == 0)
        now_debounced_input = 0;
    else if (iterator >= MAXIMUM)
    {
        iterator = MAXIMUM; // Defensive code
        now_debounced_input = 1;
    }
return now_debounced_input ;
}
¿Ejecutaría eso dentro de un ISR que se llama 100 o 1000 veces por segundo?
No, una bandera es preferible a los métodos en un ISR
Correcto, pero si el contacto está rebotando, la bandera se configurará quizás a una velocidad de 1 kHz o incluso más rápida. Y el OP está tratando de extraer la duración del tiempo bajo y/o la duración del tiempo alto del pin IO. Por lo tanto, el OP necesita detectar con precisión los bordes positivos y negativos y medir el tiempo transcurrido entre ellos.
Nvm no me di cuenta que no era para un boton de usuario -.-

En términos generales, lo que necesita es obtener la salida lógica del interruptor a través de un filtro de paso bajo . Hay varias implementaciones posibles: hardware (filtro RC en un pin µC, otras más complejas); o software (las respuestas de JCSB y Michal P. por ejemplo). Los mejores dependerán de las especificaciones del circuito que estés diseñando. Si necesita muestrear el valor a 1 kHz, las implementaciones de hardware pueden ser preferibles para evitar la carga del software, especialmente si su µC no es rápido.

Es importante tener en cuenta que es posible que deba tener en cuenta el retraso inducido por el filtro de paso bajo si su aplicación necesita ser especialmente reactiva (aunque no debería ser un problema a 1 kHz).

También recomendaría usar un filtro de histéresis (un disparador Schmitt , por ejemplo) entre la salida del filtro LP y la entrada de su sistema para evitar demasiada variación del valor: las soluciones de JCSB y Michal P. intrínsecamente proporcionan esta función, lado del software.

Todo: decidí digitalizar la forma de onda del interruptor. Dado que puedo determinar el ciclo de trabajo y otras cosas en el procesamiento posterior. Agradezco los aportes de todos y planeo probar algunas de las sugerencias más adelante, ya que me gustaría comprender mejor la eliminación de rebotes de interruptores y el manejo de interrupciones. ¡Gracias de nuevo! -Jim

¿Quiere decir digitalizar como alimentarlo a un ADC en lugar de una entrada de interrupción?
Sí, eso es lo que quise decir.