01 · Punto de partida
Presionás una vez, cuenta veinte
Armaste un contador que suma 1 cada vez que se pulsa un botón. Lo probás: pulsás una vez y el display muestra 7. Pulsás otra vez y salta a 19. El circuito está bien, el botón no está roto: el problema es que un contacto metálico, al cerrarse, rebota como una pelota contra el piso y genera una ráfaga de aperturas y cierres antes de quedar firme.
Una escena típica. Una interrupción por flanco en un microcontrolador debería ejecutarse una vez por pulsación. En la práctica se ejecuta cinco, diez o veinte veces, y el menú de tu aparato salta de la opción 1 a la 6. Con un LED que solo muestra el estado actual no notás nada, porque el ojo promedia; pero apenas el circuito cuenta eventos, el rebote lo rompe.
Para el programador es el mismo problema que un usuario que hace varios clics seguidos en un botón lento: hay que decidir cuándo dos eventos son «el mismo». La diferencia es que acá sucede en milisegundos y no se ve.
- ¿Cómo se define el valor de una entrada cuando el botón está suelto?
- ¿Cuánto dura un rebote y qué circuitos rompe?
- ¿Cómo se filtra con un capacitor y un Schmitt trigger?
- ¿Cómo se filtra con dos puertas NAND?
- ¿Cómo se filtra en software sin bloquear el programa?
02 · Pull-up y pull-down
Una entrada que nadie maneja
Un pulsador solo conecta dos puntos cuando se aprieta; suelto, no conecta nada. Si una entrada digital está conectada a un botón suelto, queda flotando: nadie la tira a 0 ni a 1, capta ruido y puede leer cualquier cosa (Temas 3 y 7). La solución es una resistencia que le da un valor por defecto: una resistencia de pull.
| Conexión | Suelto | Pulsado | Comentario |
|---|---|---|---|
| Pull-up a VCC + botón a GND | 1 | 0 | La convención. Activo en bajo (Tema 7). Consume solo mientras se pulsa |
| Pull-down a GND + botón a VCC | 0 | 1 | Válida. Intuitiva («1 = pulsado»), menos común en microcontroladores |
| Botón a GND sin resistencia | flota | 0 | Mal: suelto, la entrada capta ruido |
| Botón a VCC sin resistencia | flota | 1 | Mal por lo mismo. Y si el pin está en modo salida, pulsar lo cortocircuita |
Qué valor de resistencia
Con pull-up y botón a masa, la resistencia queda entre VCC y GND cuando se pulsa, y por ella circula I = VCC / R. Un valor chico da una entrada firme pero malgasta corriente; uno grande ahorra, pero deja la entrada débil frente al ruido y a la pequeña corriente de fuga de la propia entrada.
| R | Corriente pulsado | Inmunidad al ruido | Cuándo |
|---|---|---|---|
| 1 kΩ | 5 mA | Excelente | Cables largos o muy ruidosos; consume bastante |
| 4,7 kΩ | ≈ 1 mA | Muy buena | Cables de decenas de centímetros, entornos con ruido |
| 10 kΩ | 0,5 mA | Buena | El valor por defecto: cumple casi siempre |
| 100 kΩ | 0,05 mA | Aceptable | Sistemas a batería con cables cortos |
| 1 MΩ | 5 µA | Pobre | Antena: una fuga de 1 µA ya cae 1 V |
- Pull-up interno
- La mayoría de los microcontroladores traen pull-ups programables (típicamente de 20 a 50 kΩ). Ahorran la resistencia externa pero no el rebote, y con cables largos o ruidosos pueden quedar cortos de fuerza.
- Un pull consume poco
- Con pull-up y botón a masa, la corriente solo circula mientras se pulsa. Suelto, el botón es un circuito abierto y no pasa nada: 0 mA.
- Protección de la entrada
- Con cables largos o botones accesibles al usuario se suele agregar una resistencia en serie (100 a 470 Ω) antes del pin para limitar corrientes por descargas estáticas o picos.
03 · Rebote
Veinte flancos por un dedo
Un pulsador mecánico es una lámina elástica que se aprieta contra un contacto. Cuando choca, la lámina rebota y se separa y vuelve a tocar varias veces hasta que se asienta. Mientras tanto, la entrada ve una serie de flancos rápidos. Lo mismo ocurre, en menor grado, al soltar.
| Contacto | Duración habitual | Comentario |
|---|---|---|
| Pulsador táctil pequeño | 0,1 a 5 ms | Los baratos son más ruidosos que los buenos |
| Interruptor de palanca | 1 a 10 ms | Rebota al cerrar y al abrir |
| Pulsador de panel, botón grande | 5 a 50 ms | Mayor masa y recorrido, más rebote |
| Relé mecánico | 1 a 10 ms | Su contacto rebota igual que un botón |
| Sensor magnético (reed) | 0,5 a 3 ms | Rebote corto pero presente |
Como orden de magnitud: unos pocos milisegundos, hasta unas decenas en los peores. Un microcontrolador a 16 MHz ejecuta miles de instrucciones en ese tiempo; para él, el rebote es una eternidad llena de eventos.
| Circuito | Síntoma | Remedio |
|---|---|---|
| Contador por flancos | Suma varias unidades por pulsación | Antirrebote obligatorio |
| Interrupción por flanco | Dispara muchas veces el servicio por pulsación | Antirrebote por tiempo o por hardware |
| Reloj manual de un circuito secuencial | Avanza varios pasos de una vez | RC + Schmitt, o latch SR |
| Alternar un estado (toggle) | Cambia de estado un número aleatorio de veces | Antirrebote |
| LED que sigue el botón | Nada visible: el ojo promedia | Ninguno |
| Lectura de nivel cada 100 ms | Casi nunca lo ve | Ninguno, o una segunda lectura |
04 · Antirrebote por hardware
RC + Schmitt, o dos NAND
Un filtro RC con un Schmitt trigger
La idea: si la señal pasa por una resistencia R hacia un capacitor C, el voltaje del capacitor no puede cambiar de golpe. Cambia con una constante de tiempo τ = R · C. Los rebotes, que son más rápidos que τ, apenas lo mueven; el cambio sostenido sí lo hace subir o bajar hasta el nuevo valor.
Pero una rampa lenta en una entrada lógica común es un problema: mientras el voltaje cruza la zona entre 0 y 1 (Tema 3), la puerta puede oscilar o dar flancos dobles. El Schmitt trigger lo resuelve con histéresis: tiene dos umbrales. Para pasar de 0 a 1, la entrada tiene que subir hasta el umbral alto; para volver a 0, tiene que bajar hasta el umbral bajo, que es menor. Una vez que cruzó, pequeñas oscilaciones no lo hacen volver.
- 74HC14
- Seis inversores Schmitt en un chip (Tema 7). A 4,5 V sus umbrales típicos rondan 2,4 V (subida) y 1,4 V (bajada); varían de un chip a otro y con la tensión, así que no los uses como valor exacto. Es el componente clásico de este filtro.
- 74HC132
- Cuatro NAND de dos entradas con Schmitt. Sirve para el mismo filtro con una entrada extra de habilitación.
- Qué valor de τ
- El mismo orden que el rebote medido: si el botón rebota 10 ms, τ de unos 5 a 15 ms. Con 10 kΩ, 1 µF da 10 ms; 100 nF da 1 ms. Más grande filtra más, pero agrega retardo: el cambio demora unos 0,8 a 1,2 τ en cruzar los umbrales.
- Variantes del circuito
- La simétrica (R en serie después del botón y C a masa) demora por igual al pulsar y soltar. La simple (pull-up y C en paralelo con el botón) descarga el capacitor por el contacto casi sin retardo al pulsar, y tarda R·C al soltar. En ambas, el Schmitt va al final.
| R | C | τ | Sube (≈ 0,8 τ) | Baja (≈ 1,2 τ) | Para rebotes de… |
|---|---|---|---|---|---|
| 10 kΩ | 100 nF | 1 ms | 0,8 ms | 1,2 ms | Hasta ~1 ms (pulsadores táctiles buenos) |
| 10 kΩ | 470 nF | 4,7 ms | 3,8 ms | 5,7 ms | Hasta ~5 ms |
| 10 kΩ | 1 µF | 10 ms | 8 ms | 12 ms | Hasta ~10 ms (el caso general) |
| 10 kΩ | 4,7 µF | 47 ms | 38 ms | 57 ms | Botones grandes; se siente lento |
Dos NAND cruzadas: un latch SR
El Tema 9 mostró que dos NAND cruzadas guardan un bit. Con un interruptor de dos posiciones (SPDT, un común y dos contactos), cada contacto va a una entrada del latch con su pull-up. Mientras el común toca un contacto, esa entrada está en 0 y fija la salida. Cuando la lámina rebota y se separa de ese contacto, la entrada vuelve a 1 y el latch está en (1, 1): mantiene su estado. Solo cambia cuando el común toca el contacto opuesto, y eso no sucede hasta completar el recorrido.
| Posición del conmutador | ~S | ~R | Q |
|---|---|---|---|
| Toca el contacto «set» | 0 | 1 | 1 |
| Rebota (en el aire) | 1 | 1 | sigue en 1 |
| Vuelve a tocar «set» | 0 | 1 | 1 |
| Toca el contacto «reset» | 1 | 0 | 0 |
Es un antirrebote perfecto, sin retardo y sin dependencia de un tiempo, pero necesita un conmutador de tres terminales, no un pulsador común. Se usa en relojes manuales de bancos de práctica y en interruptores de fin de carrera.
05 · Antirrebote por software
Ignorar el cambio por un rato
Con un microcontrolador, el antirrebote por software cuesta cero componentes: un par de líneas de código. Hay cuatro técnicas, de la más simple a la más robusta.
| Técnica | Cómo funciona | Ventajas | Problemas |
|---|---|---|---|
| Espera bloqueante | Al ver un cambio, delay(20) y releer | Trivial | Congela todo el programa durante la espera; no escala a varios botones |
| Ventana de ignorar (no bloqueante) | Se acepta el primer cambio y se ignoran los siguientes durante W ms, comparando con millis() | Responde de inmediato; no bloquea | Si W es menor que el rebote, se cuelan rebotes |
| Muestras consecutivas (integrador) | Se acepta el cambio cuando N muestras seguidas coinciden (por ejemplo, un registro de 8 bits que debe valer 0x00 o 0xFF) | Robusto ante ruido; filtra también glitches ajenos al rebote | Agrega retardo de N muestras |
| Máquina de estados | Estados «suelto», «pulsado confirmado», con temporizadores | Detecta pulsación larga, doble clic, repetición | Más código |
- Con qué frecuencia leer
- Lo más sencillo es muestrear el botón desde un temporizador cada 1 a 10 ms (sondeo, o polling) y procesar los cambios en el programa principal. Es más fácil de depurar que filtrar dentro de una interrupción.
- Ventana mayor o igual al rebote
- La regla de oro: W ≥ el tiempo de rebote medido, con margen. Típicamente 20 a 50 ms. Más largo agrega demora al responder a pulsaciones rápidas.
- Detectar el cambio con XOR
- El detector de cambios del Tema 10 sirve acá:
cambio = nuevo ^ anteriormarca qué botones cambiaron en un puerto entero, y se combina con la ventana para aceptarlos. - Interrupciones
- Una interrupción por flanco dispara con el primer rebote. Una opción sensata: en la interrupción, solo anotar la hora y desactivarla durante W ms; la acción real se hace después, ya filtrada.
06 · Casos reales
Más allá del botón simple
- Soltar también rebota
- Al liberar el botón hay otra ráfaga. Un contador que cuenta flancos de bajada y no filtra puede contar falsos «cierres» durante esa ráfaga, porque el contacto cae un instante antes de quedar suelto. Filtrá los dos sentidos.
- Pulsación larga y repetición
- Con el estado ya filtrado, medir cuánto dura la pulsación permite distinguir un toque corto de uno largo (por ejemplo 300 ms o más) y repetir una acción mientras se mantiene. Es una pequeña máquina de estados.
- Teclado matricial
- Un teclado de 16 teclas se lee con 8 pines (4 filas y 4 columnas) en lugar de 16. Se activa una fila por vez y se leen las columnas; cada tecla rebota como un botón individual, así que el filtrado se aplica por tecla.
- Encoder rotativo
- Dos contactos que se abren y cierran en cuadratura al girar. También rebotan y se leen con la lógica del código Gray (Tema 5): entre un estado y el siguiente cambia un solo bit, y un salto de dos bits indica un error de lectura.
- Cables largos y ruido
- Un botón al final de un cable de metros recibe interferencias que parecen pulsaciones. Se combina pull-up fuerte (1 a 4,7 kΩ), resistencia en serie, capacitor y filtrado por software con ventana larga.
- Tensiones distintas
- Si el botón está en un sistema de 12 o 24 V, nunca lo conectes directo a un pin de 3,3 o 5 V: hace falta un divisor, un optoacoplador o una entrada industrial.
07 · Simulador
Laboratorio: presionar sin mentir
Cada vez que apretás «Presionar», el simulador genera un ciclo completo: el dedo aprieta el botón, lo mantiene y lo suelta. Cada transición produce una ráfaga de rebotes al azar. Esa señal cruda pasa por tres detectores en paralelo, y cada uno cuenta cuántas pulsaciones cree haber visto. Con las perillas modificás el rebote, el filtro RC y la ventana de software, y los trazos se actualizan al instante.
- Fila 1: el pin real (1 = suelto con pull-up, 0 = pulsado). Los puntos naranjas son los flancos de bajada que vería una interrupción simple.
- Fila 2: la tensión del capacitor del filtro RC, con sus dos umbrales de Schmitt.
- Filas 3 y 4: la salida del Schmitt y la salida del antirrebote por software con ventana. Los puntos naranjas son las pulsaciones que cada uno detecta.
Presionómetro
Ciclo de ejemplo (aún sin contar)
Presioná para generar rebotes y contar.
Recorrido guiado (7 minutos)
- 1Presioná varias veces con los valores iniciales.
El contador crudo suma varias unidades por ciclo, mientras que el RC y el software suman 1. Un solo gesto del dedo, una sola pulsación real.
- 2Mirá la fila del pin al soltar.
La suelta también rebota. Por eso el crudo cuenta más que solo los rebotes de la pulsación: algunos de sus puntos naranjas aparecen cuando el botón se está soltando.
- 3Bajá τ a 0,5 ms.
El capacitor sigue a los rebotes y el Schmitt los deja pasar: el contador RC empieza a contar de más. Un filtro más rápido que el rebote no filtra.
- 4Subí τ a 20 ms.
La salida del Schmitt tarda más en reaccionar (mirá el retardo entre el dedo y el flanco limpio). Filtra bien, pero cuesta latencia; con una pulsación muy corta podría directamente no llegar al umbral.
- 5Bajá la ventana de software a menos que el rebote.
Con rebote de 10 ms y ventana de 3 ms, el software acepta un cambio cuando todavía rebota y cuenta pulsaciones falsas. La ventana tiene que ser mayor o igual que el rebote.
- 6Subí el rebote a 40 ms con ventana de 20 ms.
Ahora el rebote es más largo que la ventana: el software también falla. Un antirrebote es tan bueno como la medición que lo calibró.
- 7Ventana en 0.
Sin ventana el software cuenta igual que el pin crudo, muestreado cada milisegundo. Un detector sin filtro es solo un contador de flancos.
Preguntas para pensar
- Con los valores iniciales, presioná 5 veces. ¿Cuánto vale cada contador? ¿Cuántas debería valer?
- ¿Por qué el contador crudo suma más que los rebotes de la pulsación?
- ¿Qué ventana mínima necesitás para un rebote de 25 ms? ¿Y qué τ de RC?
- ¿Qué pasa si presionás con un rebote de 1 ms y una ventana de 50 ms? ¿Hay algún costo?
Ver respuestas sugeridas
- El real vale 5; el RC y el software deberían valer 5; el crudo, bastante más (cada ciclo aporta varios flancos de bajada entre pulsación y suelta).
- Porque cuenta todos los flancos de bajada, incluidos los que ocurren al soltar el botón, cuando el contacto cae un instante antes de abrirse del todo.
- Ventana de al menos 25 ms; en la práctica 40 a 50 ms para tener margen. Un τ de unos 15 a 25 ms, con la latencia correspondiente (≈ 1 τ).
- Funciona, pero se pierde velocidad de respuesta: dos pulsaciones más cercanas que 50 ms se confunden. Es el costo de una ventana demasiado grande.
08 · Rebote en código
Generar, filtrar y contar
Estos bloques modelan un botón con rebotes reproducibles, calculan el efecto del pull-up, implementan tres filtros (ventana de tiempo, registro de muestras y máquina de estados con pulsación corta y larga) y simulan el filtro RC con Schmitt para comprobar qué τ funciona.
Python en tu navegador. Los bloques son editables: cambiá el rebote, las ventanas o la semilla y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
Paso 1: el pull-up y su corriente
VCC = 5.0
FUGA = 1e-6 # corriente de fuga de la entrada, orden de magnitud
print(" R (ohm) I pulsado caída por fuga")
for R in (1_000, 4_700, 10_000, 100_000, 1_000_000):
i = VCC / R
caida = FUGA * R
print(f"{R:>10,} {i * 1000:7.3f} mA {caida:6.3f} V")
10 kΩ: 0,5 mA y apenas 10 mV de caída. 1 MΩ: 5 µA de consumo, pero una fuga de 1 µA ya resta 1 V a un «1» de 5 V: la entrada queda débil.
Paso 2: un ciclo de pulsación con rebotes
import random
def ciclo(rng, rebote=10, t_p=5, t_r=40, largo=70):
# una muestra por ms: 1 = suelto (pull-up), 0 = pulsado
def cambios():
k = rng.choice([1, 2, 3, 4])
return [0] + sorted(rng.sample(range(1, rebote), 2 * k))
cp, cr = cambios(), cambios()
muestras = []
for t in range(largo):
n = sum(1 for c in cp if t - t_p >= c) + sum(1 for c in cr if t - t_r >= c)
muestras.append(1 if n % 2 == 0 else 0)
return muestras
def bajadas(m):
return sum(1 for a, b in zip(m, m[1:]) if a == 1 and b == 0)
rng = random.Random(7)
m = ciclo(rng)
print("".join("▔" if x else "▁" for x in m))
print("flancos de bajada vistos por una interrupción simple:", bajadas(m), "(el dedo hizo 1)")
El dedo hizo una pulsación, pero el pin tiene varios flancos de bajada: los de la pulsación y los de la suelta. Cambiá la semilla y verás otro número, casi siempre mayor que 1.
Paso 3: ventana de ignorar (no bloqueante)
import random
def ciclo(rng, rebote=10, t_p=5, t_r=40, largo=70):
def cambios():
k = rng.choice([1, 2, 3, 4])
return [0] + sorted(rng.sample(range(1, rebote), 2 * k))
cp, cr = cambios(), cambios()
return [1 if (sum(1 for c in cp if t - t_p >= c) + sum(1 for c in cr if t - t_r >= c)) % 2 == 0 else 0
for t in range(largo)]
def debounce(muestras, ventana):
estado, ultimo, eventos = 1, -10**9, []
for t, m in enumerate(muestras):
if m != estado and t - ultimo >= ventana:
estado, ultimo = m, t
eventos.append((t, "presión" if m == 0 else "suelta"))
return eventos
m = ciclo(random.Random(7), rebote=10)
for ventana in (0, 2, 5, 10, 20):
ev = debounce(m, ventana)
pulsaciones = sum(1 for _, e in ev if e == "presión")
print(f"ventana {ventana:>2} ms: {pulsaciones} presión(es), {len(ev)} eventos")
Con ventana 0 el filtro acepta todo y cuenta de más. A medida que crece y alcanza el rebote (10 ms), los eventos bajan a 2 (una presión y una suelta). La ventana tiene que ser ≥ al rebote.
Paso 4: registro de muestras consecutivas
import random
def ciclo(rng, rebote=10, t_p=5, t_r=40, largo=70):
def cambios():
k = rng.choice([1, 2, 3, 4])
return [0] + sorted(rng.sample(range(1, rebote), 2 * k))
cp, cr = cambios(), cambios()
return [1 if (sum(1 for c in cp if t - t_p >= c) + sum(1 for c in cr if t - t_r >= c)) % 2 == 0 else 0
for t in range(largo)]
def debounce_registro(muestras):
h, estado, eventos = 0xFF, 1, []
for t, m in enumerate(muestras):
h = ((h << 1) | m) & 0xFF # historial de las últimas 8 muestras
if h == 0x00 and estado == 1:
estado = 0
eventos.append((t, "presionado"))
elif h == 0xFF and estado == 0:
estado = 1
eventos.append((t, "soltado"))
return eventos
for semilla in (7, 8, 9):
m = ciclo(random.Random(semilla))
print(f"semilla {semilla}:", debounce_registro(m))
Hace falta ver 8 muestras iguales seguidas (0x00 o 0xFF) para aceptar el cambio. Cuesta un retardo de unos 8 ms, pero filtra también picos de ruido que no son rebotes.
Paso 5: pulsación corta o larga
import random
def ciclo(rng, rebote=10, t_p=5, t_r=40, largo=70):
def cambios():
k = rng.choice([1, 2, 3, 4])
return [0] + sorted(rng.sample(range(1, rebote), 2 * k))
cp, cr = cambios(), cambios()
return [1 if (sum(1 for c in cp if t - t_p >= c) + sum(1 for c in cr if t - t_r >= c)) % 2 == 0 else 0
for t in range(largo)]
class Boton:
def __init__(self, ventana=20, largo_ms=300):
self.estado, self.ult, self.t_press = 1, -10**9, None
self.ventana, self.largo = ventana, largo_ms
def muestrear(self, t, m):
if (m ^ self.estado) and t - self.ult >= self.ventana: # XOR: ¿cambió?
self.estado, self.ult = m, t
if m == 0:
self.t_press = t
return "presión"
return "suelta larga" if t - self.t_press >= self.largo else "suelta corta"
return None
rng = random.Random(3)
for nombre, t_r, largo in (("toque", 55, 100), ("sostenido", 405, 500)):
b = Boton()
ev = [(t, e) for t, m in enumerate(ciclo(rng, 10, 5, t_r, largo)) if (e := b.muestrear(t, m))]
print(nombre, ev)
Con el estado ya filtrado, medir el tiempo entre la presión y la suelta distingue un toque (≈ 50 ms) de una pulsación sostenida (≈ 400 ms). El detector de cambios es el XOR del Tema 10.
Paso 6: filtro RC con Schmitt
from math import log
import random
def ciclo(rng, rebote=10, t_p=5, t_r=60, largo=110):
def cambios():
k = rng.choice([1, 2, 3, 4])
return [0] + sorted(rng.sample(range(1, rebote), 2 * k))
cp, cr = cambios(), cambios()
return [1 if (sum(1 for c in cp if t - t_p >= c) + sum(1 for c in cr if t - t_r >= c)) % 2 == 0 else 0
for t in range(largo)]
def rc_schmitt(muestras_ms, tau_ms, vt_alto=0.55, vt_bajo=0.30, dt=0.1):
vc, salida, caidas = 1.0, 1, 0
for m in muestras_ms:
for _ in range(round(1 / dt)):
vc += (m - vc) * dt / tau_ms
if salida == 1 and vc <= vt_bajo:
salida, caidas = 0, caidas + 1
elif salida == 0 and vc >= vt_alto:
salida = 1
return caidas
m = ciclo(random.Random(3))
for tau in (0.3, 1, 3, 5, 10):
print(f"τ = {tau:>4} ms -> pulsaciones contadas: {rc_schmitt(m, tau)}")
print()
for R, C in ((10_000, 100e-9), (10_000, 1e-6), (10_000, 4.7e-6)):
tau = R * C * 1000
print(f"R={R:,} C={C * 1e6:g} µF τ={tau:5.1f} ms sube≈{tau * log(1 / (1 - 0.55)):5.1f} ms baja≈{tau * log(1 / 0.30):5.1f} ms")
Con τ mucho menor que el rebote el Schmitt deja pasar flancos de más; con τ del orden del rebote cuenta 1. La segunda tabla da los retardos: el cambio recién se ve a 0,8 τ (subida) y 1,2 τ (bajada).
09 · Analogía de programador
Debounce, throttle y el usuario impaciente
Si trabajaste con interfaces, ya conocés el problema con otros nombres. En JavaScript hay dos técnicas con nombre propio, y las dos aparecen en el hardware.
| En software | En electrónica digital |
|---|---|
throttle: ejecutar el primer evento y ignorar los siguientes durante un tiempo | La ventana de ignorar por software: respuesta inmediata, cambios posteriores descartados |
debounce: esperar a que se calme la ráfaga y reaccionar al final | El filtro RC + Schmitt: el cambio se confirma cuando la señal se estabilizó |
| Clics múltiples en un botón lento: el usuario insiste | Los rebotes: el contacto «insiste» hasta asentarse |
| Valor por defecto de un parámetro ausente | La resistencia de pull: qué lee la entrada cuando nadie escribe |
| Operación idempotente (hacerla dos veces no cambia nada) | El latch SR: los rebotes repiten «set» pero el estado ya es 1 |
| Sondeo (polling) frente a eventos | Leer el botón cada ms frente a una interrupción por flanco |
| Histéresis para evitar parpadeo (umbral de subida distinto de bajada) | El Schmitt trigger: dos umbrales distintos |
| Una máquina de estados con temporizador | Pulsación corta, larga y repetición |
- 1Rebote = ráfaga de eventos duplicados.
Un solo gesto genera muchos eventos casi iguales; hay que decidir cuándo son «el mismo».
- 2Ventana de software = throttle.
Reacciona al primer flanco y descarta los siguientes. Rápido, pero se confía en que el primer flanco es real.
- 3RC + Schmitt = debounce.
Espera a que la señal se calme. Más lento, pero filtra picos aislados que el throttle aceptaría como un primer evento.
10 · Comprensión
Confusiones frecuentes
«Un botón sin resistencia anda igual»
Anda a veces: suelto, la entrada flota y capta ruido, así que lee 0 o 1 sin que nadie toque nada. Hace falta una resistencia de pull (o activar el pull interno).
«Un pull-up de 1 MΩ gasta menos, así que es mejor»
Gasta menos, pero es una antena: el ruido y la fuga de la entrada alcanzan para falsear el nivel. El pull de 10 kΩ es el punto de equilibrio habitual, y encima solo consume mientras se pulsa.
«Mi botón no rebota»
Todos rebotan, incluso los caros. Si tu contador anda sin filtro, algo ya filtra por vos: un condensador cercano, una puerta lenta, o un muestreo espaciado. Con un osciloscopio vas a ver la ráfaga.
«Un capacitor más grande es siempre mejor»
Filtra más, pero agrega retardo y además hace más lenta la respuesta a pulsaciones rápidas. Con un filtro RC sin resistencia en serie, descargarlo a través del contacto genera un pico de corriente que desgasta el botón. Ajustá τ al rebote medido.
«El RC solo alcanza, el Schmitt sobra»
Una rampa lenta en una entrada común pasa lentamente por la zona prohibida y puede dar flancos dobles u oscilaciones. El Schmitt, con sus dos umbrales, asegura un solo flanco limpio.
«Con delay(50) en la interrupción resuelvo todo»
Bloquear dentro de una interrupción detiene todo lo demás, y en muchos micros delay() ni siquiera funciona ahí dentro. Mejor sondear con un temporizador o anotar la hora y filtrar fuera de la interrupción.
«Una ventana de 5 ms sirve para todos los botones»
Depende del rebote de cada botón. Hay que medirlo (o ser conservador con 20 a 50 ms). Una ventana menor que el rebote deja pasar pulsaciones falsas.
11 · Práctica guiada
Ejercicios de botones
Ejercicio 1: elegir el RC
Medís un rebote de 15 ms. Elegí R y C para que τ sea del mismo orden. ¿Sirven 10 kΩ y 1,5 µF? ¿Cuánto tarda en verse el cambio al pulsar y al soltar (umbrales al 30 % y 55 % de VCC)?
Ver solución razonada
τ = 10 kΩ × 1,5 µF = 15 ms, del mismo orden que el rebote (el 1,5 µF existe en la serie E6). El Schmitt ve la bajada tras ≈ 1,2 τ = 18 ms y la subida tras ≈ 0,8 τ = 12 ms. Es un retardo aceptable para un botón humano (el límite perceptible está en las decenas de ms). Verificalo con el Paso 6.
Ejercicio 2: el antirrebote con dos NAND
Dibujá un conmutador SPDT con dos NAND cruzadas y dos pull-ups. Explicá por qué los rebotes no alteran la salida. ¿Por qué no sirve un pulsador común de dos terminales?
Ver solución razonada
Cada contacto va a una entrada del latch (con su pull-up). Al tocar «set», ~S = 0 y Q = 1; si rebota y se separa, ~S vuelve a 1, el latch queda en (1, 1) y mantiene Q = 1. Solo cambia al tocar el contacto opuesto. Un pulsador de dos terminales no tiene el segundo contacto: al soltarse, la entrada «reset» quedaría flotando, y no habría forma de ordenarle al latch volver a 0.
Ejercicio 3: medir tu botón
Con un osciloscopio sobre un botón con pull-up, configurá el disparo en flanco de bajada, modo único, y presioná 5 veces. ¿Qué anotás de cada captura? ¿Cómo elegís la ventana de software?
Ver solución razonada
Anotá, por captura, la duración total del rebote (del primer flanco a la última transición) y la cantidad de flancos. Típicamente son entre 1 y 20 ms y de 3 a 15 flancos, con mucha variación entre pulsaciones. La ventana se elige tomando el peor caso medido y sumando margen (por ejemplo, el doble). No uses el dato de la hoja de datos como único criterio: cada botón tiene su firma y el desgaste la empeora.
Ejercicio 4: elegir el pull-up
Un botón a 2 metros de la placa, en un ambiente con motores, funciona a 3,3 V. ¿Qué valor de pull-up elegís y cuánto consume pulsado? ¿Y si el aparato corre con una batería y el botón se usa pocas veces por día?
Ver solución razonada
Un cable largo y ruidoso pide una entrada firme: 4,7 kΩ, que consume 3,3 V / 4,7 kΩ ≈ 0,7 mA solo mientras se pulsa. Para la batería, el consumo promedio del pull es despreciable (el botón rara vez está apretado), así que no hay razón de subirlo; lo que pesa es el consumo del resto. Se suma una resistencia en serie de unos 220 Ω y un capacitor de 100 nF cerca del pin.
Ejercicio 5: ventana mínima en software
Medís un rebote máximo de 12 ms. Tu programa lee el botón cada 1 ms. ¿Qué ventana elegís y qué pasa con dos pulsaciones muy rápidas? Verificá con debounce().
Ver solución razonada
Una ventana de 20 ms (≥ 12 ms con margen). Dos pulsaciones separadas por menos de 20 ms se tratan como una; para una persona es imposible (un dedo no pulsa a más de unas 10 a 15 veces por segundo, es decir, cada 70 ms o más), así que no se pierde nada. Con el Paso 3 podés probar ventanas de 5, 10 y 20 ms.
Ejercicio 6: cuatro botones en un puerto
Cuatro botones se leen juntos en los bits de un puerto, una vez por milisegundo. ¿Cómo sabés cuáles cambiaron desde la lectura anterior con una sola operación, y cuáles se acaban de pulsar (con pull-up, activos en bajo)?
Ver solución razonada
cambio = nuevo ^ anterior marca con 1 los botones que cambiaron (Tema 10). Los que se acaban de pulsar son los que cambiaron y ahora valen 0: pulsados = cambio & ~nuevo. Los que se soltaron: cambio & nuevo. Para filtrar cada botón, se mantiene un registro de historial por bit, o un contador por bit que se reinicia con cada cambio.
12 · Síntesis
Ideas para recordar
- Una entrada que nadie maneja flota: se le da un valor por defecto con una resistencia de pull. La convención es botón a masa con pull-up de 10 kΩ (0,5 mA, solo mientras se pulsa).
- Un contacto mecánico rebota: de décimas de milisegundo a decenas de milisegundos, tanto al cerrar como al abrir.
- Si el circuito cuenta flancos o reacciona a ellos, hay que filtrar; si solo muestra niveles, no se nota.
- RC + Schmitt (74HC14): τ = R·C del orden del rebote; el Schmitt, con dos umbrales, entrega un flanco limpio. El costo es un retardo de ≈ 1 τ.
- Latch SR con dos NAND y un conmutador SPDT: el primer contacto fija el estado y los rebotes lo mantienen.
- Software: ventana de ignorar (no bloqueante), registro de muestras o máquina de estados. La ventana debe ser ≥ al rebote medido, típicamente 20 a 50 ms.
- Ventana de ignorar ≈ throttle; RC + Schmitt ≈ debounce. Todos los botones rebotan: medí el tuyo.
¿Listo para el Tema 12?
Podés avanzar si sos capaz de responder esto sin mirar:
- Dibujar un botón con pull-up y decir qué lee la entrada suelto y pulsado.
- Calcular la corriente de un pull-up de 4,7 kΩ a 3,3 V.
- Explicar por qué un contador por flancos suma de más y un LED no lo nota.
- Elegir R y C para un rebote de 10 ms y decir qué retardo agregan.
- Explicar para qué sirven los dos umbrales de un Schmitt trigger.
- Describir cómo dos NAND cruzadas ignoran los rebotes.
- Elegir una ventana de software y justificarla con una medición.
Ya sabés dominar una entrada rebelde. Ahora hay que conectar todo en una placa de verdad: en el próximo tema, chips 74HCxx en la mesa de trabajo.