2.2 Debounce tipke
Cilj ovog primjera je pokazati ti kako pritiske tipke učiniti pouzdanijima i dosljednijima pomoću tehnike koja se zove debounce.
Kada pritisnemo fizičku tipku, njezini kontakti mogu brzo odskakivati, zbog čega mikrokontroler detektira više pritisaka umjesto jednog.
U ovom primjeru koristit ćemo softverski debounce pomoću time.ticks_ms() kako bismo osigurali da jedan pritisak znači jedno prebacivanje LED-ice.
U ovoj dokumentaciji naučit ćeš:
- Kako pročitati stanje tipke pomoću
value(). - Što je debounce tipke i kako ga riješiti pomoću
time.ticks_ms(). - Zašto
time.ticks_diff()postoji i kada ga moraš koristiti. - Kako prebacivati LED-icu pri svakom pritisku tipke.
Potreban hardver:
- 1x Soldered NULA MINI pločica
- 1x Breadboard
- 1x Tipka
- 1x LED-ica (bilo koje boje)
- 1x otpornik od 330 ohma
- 5x Jumper žice
- 1x USB-C kabel
Spajanje komponenti
Prati osam koraka ispod. Svaka fotografija snimljena je s istog položaja, pa je možeš usporediti s prethodnom i vidjeti točno što se promijenilo.
1. Umetni NULA MINI pločicu na breadboard
Utisni pločicu u jedan kraj breadboarda tako da njezina dva reda pinova sjednu s obje strane središnjeg kanala, s čipom okrenutim prema dolje. Pritisni je ravnomjerno dok svi pinovi ne sjednu.

Ovaj primjer koristi tri pina. Imena su ispisana duž oba ruba pločice, a svaki pin sjedi u svom numeriranom redu. Na fotografiji pločica zauzima redove 25 do 30, što stavlja IO19 u red 27 na a–e strani, te IO4 u red 27 i GND u red 30 na f–j strani.
2. Postavi tipku
Utisni tipku u sredinu breadboarda tako da premosti središnji kanal, nekoliko redova dalje od pločice. Na fotografiji su njezine nožice u redovima 15 i 17.

3. Spoji GND na tipku
Masa ide u dva skoka, koristeći plavu − vodilicu duž ruba breadboarda kao dijeljenu liniju mase.
- Prva jumper žica: iz reda u kojem je GND (red 30 na fotografiji) do plave − vodilice.
- Druga jumper žica: s te iste plave − vodilice natrag u red 15, na f–j strani tipke.

4. Spoji IO19 na tipku
Povuci jednu jumper žicu iz reda u kojem je IO19 (red 27 na a–e strani) u red 17, na a–e strani tipke.

Dvije žice dolaze do tipke s suprotnih strana: red 15 na f–j strani, red 17 na a–e strani. To je dijagonalni par, i to je ono što tipku čini stvarnim prekidačem. Tipka je sada gotova; ostatak ove stranice je LED-ica.
5. Izvedi IO4 i dodaj otpornik
LED-icu pobuđuje IO4, koji sjedi u redu 27 na f–j strani pločice, istoj strani kao GND i suprotnoj strani od IO19.
- Pločica prekriva većinu tog reda, pa koristi krajnju rupicu, j27, i povuci jumper žicu do reda 7.
- Zatim postavi otpornik od 330 Ω tako da premosti red 7 i red 5.

6. Dodaj LED-icu
LED-ica ide sljedeća, nastavljajući lanac: duga nožica (anoda) u red 5, isti red u kojem otpornik završava, i kratka nožica (katoda) u red 3.

7. Spoji LED-icu na masu
Jedna jumper žica zatvara krug: iz reda 3, gdje sjedi kratka nožica LED-ice, do plave − vodilice, iste vodilice koju tipka već koristi za masu.

Cijeli put je sada IO4 → 330 Ω → LED-ica → − vodilica → GND. Budući da je vodilica dijeljena, tipka i LED-ica masu uzimaju s istog mjesta.
8. Spoji pločicu na računalo

