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.

An idealised scope trace showing a switch input signal. The signal starts high (open switch), drops low, bounces back and forth several times within a few milliseconds, then settles low (closed switch).

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).

A capacitor drawn as two parallel horizontal plates with a dielectric (insulator) between them. A lead connects each plate to an external terminal -- A on top, B on bottom. Equal and opposite charges +Q and -Q accumulate on the two plates when a voltage V is applied across the terminals.

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.

A graph showing a capacitor's voltage rising along an exponential curve from 0 V toward the supply rail. The time τ = RC is marked on the x-axis where the curve reaches 63 % of the supply voltage.

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 τ.

A graph showing a capacitor's voltage falling along an exponential curve from Vmax toward 0 V. The time τ = RC is marked on the x-axis where the curve drops to 37 % of the starting voltage.

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:

A switch input with hardware debouncing. Vcc connects through a 10 kΩ pull-up resistor down to a junction. That junction connects to ground through the switch on one branch, and through a 10 kΩ series resistor to the Pin on the other branch. A 100 nF capacitor between Pin and ground completes the low-pass filter.

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 (pull-up), R2 = 10 (série), C = 100 nF.

Quando a chave está aberta, a corrente flui Vcc → R1R2 → 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.