Skip to main content

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 funkcije millis() 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 digitalRead().
  • Što je debounce tipke i kako ga riješiti pomoću millis().
  • 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
ℹ️
Dio kruga s tipkom isti je kao u 2.1 Brojač pritisaka tipke. Ako ti je taj primjer još sastavljen, ostavi ga spojenog i preskoči na korak 5. Sve prije toga je ponavljanje.

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

ℹ️
Ovaj korak pretpostavlja da znaš kako je breadboard spojen iznutra i što su njegove naponske vodilice (power rails). Za uvod pogledaj stranicu dokumentacije Osnove breadboarda.

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.

NULA MINI pločica postavljena na breadboard
Korak 1: pločica postavljena na breadboard

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.

ℹ️
Tvoja pločica može sjediti u drugim redovima od one na fotografiji. Važno je u koji red sleti koji pin. Čitaj imena ispisana na pločici, a ne prepisuj brojeve.

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.

Tipka postavljena preko središnjeg kanala
Korak 2: tipka premošćuje središnji kanal
⚠️
Tipka ima četiri nožice, ali samo dvije su važne. Dvije nožice na istoj strani tipke trajno su spojene unutar nje. Pritisak tipke spaja jednu stranu s drugom. Zato uvijek koristi jednu nožicu s obje strane središnjeg kanala, jednu u pojasu a–e i jednu u pojasu f–j. Ako uzmeš obje žice s iste strane, te dvije su već spojene i pločica će se ponašati kao da je tipka zauvijek pritisnuta.

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.
Dvije jumper žice spajaju GND na tipku preko negativne vodilice
Korak 3: GND dolazi do tipke preko plave − vodilice
ℹ️
Obje žice moraju ići u isti stupac vodilice, onaj koji ide uz plavu liniju. Vodilica uz crvenu liniju je zaseban pojas i nije spojena s njom.

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.

Jumper žica spaja IO19 na lijevu stranu tipke
Korak 4: IO19 spojen na drugu stranu 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.

⚠️
IO4 i IO19 su oba u redu 27, na suprotnim stranama središnjeg kanala. Potpuno su odvojeni. Sve spajanje LED-ice odvija se na f–j strani.
  • 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.
Jumper žica s IO4 i otpornik od 330 ohma na mjestu
Korak 5: IO4 izveden u red 7, s otpornikom od 330 Ω koji premošćuje redove 7 i 5
ℹ️
Otpornik nema polaritet, pa nije važno kojim je krajem okrenut. Redovi su samo ono što fotografija slučajno koristi. Bilo koji slobodni redovi rade, dokle god otpornik i LED-ica završe serijski spojeni između IO4 i mase.

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.

LED-ica dodana, anoda prema otporniku i katoda prema masi
Korak 6: LED-ica na mjestu, duga nožica prema otporniku
⚠️
Postavi LED-icu na pravu stranu. LED-ica propušta struju samo u jednom smjeru. Duga nožica mora biti okrenuta prema otporniku i IO4, a kratka nožica prema masi. Okrenuta obrnuto, jednostavno se neće upaliti. Ništa se neće pokvariti, ali se ni ništa neće dogoditi.

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.

Jumper žica spaja katodu LED-ice na negativnu vodilicu
Korak 7: kratka nožica LED-ice spojena na plavu − vodilicu

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

Gotov krug napajan preko USB-C
Korak 8: gotov krug, napajan preko USB-C

Čitanje stanja tipke

Funkcija digitalRead(pin) čita vrijednost sa zadanog digitalnog pina i kao rezultat vraća ili HIGH ili LOW. Tu funkciju pozivamo unutar loop():


void loop() {

//digitalRead() je funkcija koja čita vrijednost sa zadanog digitalnog pina, ili HIGH ili LOW.
bool reading = digitalRead(BUTTON_PIN);
}
ℹ️
Vrijednost je obrnuta od onoga što bi mogao očekivati. INPUT_PULLUP uključuje otpornik unutar čipa koji drži pin na 3.3 V, pa pin čita HIGH dok je tipka otpuštena. Pritisak tipke spaja pin na GND, pa čita LOW. Tipka spojena na ovaj način naziva se active low, i zato kod ispod pazi na promjenu s HIGH na LOW.

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.

Debounce tipke vizualiziran
Vizualizacija debounce tehnike

Najjednostavnija softverska strategija za rješavanje ovoga je debouncer zasnovan na kašnjenju. Cijela mu je svrha pričekati da prozor odskakivanja prođe. Tu millis() dolazi u pomoć.

Funkcija millis() vraća broj milisekundi koje su prošle od kada je pločica počela izvršavati trenutni program. Možemo je koristiti da provjerimo je li prošlo dovoljno vremena između posljednje promjene stanja tipke i trenutnog vremena; ako jest, spremni smo za sljedeći pritisak, ako nije, čekaj još malo.


