¿Diferencias entre interrupciones y muestreo para botón de hardware?

8

Tengo un botón de hardware que conecté a una interrupción, pero mi problema es que rebota un poco, lo que hace que el botón no sea confiable. Creo que una buena parte de estos problemas se resolvería mediante el muestreo en el bucle principal, pero eso se siente técnicamente mal.

¿Las interrupciones son más apropiadas para la comunicación dentro del circuito o las interrupciones también son apropiadas para los interruptores de hardware? Si es así, ¿qué técnicas de rebote puedo usar?

Intenté mantener una variable del temporizador y compararla con el tiempo actual, los retrasos y otras técnicas. Parece que los rebotes son tan rápidos que no importa.

OneChillDude
fuente
2
Lea esto: ganssle.com/debouncing.pdf
Smith
No hay nada de malo con el muestreo en el bucle principal, si reacciona en el bucle principal. Las interrupciones son más apropiadas si quieres reaccionar asincrónicamente. A veces sí, y a veces no.
Eugene Ryabtsev
La mejor manera de eliminar el rebote es un simple filtro de paso bajo.
lucas92

Respuestas:

8

El rebote es una pregunta frecuente. Debería poder encontrar ... un número casi ilimitado de páginas web sobre el tema. Smith también comentó sobre el PDF ampliamente leído de Jack Ganssle sobre el tema. Y con todas estas respuestas tienes métodos de hardware y software.

Agregaré un poco a esta "literatura" hablando principalmente de ideas que no están bien cubiertas. Pero antes de hacerlo, un punto o dos:

  1. El rebote en el hardware analógico puede lograr resultados que usted no puede lograr con un interruptor "observado" solo digitalmente de forma periódica mediante sondeos o incluso por eventos de cambio de clavijas de hardware. Pero puede hacerlo "lo suficientemente bien" para todos los intentos y propósitos, digitalmente. Casi nadie en estos días usa soluciones externas antirrebote. Pero he usado de todo, desde el estiramiento del pulso usando one-shots (74121) hasta las técnicas mencionadas por Jack Ganssle aquí .
  2. Para aquellos que solo realizan programación integrada y no están interesados ​​en aprender sobre electrónica, los interruptores antirrebote son probablemente uno de los dos conjuntos de habilidades básicas necesarias. El funcionamiento de los LED es probablemente el otro. Y con esto, no me refiero a tener solo una habilidad en estos. Me refiero a poder hacerlo de varias maneras. Por lo que realmente haces necesidad de aprehender plenamente lo que Jack Ganssle escribe sobre, y aún más, en relación con los interruptores.

Como mencioné el estiramiento del pulso usando un 74121 y como Jack Ganssle no lo menciona, y tampoco lo hace nadie aquí todavía, también puedo proporcionar este enlace adicional como lectura adicional sugerida sobre el uso del 74121 o 555 como una sola vez temporizador para interruptores antirrebote.


Ahora, para hacerlo a través de la observación con un microcontrolador.

Usualmente uso una máquina de estado para manejar el rebote. Esto casi siempre es impulsado por un temporizador regular de "latidos" que configuré aproximadamente8ms, donde sea posible. (Generalmente, NO uso eventos de interrupción activados por bordes por varias razones).

La máquina de estado se ve así:

esquemático

simular este circuito : esquema creado con CircuitLab

El valor de DEBOUNCED para el conmutador podría tomar valores "inactivos", "activos" y "desconocidos". De esta manera, puede asegurarse de que su software espere hasta que el valor del interruptor se establezca después de la inicialización. Pero por lo general, no me molesto con eso. Reemplazo el valor "desconocido" con algún valor predeterminado y solo uso un sistema de valores binarios.

La máquina de estado se ingresa configurando primero el valor de rebote a su valor predeterminado y luego ingresando el estado "CAMBIO" de la máquina de estado. En cada intervalo de tiempo (típicamente8mssi me salgo con la suya), leeré el valor actual del interruptor y realizaré una actualización del estado actual y, posiblemente, el valor sin rebote. Entonces solo salgo. El código de alto nivel solo accede al estado sin rebote.

