Arduino základy – 7. Blikání bez funkce Delay
Občas se stane, že v programu potřebujete dělat dvě věci najednou. Chcete třeba blikat LED a zároveň číst stlačení tlačítka či jiný vstup. V takovém případě nemůžete použít delay(), protože ta úplně zastaví chod celého programu – ten by stisk tlačítka nakonec ani nemusel zaznamenat. Ve sketchi níže je ukázáno, jak blkat LED bez užití výše zmíněného příkazu. Pokud se změní stav LED (zapnuto <-> vypnuto), je zaznamenýn čas této změny. V cyklu loop() se vždy kontroluje, jestli od poslední změny již uplynula požadovaná doba. Pokud ano, je opět změněn stav LED.
Obsah článku:
Potřebný hardware
- Arduino
- LED
- 330 Ohm rezistor
Obvod
Anodu LED (delší nožička) připojíme přes 330 ohm rezistor na pin 13. Katodu (kratší nožička) připojíme ke GND. V dalším kroku připojíme Arduino k PC a naprogramujeme jej.
Schéma
Kód
Znázorněný kód užívá funkci millis() – příkaz, který vrací uplynulý čas v milisekundách od okamžiku spuštění aktuálního programu – pomocí které bliká vaší LED.
/* Bliknutí bez Delay (Blink without Delay) Vypíná a zapíná LED připojenou k digitálnímu pinu, a to bez použití funkce delay(). Ve výsledku to znamená, že program může být současně spuštěný, aniž by byl přerušen čekáním mezi bliky LED. Obvod: * LED připojena mezi pinem 13 a zemí. * Poznámka: na většině Arduin je k pinu 13 LED již připojena, v takovém případě není potřeba žádný další hardware. vytvořeno 2005 autorem David A. Mellis upraveno 8. února 2010 Paul Stoffregen http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay */ // konstanty užité k nastavení čísel pinů se nezmění : const int ledPin = 13; // číslo pinu LED // Proměnné se změní: int ledState = LOW; // ledState použité k nastavení LED unsigned long previousMillis = 0; // bude ukládat poslední případ aktualizace LED // následné proměnné jsou „long“, jelikož čas měřený v milisekundách // se rychle změní na vyšší číslo, jež může být uloženo v int. unsigned long interval = 1000; //interval, ve kterém bude LED blikat (milisekundy) void setup() { // nastavte digitální pin jako výstup: pinMode(ledPin, OUTPUT); } void loop(){ // Sem vložíte kód, jež se bude neustále opakovat // zkontrolujte, zda-li je správný čas na bliknutí LED; tj. zda // je rozdíl mezi aktuálním časem a časem posledního bliknutí LED // delší než interval ve kterém chcete, aby vaše // LED blikala. unsigned long currentMillis = millis(); if(currentMillis - previousMillis > interval) { // uložte, kdy jste naposledy blikli LED previousMillis = currentMillis; // jestliže je LED vypnutá, zapněte ji a naopak: if (ledState == LOW) ledState = HIGH; else ledState = LOW; // nastavte LED s ledState proměnné: digitalWrite(ledPin, ledState); } }
Pokud byste chtěli zajít ještě dál a řešit i případy, kdy Arduinu „dojdou bity“ na zobrazení aktuálního času, přečtěte si tento článek [EN].
Přeloženo z https://arduino.cc/en/Reference/HomePage a mírně upraveno.
- Teleball: Arduino programování pro retro nadšence - 12.10.2015
- Arduino třídička barevných bonbonů - 30.9.2015
- Výškový meteorologický balon s čichem pro atmosféru - 18.9.2015
zlamalik
2.9.2017 at 9:47Ty promenne jsou typu unsigned, ty se do zaporu nikdy nedostanou. To currentMillis – previousMillis bude fungovat spravne i po preteceni.
Zbyšek Voda
2.9.2017 at 16:08Máte pravdu, že se do záporu nedostanou.
Také ale není pravda, že bude vše fungovat správně i po přetečení.
Norma definuje, že pokud je rozdíl dvou unsigned čísel záporný, je výsledek ono záporné číslo modulu číslo o jedno větší, než je největší zobrazitelné číslo v daném typu.
(řeší třeba zde: https://stackoverflow.com/questions/7221409/is-unsigned-integer-subtraction-defined-behavior)
Takže jestli se nepletu, bude se potom výsledek pohybovat u horní hranice unsigned long, což taky nechcete.
Nejlepší řešení podle mě popisuje tenhle článek: https://www.baldengineer.com/arduino-how-do-you-reset-millis.html
Georgio
19.2.2021 at 9:17Zdravím,
moc jsem to z toho odkazu “ https://www.baldengineer.com“ nepochopil. Google mi to přeložil, jako že se to potom začne odčítat. Nechápu jak.
Po přetečení previousMillis bude větší než aktuální čas. Unsigned zařídí, že i tak bude výsledek kladný. Bude to víc než interval, takže program pojede dál a nastaví se správné previousMillis.
Má dotaz ohledně arduina. Pokud nebude v programu žádné delay, nepojede to pořád na 100%? Výdrží to procesor?
xTom01
23.9.2016 at 15:17Proč to dělat tak složitě? Tohle je mnohem jednodušší a navíc odolné proti přetečení millis():
int led = 13;
int interval = 500;
void setup() {
pinMode(led, OUTPUT);
}
void loop() {
if((millis()/interval)%2) {
digitalWrite(led,HIGH);
} else {
digitalWrite(led,LOW);
}
}
V podstatě jde o to, že uplynulý čas v millis() to rozseká na úseky dély interval a v každém sudém dioda svítí a v lichém ne (nebo obráceně, to už je jedno).
Ivan
13.10.2016 at 13:59Ten loop je nesmysl, to by ti jen každých 500ms problikla ledka na jednu ms 🙂
Mirexx
24.8.2016 at 12:32Robiť z komára slona? Blikať 50 dní určite nikto nepotrebuje, ide o ukážku, ked blikať necháme maximalne niekloko minut aby sme sa presvedčili o správnosti programu a potešili sa z blikania.
Zbyšek Voda
24.8.2016 at 14:32Ve většině případů opravdu není potřeba, aby LED blikala 50 dnů. Jde spíš o to, aby uživatelé věděli o tom, že u millis() k přetečení dochází.
Představte si, že si někdo postaví například jednoduché ovládání kotle – jenom ON/OFF v určitém čase. A zrovna v programu použije funkci millis(). 49. den odjede na dovolenou, 50. den millis přeteče a kotel mu vesele topí další dva týdny, protože nevěděl, že millis() přetéká.
Vím, že je to prkotina, ale je dobré o této věci vědět…
damec
11.2.2015 at 15:03Dobrý den, jsem začátečník a tak mi není jasné jak tohle zafunguje pokud během daného intervalu přeteče počítadlo milisekund. Prosím, pokud tam bude chyba, poraďte jak jí odstranit. Díky :-).
Zbyšek Voda
11.2.2015 at 19:52Funkce millis() přeteče asi po 50 dnech (přesněji po 4294967295 milisekundách běhu programu). Jestli se nepletu, tak je teď program napsaný tak, že se po těch 50 dnech blikání zastaví. CurrentMillis – previousMillis bude totiž potom prakticky pořád záporné.
Řešením by tedy mohlo být odchycení právě té doby, kdy je millis() blízko hodnoty 4294967295. Jestli si to dobře představuji, tak by stačilo na konec loopu přidat
if(currentMillis > 4294967000){
previousMillis = 0;
}
Na přechodu bude malá odchylka, ale v řádek desetin sekund. To je ale oproti celkové odchylce způsobené zdržením při vykonávání programu (cca 1ms na 1s) docela zanedbatelné. V případě nutnosti přesnějšího měření bych radil spíše použití RTC, nebo přijímače z atomových hodin.
Jinak tento problém také řeší zde