3.11. Debouncing¶
Uma chave é desenhada como um contato perfeito de aberto ou fechado, mas os contatos de uma chave real não alternam de forma limpa entre os dois estados. Eles trepidam – fazem e desfazem o contato elétrico muitas vezes em poucos milissegundos antes de estabilizar. Uma entrada GPIO lendo o pino enxerga isso como uma rajada de bordas; um laço de polling descuidado conta vários “toques” para um único toque real, e um manipulador de interrupção é executado várias vezes por toque efetivo.
Uma chave que trepida produz uma rajada de transições rápidas antes de estabilizar.¶
Debouncing é a prática de filtrar a trepidação para que cada toque físico seja registrado como um único evento. Duas abordagens resolvem isso – software (uma regra de temporização no firmware) ou hardware (um pequeno filtro no fio). Elas não são mutuamente exclusivas.
3.11.1. Debouncing por software¶
A ideia é lembrar quando a entrada mudou pela última vez e rejeitar mudanças adicionais dentro de uma janela curta a partir desse instante. A trepidação de contato geralmente dura menos de 10 ms; um toque real leva de 50 a 100 ms; uma janela de 30 a 50 ms captura todas as trepidações sem bloquear toques reais.
Em um laço de polling, leia o pino, compare com o último valor estável e só aceite uma mudança depois que a janela de debounce tiver decorrido:
import time
from machine import Pin
button = Pin("P0", Pin.IN, Pin.PULL_UP)
last_state = 1
last_change = 0
DEBOUNCE_MS = 50
while True:
now = time.ticks_ms()
state = button.value()
if state != last_state and time.ticks_diff(now, last_change) > DEBOUNCE_MS:
last_change = now
last_state = state
if state == 0:
do_action()
time.sleep_ms(10)
Para leituras orientadas a interrupção, aplique a mesma regra de temporização dentro do manipulador e então repasse o toque real ao contexto principal via micropython.schedule() (veja Entrada GPIO):
import time
import micropython
from machine import Pin
button = Pin("P0", Pin.IN, Pin.PULL_UP)
last_irq = 0
DEBOUNCE_MS = 50
def handle_press(pin):
do_action()
def on_press(pin):
global last_irq
now = time.ticks_ms()
if time.ticks_diff(now, last_irq) < DEBOUNCE_MS:
return
last_irq = now
micropython.schedule(handle_press, pin)
button.irq(handler=on_press, trigger=Pin.IRQ_FALLING)
A ISR filtra trepidações pelo timestamp e enfileira o callback; handle_press é executado de volta no contexto principal, onde alocação e I/O lenta são seguros.
3.11.2. Debouncing por hardware¶
O debouncing por hardware filtra a trepidação eletricamente, antes que ela sequer chegue ao pino. A ferramenta padrão é um capacitor.
Um capacitor é um componente de dois terminais que armazena carga elétrica. Fisicamente, são duas placas condutoras mantidas a uma curta distância uma da outra, separadas por um isolante (o dielétrico).
Um capacitor de placas paralelas: dois condutores separados por uma camada isolante.¶
Aplicar uma tensão entre seus terminais força cargas iguais e opostas para as duas placas; a relação é
Q = C × V
onde Q é a carga armazenada (coulombs), V é a tensão sobre o capacitor e C é sua capacitância (farads). A capacitância é fixada pela construção do dispositivo; mais capacitância significa mais carga armazenada na mesma tensão.
A consequência: um capacitor não pode mudar sua tensão instantaneamente. A carga que entra ou sai precisa passar por qualquer resistência que houver no caminho, e essa resistência determina a velocidade com que a tensão pode mudar.
3.11.2.1. Constante de tempo RC¶
Carregar um capacitor através de um resistor produz uma subida exponencial suave em direção à tensão de alimentação, não um degrau. O tempo característico dessa subida é a constante de tempo RC:
τ = R × C
Após um τ, o capacitor atinge cerca de 63 % da tensão de alimentação. Após 5 τ, está acima de 99 % – “totalmente carregado” para fins práticos.
Um capacitor carrega ao longo de uma curva exponencial. τ = RC é o tempo para atingir 63 % da tensão final.¶
A descarga através de um resistor segue a imagem espelhada: a tensão cai exponencialmente a partir de seu valor inicial em direção a zero, caindo para 37 % da tensão inicial após um τ e para menos de 1 % após 5 τ.
Um capacitor descarrega ao longo de um decaimento exponencial. τ = RC é o tempo para cair a 37 % da tensão inicial.¶
3.11.2.2. O circuito de debounce¶
Um capacitor entre um pino de entrada e o terra, alimentado através de um resistor em série, forma um filtro passa-baixas. Picos rápidos não têm tempo de carregar ou descarregar o capacitor através desse resistor; o pino permanece próximo da tensão em que estava antes do pico. Mudanças lentas – um toque deliberado – carregam ou descarregam o capacitor e a leitura acompanha.
R1 puxa o lado alto da chave para Vcc, produzindo um sinal bruto da chave que trepida. R2 e C então filtram esse sinal por passa-baixas até o pino:
Debouncing por hardware: R2 e C filtram por passa-baixas o sinal bruto da chave antes que ele chegue ao pino.¶
Valores típicos: R1 = 10 kΩ (pull-up), R2 = 10 kΩ (série), C = 100 nF.
Quando a chave está aberta, a corrente flui Vcc → R1 → R2 → capacitor (em série), carregando o capacitor até Vcc com τ_charge = (R1 + R2) × C = 2 ms.
Quando a chave fecha, o nó da chave é fixado ao terra e o capacitor se descarrega através de R2 somente para esse terra com τ_discharge = R2 × C = 1 ms.
Ambas as bordas são filtradas por RC. Como o capacitor fica em seu próprio nó, a jusante de R2 a partir da chave, ele oscila de forma limpa entre Vcc (aberta) e 0 V (fechada) – nenhuma corrente precisa fluir através de R1 em regime permanente em qualquer um dos casos.
3.11.3. Escolhendo entre eles¶
Software é o padrão. Não custa nada em componentes, o limiar é fácil de ajustar e funciona em qualquer pino que a CPU leia.
Hardware vale as peças quando a trepidação alcança algo além do código de polling da CPU – uma interrupção que não pode disparar em dobro, um contador de hardware, um periférico sem filtro próprio.
Debouncing por software e por hardware também coexistem pacificamente: um pequeno filtro RC suprime os piores picos, e uma janela de debounce por software cobre o que restar.