Lekce 32 - Arduino a časování procesů v programu

29.07.2014 19:47

Translate to English

  Tento tutoriál trošku navazuje na lekci 29 s tím, že se více zaměřuje na časování procesů. 

  V dnešní lekci se zaměříme trochu na teorii a řekneme si něco o tom jak načasovat jednotlivé procesy v programu tak, abychom nemuseli vůbec pracovat s funkcí delay(). Už se Vám určitě při programování stalo, že potřebujete zajistit, aby procesy které se opakují a společně nemají stejnou časovou základnu napsat naprogramovat nějak elegantně bez psaní složitého kódu. Já použiji příklad, který svou jednoduchostí jde napsat i pomocí funkce delay(), ale pro pochopení principu se bude hodit. 

    Mějme napsat program který bude blikat 3 LED diodami, ale každou v jiném intervalu. Interval pro první LED bude 1 vteřina, pro druhou 3 vteřiny a pro třetí to bude 5 vteřin. S použitím funkce delay() by to vypadalo takto:

    Já jsem tento masakr ani nedopsal, neboť abych dosáhl možnost opakování cyklu v loop(), musel bych zpracovat ještě 15 vteřin. Řešení není složité. Využijeme funkci millis(), která vrací počet milisekund od zapnutí nebo restartování Arduina a podle toto spočítáme, jestli se má část programu ve smyčce loop vykonat. Program si vlastně rozdělíme na několik částí, které se budou vykonávat v různých časových intervalech. Vypadá to takto:

Když odmažete komentáře, zjistíte, že kód není složitý. Důležité je pochopit jak program funguje. Při startu programu, po načtení knihoven, deklarování proměnných Ardunino projede celou funkci setup(), kde zadáváme nastavení. (např. nastavíme sériový port, inicializujeme LCD displej apod.) Tuto část Arduino po startu systému projede jen jednou. Funkce loop() naopak, když dojde na konec tak se opět vrátí na začátek a pořád tuto funkci projíždí v cyklech do nekonečna. 

     Když si tedy trošku rozebereme části v posledním napsaném kódu tak zjistíme, že jsme nejdříve deklarovali 3 proměnné a to 

  1. long lastJob1, lastJob2 = 0, lastJob3 = 0;

- tyto proměnné budeme využívat pro uchování času, kdy byla určitá část programu vykonána. Proč jsou 3? Protože máme 3 různé časové intervaly, ve kterých se bude něco vykonávat. V našem případě rozsvícení a zhasnutí LED diody. Proč je proměnná deklarována jako long? Protože budeme využívat funkci millis(), která vrací počet milisekund od startu Arduina a vrací právě 32 bitové číslo. Tedy integer (int) jelikož je pouze 16 bitové číslo tak by to při běhu programu nad 25 dní nestačilo. Prostě je to long. Co se stane, když počet milisekund bude tak velký, že se nevejde ani do čísla formátu long? Na to je Arduino připraveno a nastane to přibližně za 50 dní běhu programu. Proměnná tzv. přeteče a bude počítat opět od 0. 

  1. int LED1 = 9;
  2. int LED2 = 10;
  3. int LED3 = 11;

- nemusíme moc komentovat. Aby jsme nemuseli psát do funkce digitalWrite čísla pinů, na kterých máme připojeny LED diody, uložíme si čísla pinů do proměnných a místo čísel píšeme do kódu jen název proměnné. Je to výhodné taky z důvodu, že když změníme piny, na kterých máme LED diody, přepíšeme pouze tyto řádky. Delší možnost jak to udělat je pomocí klíčového slova #define. Pozor, potom ovšem už středník na konci není, nejedná se totiž o příkaz.

  1. #define LED1 9

Jako další máme funkci setup() ve které se neudělá nic jiného, než, že se nastaví piny s LED diodami jako výstupní a všechny po startu budou hned rozsvícené. Zajímat nás bude pouze funkce loop() ve které hned narazíme na první podmínku:

  1. if (millis() > (1000 + lastJob1))

Tato vyhodnocuje zdali se má tato část programu uvedená ve složených závorkách vykonat. Tak si to nasimulujeme. Hned po startu Arduina bude počet milisekund, který vrátí funkce millis() teoreticky 0. V proměnné lastJob1 máme také zatím uloženou 0. Příklad tedy zní 0 > 1000 + 0  - podmínka nění splněná a Arduino tento blok programu přeskočí. A jde dál. Dojde na konec funkce loop() a vrátí se na začátek jmenované funkce. A podmínku vyhodnotí znovu. Ale tato opět není splněna (já jsem si to změřil a Arduino funkci loop() v našem programu prošlo za 1,163 ms) a celé se to opakuje až najednou počet milisekund od startu je 1001 a podmínka pro vykonání našeho bloku je splněna. 1001 > 1000 + 0. Vykoná se toto:

  1. if (digitalRead(LED1) == 0) digitalWrite(LED1, HIGH);
  2.     else digitalWrite(LED1, LOW);
  3.     lastJob1 = millis();