Prebacivanje LED-ice

Svaki put kada se detektira valjani pritisak, stanje LED-ice preokrenemo jednostavnim prebacivanjem. Ovaj kod smješten je unutar filtra debouncera.

if (lastState == HIGH && reading == LOW) {

//Preokrećemo ledState
ledState = !ledState;

//Palimo ili gasimo LED-icu ovisno o trenutnom ledState
if (ledState == true) {
digitalWrite(LED_PIN, HIGH);
} else {
digitalWrite(LED_PIN, LOW);
}
}
lastState = reading;

Kod

Ispod je cijeli kod primjera za prebacivanje LED-ice tipkom uz debounce. Koristi funkciju millis() da provjeri je li prošlo dovoljno vremena između promjena stanja tipke.

/*
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.
Ako želiš koristiti drugi pin, pobrini se da koristiš pin označen s IO__.
*/
const int BUTTON_PIN = 19;

/*
Ovo je varijabla kojoj predajemo broj pina na koji smo spojili LED-icu.
NULA pločica ima logiku imenovanja pinova ovako: IO4, gdje je 4 broj koji dajemo varijabli.
Ako želiš koristiti drugi pin, pobrini se da koristiš pin označen s IO__.

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.
*/
const int LED_PIN = 4;

/*
Ovo su varijable koje se koriste za debounce tipke.
*/
bool ledState = false;
bool lastState = HIGH;
unsigned long lastChangeMs = 0;
const unsigned long debounceMs = 25;

void setup() {

/*
pinMode() je funkcija koja konfigurira zadani pin da se ponaša ili kao ulaz ili u ovom slučaju kao izlaz.
To jednostavno znači da pin "sluša" dostupne podatke kada je u ulaznom modu, a "piše" podatke kada je u izlaznom modu.
Budući da naš pin mora otkriti je li tipka pritisnuta, pin ćemo staviti u INPUT_PULLUP mod.
INPUT_PULLUP uključuje mali otpornik unutar čipa koji nježno veže pin na 3.3V, pa pin čita HIGH
dok je tipka otpuštena, a tipka ga mora samo spojiti na GND da bi čitao LOW.
*/
pinMode(BUTTON_PIN, INPUT_PULLUP);

/*
pinMode() je funkcija koja konfigurira zadani pin da se ponaša ili kao ulaz ili u ovom slučaju kao izlaz.
To jednostavno znači da pin "sluša" dostupne podatke kada je u ulaznom modu, a "piše" podatke kada je u izlaznom modu.
Budući da naš pin mora upaliti LED-icu, pin ćemo staviti u OUTPUT mod.
*/
pinMode(LED_PIN, OUTPUT);
}

void loop() {

//digitalRead() je funkcija koja čita vrijednost sa zadanog digitalnog pina, ili HIGH ili LOW.
bool reading = digitalRead(BUTTON_PIN);

/*
millis() je funkcija koja vraća broj milisekundi koje su prošle od kada je pločica počela izvršavati trenutni program.
U ovom primjeru tu funkciju koristimo da provjerimo je li period debouncea završio. Taj broj će se preliti
(vratiti na nulu) nakon približno 50 dana.
*/
unsigned long now = millis();

/*
Ovo je naša logika debouncea: provjeravamo je li tipka pritisnuta i je li prošlo dovoljno vremena da ne dobijemo
lažna čitanja zbog šuma u signalu.
*/
if (reading != lastState && (now - lastChangeMs) > debounceMs) {
lastChangeMs = now;

/*
Budući da za spajanje tipke koristimo pull-up metodu, vrijednosti su obrnute od onoga što bi
mogao očekivati: pin čita HIGH dok je tipka otpuštena a LOW dok je pritisnuta. Zato LED-icu moramo
prebaciti kada stanje tipke ide s HIGH na LOW.
*/
if (lastState == HIGH && reading == LOW) {

//Preokrećemo ledState
ledState = !ledState;

//Palimo ili gasimo LED-icu ovisno o trenutnom ledState
if (ledState == true) {
digitalWrite(LED_PIN, HIGH);
} else {
digitalWrite(LED_PIN, LOW);
}
}
lastState = reading;
}
}

Što bi trebao vidjeti

Prenesi skicu. 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.

LED-ica upaljena nakon pritiska tipke
Jedan pritisak, jedno prebacivanje
ℹ️
LED-ica se ne pali dok držiš tipku. Prebacuje se jednom po pritisku. To je cijela svrha debouncera: bez njega bi jedan pritisak preokrenuo LED-icu nekoliko puta i ostavio je u onom stanju u kojem bi slučajno završila.

Cijeli primjer

Pogledaj cijeli kod primjera na linku ispod:

2.2_Button_Debounce.ino

Primjer koji prebacuje LED-icu pritiskom tipke uz debounce.