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:
- 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í .
- 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é aproximadamente8em, donde sea posible. (Generalmente, NO uso eventos de interrupción activados por bordes por varias razones).
La máquina de estado se ve así:
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ípicamente8emsi 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 8emtiempo 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, encuentro20emPerí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:
- cambiar de ESTABLECIDO a CAMBIO
- cambiar de CAMBIO a RESTANTE
- 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 8em significa que en algún lugar entre dieciséisms <t≤24empara pasar de inactivo a un resultado sin rebote activo reconocido. Y tomará otro24emantes de que el estado pueda volver a estar inactivo. Eso es un mínimo de40ms<t≤48ms 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<t≤120mspara 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.