První dva řádky říkáme Arduinu, že když je LED dioda zhaslá tak jí rozsviť a když ne tak jí zhasni. Program tedy vykonal naši práci v určitém intervalu, kterou jsme chtěli. Důležité je uložit čas, kdy se tak stalo. V našem případě to je zhruba 1001ms, kterou program uloží do proměnné lastJob1. A jede se dál. A vyhodnocování dalších bloků programu nepíši, jsou analogií tohoto bloku, pouze s jiným intervalem. Program tedy dál běhá v cyklu loop() až a čase 2002ms doběhne k naší podmínce, kde vyhodnotí že 2002 > (1000 + 1001)  a diodu opět zhasne, protože poslední čas provedení bloku programu byl uložen jako 1001ms v proměnné lastJob1. 

    Takto Arduino vyhodnocuje všechny 3 naše bloky a různých intervalech rozsvěcí a zhasíná LED diody. Samozřejmě bude spíše vykonávat jiné činnosti např. interval zobrazení nějaké hodnoty na displeji bude rozdílný od intervalu ukládání této hodnoty např. na SD kartu. Možnosti využití jsou zcela na Vás. Nezapomeňte, že za tyto bloky programu můžete napsat kód který nebude ovlivněn časem a vykoná se vždy, kdy Arduino ve smyčce loop() k němu dojde. Můžete i bloky programu s časovou podmínkou přidávat a mít jich teoreticky velmi mnoho. 

 

Zpět

Diskusní téma: Lekce 32 - Arduino a časování procesů v programu

Datum
Vložil
Titulek

modulo

Co použít modulo?

/* cycle time 500ms */
if (!(milis() % 500)) {
/* kod vykonany kazdych 500ms */
}

Datum
Vložil
Titulek

Preteceni mi nesedi

nejak mi to nesedi pri preteceni
pred pretecenim nam millis() vrati 4 294 967 001 predpokladam ze v lastJob1 mam 4 294 966 000
if (millis() > (1000 + lastJob1))
takze se to provede a na konci mam v lastJob1 4 294 967 001
po preteceni mam v millis() = 0

no a podminka v dalsim behu millis()> (1000 + lastJob1) uz proste nikdy nemuze byt splnena :( protoze 1000 + lastJob1 je vetsi nez unsigned long

leda ze by i ta hodnota v podmince pretekla... (je to tak?)

Datum
Vložil
Titulek

Nulování millis

To přetečení mi také vrtá hlavou. Tím se časy rozhodí.
Ale ono už samotné časování od krystalu má z dlouhodobého hlediska velkou chybu. Lze vůbec millis nějak nulovat?

Datum
Vložil
Titulek

Re: Nulování millis

K tomuto dojde jednou za zhruba 50 dní a jediná nevýhoda že interval bude kratší v době přetečení.
Nulovat millis() jde, ale není to jednoduchý proces.

Datum
Vložil
Titulek

Funkce millis() a datový typ

Neměly by být proměnné pro ukládání časů (lastJob*) teoreticky typu unsigned long místo long?

Po přetečení funkce millis a jejím vynulování by bylo asi také nutné vynulovat proměnné lastJob*. Jak se v praxi řeší takový stav, kdy dojde po nonstop provozu k vynulování millis?

Datum
Vložil
Titulek

Re: Funkce millis() a datový typ

Použití unsigned long je teoreticky správnější. Dostaneme hodnotu která bude dvojnásobná. Jak jsem již uvedl k přetečení dojde za 50 dní. Když to budeme používat např. k vyhodnocení nějakých hodnot třeba každých 5 minut, tak když dojde k přetečení do 0, tak výpočtem dojde že se má proces vykonat (např. lastJob = 58745555 + nějaký interval bude vždy větší jak přetečená hodnota millis(). Tím se proces vykoná a lastJob bude zase uložen jako aktuální millis(). Chyba bude jen v intervalu procesu, který se vykoná okamžitě po přetečení funkce millis() a tudíž bude kratší. Jednou za zhruba 50 dní je to chyba přijatelná. Samozřejmě by to šlo programově ošetřit, ale to není cílem toho návodu. Řešení by bylo použití také v tomto případě obvodu reálného času.

Datum
Vložil
Titulek

Re: Re: Funkce millis() a datový typ

Je několikrát zmiňováno, že k přetečení funkce millis() dojde každých 5 dnů. Nemá to být spíše každých zhruba 50 dnů?

Datum
Vložil
Titulek

Re: Re: Re: Funkce millis() a datový typ

Máte pravdu, v textu mám 50 dní. 50 dní je správné číslo. Tedy Opraveno :-)

Datum
Vložil
Titulek

Re: Re: Re: Re: Funkce millis() a datový typ

Pokial chceme casovat v desatinach, tak sa s unsigned long dostaneme na 13 rokov...

Datum
Vložil
Titulek

Re: Re: Re: Re: Re: Funkce millis() a datový typ

Nasel jsem lepsi zapis kde nenastane ani po preteceni casovy rozdil:

unsigned long currentMillis = millis();
if ((unsigned long)(currentMillis - previousMillis) >= interval) {
previousMillis = currentMillis;
}

vice na :
https://www.baldengineer.com/arduino-how-do-you-reset-millis.html

1 | 2 >>

Vyhledávání

arduino8.cz © 2015 Všechna práva vyhrazena.