3.11. Debouncing

Sebuah sakelar digambarkan sebagai kontak terbuka-atau-tertutup yang sempurna, namun kontak sakelar nyata tidak berpindah dengan bersih antara kedua kondisi tersebut. Kontak itu bergetar -- membuat dan memutus kontak listrik berkali-kali dalam beberapa milidetik sebelum stabil. Input GPIO yang membaca pin melihat hal itu sebagai serangkaian tepi; loop polling yang ceroboh menghitung beberapa "penekanan" untuk satu penekanan nyata, dan handler interupsi berjalan beberapa kali per penekanan aktual.

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

Sakelar yang memantul menghasilkan serangkaian transisi cepat sebelum stabil.

Debouncing adalah praktik memfilter getaran tersebut sehingga setiap penekanan fisik terdaftar sebagai satu peristiwa. Dua pendekatan menyelesaikan ini -- perangkat lunak (aturan waktu dalam firmware) atau perangkat keras (filter kecil pada kabel). Keduanya tidak saling eksklusif.

3.11.1. Debouncing perangkat lunak

Idenya adalah mengingat kapan input terakhir berubah dan menolak perubahan lebih lanjut dalam jendela waktu singkat dari timestamp tersebut. Getaran kontak biasanya berlangsung kurang dari 10 ms; penekanan nyata membutuhkan 50 -- 100 ms; jendela 30 -- 50 ms menangkap semua getaran tanpa menghalangi penekanan nyata.

Dalam loop polling, baca pin, bandingkan dengan nilai stabil terakhir, dan hanya terima perubahan setelah jendela debounce telah berlalu:

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)

Untuk pembacaan berbasis interupsi, terapkan aturan waktu yang sama di dalam handler, lalu serahkan penekanan nyata ke konteks utama melalui micropython.schedule() (lihat Input 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)

ISR memfilter getaran berdasarkan timestamp dan mengantrekan callback; handle_press berjalan kembali di konteks utama, di mana alokasi dan I/O lambat aman dilakukan.

3.11.2. Debouncing perangkat keras

Debouncing perangkat keras memfilter getaran secara elektris, sebelum mencapai pin. Alat standar adalah kapasitor.

Kapasitor adalah komponen dua terminal yang menyimpan muatan listrik. Secara fisik, kapasitor terdiri dari dua pelat konduktif yang ditempatkan berdekatan, dipisahkan oleh isolator (dielektrik).

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.

Kapasitor pelat paralel: dua konduktor yang dipisahkan oleh lapisan isolasi.

Menerapkan tegangan di terminal-terminalnya mendorong muatan yang sama dan berlawanan ke dua pelat; hubungannya adalah

Q = C × V

di mana Q adalah muatan yang tersimpan (coulomb), V adalah tegangan di kapasitor, dan C adalah kapasitansi-nya (farad). Kapasitansi ditentukan oleh konstruksi perangkat; kapasitansi lebih besar berarti lebih banyak muatan tersimpan pada tegangan yang sama.

Konsekuensinya: kapasitor tidak dapat mengubah tegangannya secara instan. Muatan yang mengalir masuk atau keluar harus melewati resistansi yang ada di jalurnya, dan resistansi itu menentukan seberapa cepat tegangan dapat berubah.

3.11.2.1. Konstanta waktu RC

Mengisi kapasitor melalui resistor menghasilkan kenaikan eksponensial yang mulus menuju tegangan sumber, bukan langkah tiba-tiba. Waktu karakteristik kenaikan tersebut adalah konstanta waktu RC:

τ = R × C

Setelah satu τ, kapasitor telah mencapai sekitar 63 % dari tegangan sumber. Setelah 5 τ, sudah lebih dari 99 % -- "terisi penuh" untuk keperluan praktis.

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.

Kapasitor terisi sepanjang kurva eksponensial. τ = RC adalah waktu untuk mencapai 63 % dari tegangan akhir.

Pengosongan melalui resistor mengikuti gambar cermin: tegangan turun secara eksponensial dari nilai awalnya menuju nol, turun ke 37 % dari tegangan awal setelah satu τ, dan ke bawah 1 % setelah 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.

Kapasitor mengosongkan diri sepanjang peluruhan eksponensial. τ = RC adalah waktu untuk turun ke 37 % dari tegangan awal.

3.11.2.2. Sirkuit debounce

Kapasitor antara pin input dan ground, yang diumpankan melalui resistor seri, membentuk filter lolos-rendah. Lonjakan cepat tidak punya waktu untuk mengisi atau mengosongkan kapasitor melalui resistor tersebut; pin tetap dekat dengan tegangan sebelum lonjakan. Perubahan lambat -- penekanan yang disengaja -- mengisi atau mengosongkan kapasitor dan pembacaan mengikutinya.

R1 menarik sisi tinggi sakelar ke Vcc, menghasilkan sinyal sakelar mentah yang memantul. R2 dan C kemudian memfilter sinyal tersebut dengan filter lolos-rendah ke pin:

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 perangkat keras: R2 dan C memfilter sinyal sakelar mentah dengan filter lolos-rendah sebelum mencapai pin.

Nilai tipikal: R1 = 10 (pull-up), R2 = 10 (seri), C = 100 nF.

Ketika sakelar terbuka, arus mengalir Vcc → R1R2 → kapasitor (secara seri), mengisi kapasitor ke Vcc dengan τ_charge = (R1 + R2) × C = 2 ms.

Ketika sakelar menutup, node sakelar diklem ke ground dan kapasitor mengosongkan diri melalui R2 saja ke ground tersebut dengan τ_discharge = R2 × C = 1 ms.

Kedua tepi difilter RC. Karena kapasitor berada di nodenya sendiri, di hilir R2 dari sakelar, kapasitor berayun bersih antara Vcc (terbuka) dan 0 V (tertutup) -- tidak ada arus yang harus mengalir melalui R1 pada kondisi tunak dalam kedua kasus.

3.11.3. Memilih di antara keduanya

  • Perangkat lunak adalah pilihan default. Tidak memerlukan komponen tambahan, ambang batas mudah disesuaikan, dan bekerja pada pin mana pun yang dibaca CPU.

  • Perangkat keras layak digunakan saat getaran mencapai sesuatu selain kode polling CPU -- interupsi yang tidak boleh terpicu ganda, counter perangkat keras, periferal tanpa filter sendiri.

Debouncing perangkat lunak dan perangkat keras juga dapat hidup berdampingan dengan damai: filter RC kecil menekan lonjakan terburuk, dan jendela debounce perangkat lunak menangani sisanya.