Stvaranje dvaju pinova
Ovaj primjer treba po jedan pin u svakom smjeru, i oba se stvaraju na istom mjestu na vrhu datoteke. Tipka čita, pa dobiva Pin.IN zajedno s Pin.PULL_UP koji si sreo u prethodnom primjeru. LED-ica piše, pa dobiva Pin.OUT.
btn = Pin(BUTTON_PIN, Pin.IN, Pin.PULL_UP)
led = Pin(LED_PIN, Pin.OUT)
LED-icu također izričito gasimo prije nego petlja počne:
led.value(0)
False. Ako je pin slučajno ostavljen u visokom stanju od prethodno pokrenute skripte, ono što vidiš i ono što program vjeruje ne bi se poklapalo, i prvi pritisak bi izgledao kao da ne radi ništa.Čitanje stanja tipke
value() čita pin i vraća ili 1 (visoko) ili 0 (nisko). Čitamo ga jednom po prolazu kroz petlju i odgovor držimo u varijabli:
reading = btn.value()
Pin.PULL_UP uključuje otpornik unutar čipa koji drži pin na 3.3 V, pa pin čita 1 dok je tipka otpuštena. Pritisak tipke spaja pin na GND, pa čita 0. Tipka spojena na ovaj način naziva se active low, i zato kod ispod pazi na promjenu s 1 na 0.Debounce tipke
Kada pritisneš tipku, ona se mijenja iz otvorenog u zatvoreni krug, što tretiramo kao jedan pritisak. Nama se čini kao jedna čista radnja, jer reagiramo sporo u odnosu na elektroniku. Ali mikrokontroleru, koji stanje tipke provjerava tisućama puta u tom kratkom trenutku, izgleda vrlo drugačije.
Dok se tipka pritišće, dva metalna kontakta unutra dotiču se međusobno. Budući da nisu savršeno glatki ni poravnati, mogu se brzo spojiti i razdvojiti nekoliko puta prije nego se umire. Mikrokontroleru to izgleda kao da je tipka pritisnuta nekoliko puta u nizu, iako si je pritisnuo samo jednom.