Si me importa, también puedo mantener un estado anterior sin rebote. En estos casos, cuando actualice el propio estado sin rebote, primero copiaré ese estado a un "estado sin rebote anterior". Entonces puedo usar el par de valores para determinar si ha habido una transición sin rebote. A veces, no me importan las transiciones. A veces lo hago. Entonces eso depende. Pero en todos los casos, solo quiero saber acerca de las transiciones que han sido eliminadas. Nunca me importan las transiciones runt . Por lo tanto, el código de alto nivel nunca usa ninguno de los estados internos que la máquina de estado usa para su propio trabajo.

Una de las cosas buenas de este método es que puedo eliminar el rebote de un puerto completo de conmutadores, a la vez. Y también puedo hacerlo sin una sola rama en el código de interrupción. Esto significa un código de eliminación de rebotes muy rápido y corto para hasta el ancho del puerto del microcontrolador (generalmente de 8 bits de ancho). Un ejemplo del Atmel AT90 muestra cómo se logra esto usando un evento de interrupción Timer0:

.equ    SWPORTPINS  =   PINB
.def    SwRawCurr   =   r4
.def    SwRawPrev   =   r5
.def    SwState     =   r6
.def    SwDebCurr   =   r7
.def    SwDebPrev   =   r8

            ; Debounce the input switches.

                mov     SwRawPrev, SwRawCurr
                in      SwRawCurr, SWPORTPINS
                mov     Timer0Tmp1, SwRawCurr
                eor     Timer0Tmp1, SwRawPrev
                mov     Timer0Tmp0, Timer0Tmp1
                or      Timer0Tmp1, SwState
                mov     SwState, Timer0Tmp0
                mov     Timer0Tmp0, Timer0Tmp1
                com     Timer0Tmp0
                and     Timer0Tmp1, SwDebCurr
                and     Timer0Tmp0, SwRawCurr
                or      Timer0Tmp1, Timer0Tmp0
                mov     SwDebPrev, SwDebCurr
                mov     SwDebCurr, Timer0Tmp1

Ahora, este ejemplo muestra la oferta completa, incluidos los valores de cambio sin rebote anteriores y actuales. Y también realiza todas las transiciones de estado necesarias. No muestro la inicialización de este código. Pero lo anterior deja claro lo fácil que es operar la máquina de estado y el poco código que se requiere para hacerlo. Es bastante rápido y simple y no requiere ramificación (lo que a veces implica ciclos adicionales, así como espacio de código adicional).


Prefiero usar 8mstiempo porque las pruebas largas con una variedad de personas diferentes que utilizan equipos en los que he trabajado en el pasado me han llevado allí. He intentado períodos más largos y cuando lo hago, empiezo a hacer que la gente me diga que la "capacidad de respuesta" no es lo suficientemente "enérgica". (En estos días, con los niños que crecen trabajando en tiempo real en juegos "shoot 'em up", incluso podría acortarlo aún más. Se quejarán amargamente incluso por retrasos leves causados ​​por los televisores digitales modernos al configurar y mostrar un marco).

Algunas personas tendrán sentimientos muy claros acerca de cuán nítido y receptivo debe ser un sistema. Crujiente y sensible significa muestra más a menudo, no menos Pero personalmente, encuentro20msPeríodos de observación aceptables. (Sin embargo, no encuentro tiempos más largos lo suficientemente buenos incluso para mí).

Tenga en cuenta que la máquina de estado que mencioné primero debe ingresar el estado SETTLED y luego permanecer allí durante un tiempo de muestra más antes de que se actualice el valor de DEBOUNCED. Por lo tanto, presionar un botón y mantenerlo presionado, incluso en las mejores circunstancias, requerirá estas transiciones:

  1. cambiar de ESTABLECIDO a CAMBIO
  2. cambiar de CAMBIO a RESTANTE
  3. permanecer en SETTLED, actualizando DEBOUNCED

Por lo tanto, un nuevo estado sin rebote requiere un mínimo de 3 períodos de tiempo de muestra para lograrlo.

Un botón pulsador requerirá al menos 6 veces de muestra para pasar de inactivo a activo y luego a inactivo.


Mencioné los detalles anteriores para que quede absolutamente claro que un tiempo de muestra de 8ms significa que en algún lugar entre 16ms<t24mspara pasar de inactivo a un resultado sin rebote activo reconocido. Y tomará otro24msantes de que el estado pueda volver a estar inactivo. Eso es un mínimo de40ms<t48ms para pasar por un ciclo completo de botones.

