Czech English

6. Tlačítko podruhé

aneb stiskem zapni, stiskem vypni

V dnešní lekci s vrátíme k tlačítku a podíváme se na něj podrobněji. I taková to na první pohled jednoduchá součástka může pěkně pozlobit, pokud nevíme co všechno se může dít uvnitř.


Zapojení

Pro dnešní programování využijeme zapojení z lekce 3. LED budeme mít připojenu na výstup 2 a tlačítko na vstup 5 (s využitím vnitřního pull-up rezistoru).
Schéma připojení led a tlačítka
Schéma připojení led a tlačítka
Zapojení realizované na kontaktním poli
Zapojení realizované na kontaktním poli

Program

Úkolem je napsat program, kdy každým stisknutím tlačítka se změní stav led. Tedy po prvním stisknutí tlačítka se led rozsvítí, po druhém zhasne, po třetím rozsvítí, atd.
První co nás asi napadne je následující řešení:
Bohužel moc nefunguje. Problém je v tom, že když držíme stisknuté tlačítko, podmínka je splněná a stále dochází k přepínaní stavu led zapnuto/vypnuto, a to tak rychle, že to ani nepozorujeme. Proto když tlačítko uvolníme, zůstane led buď rozsvícená nebo zhasnutá náhodně. Vyrobili jsme tak generátor náhody jako když házíme mincí.

Logické funkce

Proto musíme upravit podmínku tak, abychom nezjišťovali, že je tlačítko stisknuté, ale že právě došlo k jeho stisknutí, tzn. změně stavu. Tomu se odborně říká, že reagujeme na hranu (změnu) signálu a ne na úroveň (stav).
Abychom mohli zjistit změnu stavu signálu musíme si zapamatovat jeho předchozí hodnotu a tu porovnat s aktuálním stavem.
Pro tvorbu složitějších podmínek můžeme s výhodou použít logické funkce. Ty fungují podobně jako operace v matematice, ale pracují jen s logickými hodnotami 0 a 1.
Negace (opak, inverze)
!x      ... vrátí opačnou hodnotu než má x ( 0 -> 1 , 1 -> 0 )
Logický součin (a, a zároveň)
x && y  ... výsledkem je 1 pokud jsou hodnoty x a y 1
            pokud je alespoň jedna z hodnot x, y 0, pak je výsledkem 0
Logický součet (nebo)
x || y  ... výsledkem je 1 pokud alespoň jedna z hodnot x a y je 1
            pokud jsou obě hodnoty x a y 0, pak je výsledkem 0
A nyní již řešení úlohy, kde reagujeme na okamžik stisknutí tlačítka, tedy změnu vstupního signálu z 1 (klidový stav) na 0 (aktivní stav).
Průběh napětí po stisknutí tlačítka
Průběh napětí po stisknutí tlačítka
Toto řešení je již v principu správné. Ovšem praktickým ověřením zjistíme, že funkčnost programu oproti prvnímu řešení není o moc lepší.
Problém je v tom, že tlačítko je elektromechanický prvek i s mnoha jeho necnostmi. Největším problémem je tzv. zakmitávání.
Po stisknutí tlačítka nedojde k jednorázovému sepnutí kontaktu, ale k několikerému sepnutí a rozepnutí kontaktu (mechanické kmitání). Výsledek je takový, jako bychom stiskli tlačítko několikrát rychle za sebou (celý děj trvá až několik ms).

Ošetření zákmitů (Debouncing)

Tento problém lze řešit úpravou zapojení (zařazení filtru) nebo úpravou programu. Princip programového řešení je jednoduchý, po změně hodnoty signálu po určitou dobu další změny ignorujeme (nereagujeme na ně). Tento čas volíme delší než přechodový děj (dobu zákmitu). Závisí na mechanickém provedení tlačítka a nejednodušeji jej určíme experimentálně.
switch3.ino
#define LED        2
#define TLACITKO   5
// casova prodleva v niz ignorujeme zmenu stavu tlacitka
#define PRODLEVA   50

bool  MinulyStav = 1;               // priznak predchoziho stavu tlacitka (0 .. stisknuto)
unsigned long CasZmeny;             // promenna pro ulozeni casu zmeny stavu tlacitka

void setup()
{   
  pinMode(LED, OUTPUT);             // nastaveni pinu 2 jako vystup
  pinMode(TLACITKO, INPUT_PULLUP);  // nastaveni pinu 5 jako vstup se zapnutym pull-upem
}

void loop()
{
  if (digitalRead(TLACITKO) == 0)           // je-li tlacitko stisknuto 
  {                                                  
    if (MinulyStav == 1 && millis() - CasZmeny > PRODLEVA) // neni-li nastaven priznak         
    {                                          // tlacitko stisknuto a uplynul-li vetsi cas  
                                               // od zmeny stavu tlacitka nez je mez (50ms)
      MinulyStav = 0;                             // nastav priznak tlacitko stisknuto
      digitalWrite(LED, !digitalRead(LED));       // zmen hodnotu vystupu LED
    }
  }
  else                                      // neni-li stisknuto tlacitko
  {
    CasZmeny = millis();                       // zapamatuj si posledni cas, kdy bylo nestisknuto
    MinulyStav = 1;                            // nuluj priznak, tlacitko stisknuto
  }

}

Úkoly na samostatnou práci

1. Upravte program tak, aby bylo možné tlačítkem ovládat blikání led. Po stisknutí tlačítka začne led blikat s periodou 1s. Po dalším stisknutí tlačítka led ihned zhasne. Po dalším stisku opět začne led blikat, atd.