LEDky & Tlačítka
NSWI170, 2026, Labs 03
Jáchym Bártík
Čas je relativní
- Neměli bychom čekat, že událost nastane v nějakém čase
constexpr int PERIOD = 10;
int lastTime = 0;
void loop() {
int currentTime = millis();
if (currentTime - lastTime == PERIOD) { // Don't do this!
lastTime = currentTime;
doSomething();
}
}
- Při velké zátěži může jeden běh
loop trvat
více milisekund
- Např.
millis() vrací: 1, 3, 4, 6, 9, 11, ...
if (currentTime - lastTime >= PERIOD) { // Don't do this either!
...
}
- Nyní máme jistotu, že čas nebude přeskočen
- Průměrná perioda ale bude větší, než 10 ms
Čas je relativní
constexpr int PERIOD = 10;
int lastTime = 0;
void setup() {
lastTime = millis(); // Don't forget this!
}
void loop() {
int currentTime = millis();
if (currentTime - lastTime >= PERIOD) { // Do this instead
lastTime += PERIOD;
doSomething();
}
}
- Ideální řešení
- Čas nebude nikdy přeskočen
- V dlouhodobém průměru se limitně blížíme k 10 ms
Pravidlo #3 - DRY
- Don't Repeat Yourself
- Vše je napsáno jen jednou
- Rychlejší vývoj, snadnější změny
- Lepší kontrola chyb, optimalizace
- Větší přehlednost
- Nástroje - funkce, konstanty, smyčky, objekty, ...
- Čemu se vyhnout:
- Sjednocování příliš různorodého kódu
- Horší rozšiřitelnost, plno if-else podmínek
- Zbytečně moc abstrakcí
KISS
- Keep It Simple, Stupid
- Jednodušší systémy jsou lepší, než složitější
- Rychlejší vývoj, snadnější změny
- Lepší kontrola chyb, optimalizace
- Větší přehlednost
- Může však být v rozporu s velmi DRY kódem
- Abstrakce, "obecná" řešení, ...
- Úspěsná kombinace obou principů je skutečným uměním
Cvičení #1
- Vytvořte funkci, která zobrazí číslo na LEDkách
- Modulo 16
- Převedení do binární soustavy
- Zobrazení bitů pomocí čtyř LED
-
Hint: binární operátory
& (and), | (or), ~ (negation)
<<, >> (left shift, right shift)
7 & 2 // 0111 & 0010 -> 0010 = 2
7 | 8 // 0111 | 1000 -> 1111 = 15
2 << 1 // 0010 -> 0100 = 4
3 << 2 // 0011 -> 1100 = 12
Cvičení #2
- Vytvořte čítač, který se periodicky inkrementuje
- Hodnotu vypisujte pomocí funkce ze cvičení #1
- Modulo není nutně potřeba - stejně jde jen o poslední čtyři bity
Tlačítka
- Nastavení pinu pro čtení
- Je to tak sice defaultně, ale stejně by se to mělo dělat kvůli čitelnosti
void setup() {
pinMode(BUTTON1_PIN, INPUT);
}
- Čtení je podobné jako zapisování
- Pokud funkce vrátí
ON, tlačítko je zapnuté
void loop() {
bool isPressed = digitalRead(BUTTON1_PIN) == ON;
}
Cvičení #3
- Zařiďte, aby LEDka svítila právě tehdy, když je stisknuté tlačítko
- Bonus: Druhé tlačítko přepíná, která LEDka to bude
#include <funshield.h>
void setup() {
pinMode(LED1_PIN, OUTPUT);
pinMode(BUTTON1_PIN, INPUT);
}
void loop() {
digitalWrite(LED1_PIN, digitalRead(BUTTON1_PIN));
}
Cvičení #4
- Vytvořte čítač, který se periodicky inkrementuje pokud je stisknuto tlačítko
- Vyjděte z úlohy #2
- Čas se měří jen pokud je stisknuto tlačítko
- Pozor, reálný čas samozřejmě běží pořád
Cvičení #5
- Vytvořte čítač, který se inkrementuje stisknutím tlačítka
- Hodnota se časem (sama) nijak nemění
- Při stisknutí tlačítka se zvýší o 1
- Je nutné rozlišit, ve kterém průběhu
loop bylo tlačítko stisknuto, tj. kdy digitalRead poprvé vrátila false
- Bonus: Debounce
- Tlačítko může oscilovat, neboli několikrát rychle za sebou změnit stav
- Řešení - po každém stisknutí jej na chvíli zablokujte
- Vyzkoušejte různé délky blokačního intervalu
Cvičení #6
- Přidejte další tlačítka
- Jedno dekrementuje čítač
- Tady už bude nutné použít modulo
x = (x + 15) % 16
- Druhé jej zvýší o 5
- Všechna tři tlačítka pracují se stejným čítačem
- Všimněte si, že všechna tlačítka dělají v podstatě to samé
- Zaměřte se na odstranění duplicitní logiky
- Dále si můžete všimnout, že už je tu neprakticky moc proměnných ...
Lepší řešení
- Cílem je oddělit logiku tlačítek od logiky počítání času a dalších částí programu
- K tomu se hodí funkce
- Ale vyžadují předávání příliš mnoha proměnných
- Navíc neoddělují data, pouze funkcionality
- Struktury:
struct Button {
int pin;
bool isPressed;
};
void processButton(Button &button) {
if (digitalRead(button.pin) == ON && !button.isPressed)
... // Handle the click event
}
Button buttons[3];
processButton(buttons[0]);
- Navíc mohou obsahovat i funkce
- Kombinují data s funkcionalitami, které se jich týkají
- Pro každý prvek lze specifikovat viditelnost
public vs. private
- Defaultně je vše
public
- Třídy - úplně stejné jako struktury, akorát že defaultně je vše
private
class Button { // This class does exactly the same thing as the previous structure
public:
int pin;
bool isPressed;
};
- Používejte výhradně třídy
- Struktury jsou specifikem C/C++
- Jinde se nepoužívají
- Proto mohou být dost matoucí
Domácí úkol #3
- Vytvořte tlačítka, která inkrementují čítač jak spojitě, tak diskrétně
- Tlačítko inkrementuje čítač ihned po stisknutí
- Zároveň, pokud jej držíte dostatečně dlouho, začne samo periodicky inkrementovat čítač