El uso de tiempos de muestra más largos tendrá períodos correspondientemente más largos. Utilizando el20ms Ya lo mencioné como "aceptable" para mí, entonces significa en algún lugar 100ms<t120mspara un ciclo completo de botones. Y que está recibiendo de lleno hasta la zona donde la gente no tienden a aviso. Ciertamente no me gusta la "sensación" si se hace más larga que eso.

Si sigue esta ruta, no sea arrogante al usar tiempos de muestreo más largos. Si debe hacerlo, creo que también debe hacer muchas pruebas con usuarios / consumidores.

Y si está desarrollando código para un teclado de mecanografía, use tiempos más cortos. El récord para un mecanógrafo se estableció hace décadas en 217 palabras por minuto. Esto da como resultado aproximadamente una clave cada45ms. Los mecanógrafos como ese están presionando varias teclas en un orden controlado. Para obtener un buen rendimiento para mecanógrafos muy rápidos que usan un sistema de conmutación de relé de láminas humedecido con mercurio, descubrí que2ms funcionado bien.

jonk
fuente
los tiempos de rebote varían de 0 para interruptores de mercurio a "pocos" ms típ para interruptores micro táctiles a 30 ms para interruptores de palanca torpes, por lo que 8 ms es un buen número considerando el aumento del tiempo de rebote con el envejecimiento.
Tony Stewart Sunnyskyguy EE75
@ TonyStewart.EEsince'75 Elegí hacer pruebas exhaustivas con usuarios que utilizan equipos con una variedad de diferentes tipos de interruptores y la cifra de 8 ms proviene de una destilación de todo ese trabajo. (No me preocupé demasiado por la "teoría" ya que la práctica de construir y hacer interruptores, y su gran variedad, hizo que la recopilación y el análisis de esos datos parecieran desalentadores). Siempre uso 8 ms, cuando es posible, ya que parece siendo la zona de acción larga experiencia software de escritura dado que funciona igual y donde después de la venta quejas van a una exacta cero (en ese momento, de todos modos.)
Jonk
@ TonyStewart.EEsince'75 Por cierto, esta prueba INCLUYE el uso de relés de láminas húmedas de mercurio como parte de los interruptores de tecla utilizados en los teclados (que, creo, ya no parecen estar hechos). En estos casos, sin embargo, Voy al muestreo de 1-2 ms (depende de la unidad)
Jonk
Esa luz láser de jardín que mencioné hace una vez ... tiene interruptores de control remoto de membrana táctil con un tiempo de rebote bajo, pero el programador los hizo alternar a una velocidad de 10Hz, por lo que uno debe liberarlos en <100 ms, de lo contrario, la energía se enciende y se apaga. ?. en otra nota .. El teclado de piano Yamaha es extremadamente rápido y admite el desplazamiento de 10 teclas, mientras que solo el teclado original de PC de IBM era compatible con el verdadero desplazamiento del borde de ataque. Desde entonces, todos los teclados son los primeros trazos, son el borde de ataque y luego el rollover del borde de salida, lo cual es un PITA para las habilidades de escritura pobres como las mías
Tony Stewart Sunnyskyguy EE75
@ TonyStewart.EEsince'75 Esta área de muestreo de cambio es un punto doloroso. El advenimiento de micros baratos con cero rebotes externos y quién sabe qué interruptor aplicado, sumado a la ignorancia del programador incorporado, ha significado que realmente encuentro problemas con casi CADA UNO instrumento integrado con un teclado o un botón. TODOS funcionan terriblemente, en mi opción. Y creo que se debe principalmente a que los programadores tienen poca o ninguna experiencia, solo "busca en Google y aplícalo" sin pensarlo. A veces, incluso su código de sal con puntos de votación aleatorios. Es basura. Molesto Es fácil acertar.
jonk
5

El rebote se puede hacer en el software enmascarando las IRQ para el tiempo de rebote o en el hardware agregando un condensador de retención con su RC = T> tiempo de rebote que varía de 1 a 15 ms dependiendo del tamaño del interruptor.

  • ej., pull up de 100k y 0.1μF a través del interruptor = 10ms @ 63% o ~ 8ms a 50% Vdd o si se usa la puerta de disparo Schmitt @ 1.33V = Vil desde 5V o ~ 73% V + ~ 12ms
Tony Stewart Sunnyskyguy EE75
fuente
4