Najjednostavnija softverska strategija za rješavanje ovoga je debouncer zasnovan na kašnjenju. Cijela mu je svrha pričekati da prozor odskakivanja prođe, a da bismo to mogli, moramo znati koliko je vremena prošlo.
time.ticks_ms() vraća brojač milisekundi koji pločica vodi dok je upaljena. Usporedba dvije takve vrijednosti govori nam koliko je davno nešto bilo, pa možemo ignorirati svaku promjenu koja dođe prerano nakon prethodne.
now = time.ticks_ms()
if reading != last_state and time.ticks_diff(now, last_change_ms) > DEBOUNCE_MS:
last_change_ms = now
time.ticks_diff(), nikada oduzimanjem. Brojač koji vraća time.ticks_ms() ne raste unedogled. Dosegne granicu i prelije se natrag na mali broj. Kada se to dogodi, now - last_change_ms daje veliki negativan odgovor i debouncer prestaje raditi dok se pločica ne restarta. time.ticks_diff(now, last_change_ms) zna za to prelijevanje i vraća pravu razliku. To je jedno mjesto gdje je MicroPython stroži od Arduino funkcije millis() na koju si možda navikao.Prebacivanje LED-ice
Svaki put kada se detektira valjani pritisak, preokrenemo stanje LED-ice. Ovaj kod sjedi unutar debouncera, pa se izvršava samo za pritiske koji su preživjeli filtar.
if last_state == 1 and reading == 0:
# Preokrećemo led_state. Operator "not" pretvara True u False i False u True.
led_state = not led_state
# Palimo ili gasimo LED-icu ovisno o trenutnom led_state.
if led_state:
led.value(1)
else:
led.value(0)
print("Button pressed, toggling LED")
last_state = reading
last_state == 1 and reading == 0 je ono što ovo pokreće jednom po pritisku, a ne jednom po otpuštanju. Istinit je samo u točnom trenutku kada se vrijednost mijenja iz otpuštene u pritisnutu, što se naziva padajući brid. Puštanje tipke mijenja vrijednost natrag s 0 na 1, što debouncer vidi, ali ovaj uvjet ignorira.Kod
Ispod je cijeli kod primjera za prebacivanje LED-ice tipkom uz debounce.
from machine import Pin
import time
# Ovo je varijabla kojoj predajemo broj pina na koji smo spojili TIPKU.
# NULA pločica ima logiku imenovanja pinova ovako: IO19, gdje je 19 broj koji dajemo varijabli.
BUTTON_PIN = 19
# Ovo je varijabla kojoj predajemo broj pina na koji smo spojili LED-icu.
# Zapamti da LED-ici treba otpornik od 330 ohma u seriji s njom. Taj otpornik ograničava koliko struje teče,
# a bez njega LED-ica vuče više nego što su ona ili pin građeni izdržati, pa se oboje može oštetiti.
LED_PIN = 4
# Ovdje stvaramo naša dva Pin objekta. Tipka je postavljena na Pin.IN jer je čitamo, a Pin.PULL_UP uključuje
# otpornik unutar čipa koji veže pin na 3.3V dok je tipka otpuštena. LED-ica je postavljena na
# Pin.OUT jer u nju pišemo.
btn = Pin(BUTTON_PIN, Pin.IN, Pin.PULL_UP)
led = Pin(LED_PIN, Pin.OUT)
# Ovo su varijable koje se koriste za debounce tipke.
# "led_state" pamti je li LED-ica trenutno upaljena, "last_state" pamti što je tipka pokazala kad smo
# zadnji put pogledali, a "last_change_ms" pamti kada se ta vrijednost zadnji put promijenila.
led_state = False
last_state = 1
last_change_ms = 0
DEBOUNCE_MS = 25
# Pobrini se da LED-ica počne ugašena, tako da se stanje koje pamtimo poklapa s onim što stvarno vidiš.
led.value(0)
while True:
# value() je funkcija koja čita vrijednost s našeg pina, ili 1 (visoko) ili 0 (nisko).
reading = btn.value()
# time.ticks_ms() vraća broj milisekundi koje su prošle od uključivanja pločice. Taj brojač
# se na kraju prelije natrag na nulu, i zato nikada ne uspoređujemo dva takva broja direktno i
# uvijek koristimo time.ticks_diff() da izračunamo razliku između njih.
now = time.ticks_ms()
# Ovo je naša debounce logika: provjeravamo je li se vrijednost tipke promijenila i je li prošlo dovoljno
# vremena da ne dobijemo lažna čitanja zbog šuma u signalu.
if reading != last_state and time.ticks_diff(now, last_change_ms) > DEBOUNCE_MS:
last_change_ms = now
# Budući da za čitanje tipke koristimo pull-up metodu, vrijednosti su obrnute od onoga
# što bi mogao očekivati: pin čita visoko dok je tipka otpuštena, a nisko dok je pritisnuta.
# Zato LED-icu moramo prebaciti kada stanje tipke ide s visokog na nisko.
if last_state == 1 and reading == 0:
# Preokrećemo led_state. Operator "not" pretvara True u False i False u True.
led_state = not led_state
# Palimo ili gasimo LED-icu ovisno o trenutnom led_state.
if led_state:
led.value(1)
else:
led.value(0)
print("Button pressed, toggling LED")
last_state = reading
# Vrlo kratka pauza ostavlja procesoru trenutak da obavi svoj posao u pozadini.
time.sleep_ms(10)
Što bi trebao vidjeti
Pritisni Run. Isprva se ne događa ništa. LED-ica počinje ugašena. Pritisni tipku jednom i upalit će se, pritisni je ponovno i ugasit će se. Svaki prihvaćeni pritisak također ispisuje liniju u Shell, pa možeš vidjeti debouncer u radu:
Button pressed, toggling LED
Button pressed, toggling LED
Button pressed, toggling LED

last_state == 1 and reading == 0 koji radi svoj posao, a ne debouncer: istinit je samo u onom jednom prolazu gdje se vrijednost mijenja iz otpuštene u pritisnutu. Ono što debouncer dodaje u odnosu na 2.1 Brojač pritisaka tipke je to što nikada ne blokira. 2.1 čeka u petlji da pustiš tipku, pa pločica ne može raditi ništa drugo dok je tipka pritisnuta; ova verzija pročita tipku, odluči i nastavi, a to je ono što svakom programu koji ima drugog posla stvarno treba.Cijeli primjer
Pogledaj cijeli kod primjera na linku ispod:
2.2_Button_Debounce.py
Primjer koji prebacuje LED-icu pritiskom tipke uz debounce.