Para realizar un rechazo de SW, registre la marca de tiempo del evento actual y verifique el retraso del último evento válido:

#define DELAY_DEBOUNCE       150

uint32_t    __ts_lastpress = 0;

ISR(some_vector)
{
    uint32_t    now = millis(); // some timer tick counter

    if ( now - __ts_lastpress < DELAY_DEBOUNCE )
        return; // ignore it

    __ts_lastpress = now;
    // do the job here
}

UPD: con poca modificación puede registrar doble clic:

#define DELAY_DEBOUNCE       150
#define DELAY_DOUBLE_CLICK   600

uint32_t    __ts_lastpress = 0;

ISR(some_vector)
{
    uint32_t    now = millis(); // some timer tick counter

    if ( now - __ts_lastpress < DELAY_DEBOUNCE )
        return; // ignore it

    // do the job here
    if ( now - __ts_lastpress < DELAY_DOUBLE_CLICK )
    {
        // it is double click
    }
    else
    {
        // it is single click
    }

    __ts_lastpress = now;
}
Flanker
fuente
2

Las interrupciones son definitivamente excelentes para los interruptores de hardware también. Al utilizar interrupciones, está evitando un gran desperdicio de recursos y posiblemente de energía, especialmente si se trata de dispositivos que funcionan con baterías.

Además, a medida que su código se hace más y más grande, verá que es aún más fácil implementar las interrupciones para los botones que sondearlos en su bucle principal.

En cuanto a su eliminación, probablemente sea un problema de codificación. Generalmente uso un temporizador de ~ 10 ms para eliminar el rebote, mientras verifico la liberación del botón. Asegúrese de deshabilitar también temporalmente la interrupción del botón mientras lo cancela, para que la rutina de interrupción no se ejecute varias veces.

Si aún tiene problemas, publique el código aquí, para que podamos ayudarlo.

Pedro Mantovani
fuente
1

Esto es bastante similar a la respuesta de Tony Stewart, pero creo que podría ampliarse un poco.

El esquema superior es para una interrupción en el borde bajo o en el borde descendente. El esquema inferior es para una interrupción en el borde alto o ascendente.

esquemático

simular este circuito : esquema creado con CircuitLab

personalmente, dado el costo de un condensador, vale la pena simplemente usarlo, en lugar de preocuparme si mi software rebota es defectuoso.

Tenga en cuenta que, como dijo Tony Stewart, la constante de tiempo en este circuito es de 10 ms (RC o 10kΩ1μF). Va a tomar entre tres y cinco constantes de tiempo (dependiendo de la sensibilidad de su microcontrolador para que el botón se reinicie, así que si su microcontrolador tiene problemas para repetir la función de interrupción, esa puede ser la causa, y es posible que necesite ajuste la tapa / resistencia para que la interrupción no ocurra varias veces (es decir, solo si su interrupción está configurada para funcionar en una señal alta o baja, y no en el borde ascendente o descendente.

Relacionado con el rebote de hardware

ambitiose_sed_ineptum
fuente
1
Cualquiera de las versiones funciona para ambos bordes + ve o -ve, especialmente si el pin de interrupción tiene una característica de entrada de estilo schmitt (muchos lo hacen). Tanto SW1 como SW2 experimentan un aumento de corriente al cerrar. Algunos botones de botón de carbono pueden dar resultados diferentes a los botones de domo de metal.
glen_geek
1

Los humanos son lentos, no necesitamos la atención inmediata de un micro que está en el rango de microsegundos.

Por supuesto, esta no es la única ni la forma correcta de hacerlo siempre, pero en general me parece más sensato configurar un temporizador (muchos micros tienen marcas de sistema) para disparar una interrupción a intervalos fijos y cambiar el estado del pin a un variable para que el código lo examine más tarde. Terminas con una var que está llena de cenizas durante el rebote

10010110 ceniza

pero en ciertos puntos en el tiempo obtendrá estos 4 valores:
01111111 borde ascendente acaba de eliminar el
botón 11111111 en estado estable hacia arriba
10000000 borde descendente acaba de eliminar el
botón 00000000 en estado estable hacia abajo

Sin embargo, la mayoría de las veces, solo uso un contador que se restablece durante el rebote. Es rápido, probado y fácil de hacer.
Si falla, ¡intento algo más inteligente del documento de Ganssle que otros sugirieron!

zakkos
fuente