mylms

... web o elektronice


Kusy kódu k Arduinu

Síla Arduina tkví v nesčetném množství knihoven, pomocí kterých i absolutní neprogramátor dokáže poskládat funkční program – tzv. lepič kódu :) Knihovny jsou však vytvářeny univerzálně, aby bylo jejich použití co nejjednodušší. Použití knihovny je většinou vyváženo jejich větší velikostí. Takže i jednoduchý program může být pomalejší a zbytečně zabírat mnoho paměti – to může být problém např. i Arduina Nano, Micro apod.

arduino-ide-9

Tento článek bude pojat podobně jako článek Reléové obvody. Je to tedy pro úplné začátečníky. Postupně bych rád přidával části kódu, které si myslím, že jsou zajímavé.

arduino-ide-2

Arduino



Kusy kódu

 

Čekání bez příkazu delay(); Multitasking na Arduinu

V začátcích s Arduinem se často používá příkaz delay();. Tento příkaz má jednu obrovskou nevýhodu. Kromě generování PWM a sériové komunikace komplet zastaví provádění programu. Jeho použité nemusí být nesprávné, pokud není potřeba např. hlídat stavy tlačítek, vykreslovat na displej atd.
Mnohem elegantnější je zjistit si aktuální čas pomocí příkazu millis() přičíst si k času požadované zpoždění a pomocí příkazu IF kontrolovat, jestli již nastal správný čas. Program tedy bude provádět ostatní příkazy a pokud nastane čas, kdy se má příkaz provést zavolá se funkce která příkaz provede. Tímto programátorským stylem se částečně vyhnete vytváření špagetového kódu protože budete nuceni kód rozdělit do jednotlivých funkcí. Každou funkci můžeme samostatně ladit, aniž bychom zasáhli do ostatních funkcí v programu.

Tímto kódem lze vytvořit takový jakoby „multitasking“. Máme více funkcí (např. hlídání tlačítek, vykreslování na displej, čtení času, měření teploty) a každé této funkci můžeme přidělit čas, po kterém se má obnovovat. Nejdůležitější jsou tlačítka – funkci kontroly stisknutého tlačítka můžeme spouštět každých např. 50 ms. Čtení času nám může stačit jednou za půl minuty a čtení teploty ze snímače jednou za minutu. Displej vykreslíme pouze pokud nastane nějaká změna teploty, nebo času. Program tedy nečeká neustále na příkazu delay(), ale aktivně hlídá tlačítka a jednou za čas zkontroluje čas a teplotu.

Je třeba upozornit, že pokud budeme chtít spouštět funkci např. jednou za 100 ms a ostatní části programu se vykonávají déle, bude funkce vykonána v nejbližší možný čas – až na ní přijde řada. Proto je nutné porovnávat pomocí znaménka „>=“. Pokud by zde bylo „==“ a v přesném čase by program nebyl na řádku s podmínkou, tak by se podmínka nevykonala. Kód tedy nefunguje jako přerušení, které pozastaví ostatní části programu a spustí konkrétní funkci.

 

long time1; //proměnná s uloženým časem posledního vyvolání funkce podprogram1 
long time2; //proměnná s uloženým časem posledního vyvolání funkce podprogram2

void setup() {
//pokud zde nebude přiřazení hodnoty do proměnné, tak se obě funkce pustí ihned po spuštění MCU a pak dále po nastaveném čase
//pokud zde přiřazení proměnné bude, spustí se funkce až po nastaveném čase
time1 = millis(); //ulož čas provedení podprogramu 1
time2 = millis(); //ulož čas provedení podprogramu 2
}

void loop() {
if (millis() >= time1 + 1000) {
 //pokud je čas od poslední změny větší, nebo roven 1000 ms, proveď příkazy
 time1 = millis(); //ulož čas provedení podprogramu 1
 Podprogram1(); //spusť funkci Podprogram1
}

if (millis() >= time2 + 5000) {
 //pokud je čas od poslední změny větší, nebo roven 5000 ms, proveď příkazy
 time2 = millis(); //ulož čas provedení podprogramu 2
 Podprogram2(); //spusť funkci Podprogram2
}

//další části programu, které běží i když se čeká na spuštění funkcí
}

void Podprogram1() {
//tato funkce se spustí jednou za 1000 ms
}

void Podprogram2() {
//tato funkce se spustí jednou za 5000 ms
}

 

 

 

Hlídání sepnutí/vypnutí (náběžné/sestupné hrany) vstupu/proměnné

Pod tímto krkolomným názvem se skrývá úplně primitivní funkce, která nám zajistí, že např. po stisknutí tlačítka bude provedena určitá funkce pouze jednou. Tato část kódu jednoduše hlídá změnu bool proměnné z 0 na 1, nebo naopak z 1 na 0. V případě změny se spustí definovaná funkce.
Protože se kód v programu cyklicky spouští budeme hlídat změnu proměnné mezi minulým a aktuálním cyklem. Řekněme, že hlídáme změnu proměnné mezi prvním a druhým cyklem. V prvním cyklu se program spustí. Načte se proměnná na vstupu 11 (LOW) např. do proměnné presentInput11. Zkontroluje se jestli je oproti minulému cyklu změna (není, protože program byl právě spuštěn) a proměnná se zkopíruje do proměnné lastInput11. V druhém cyklu došlo ke změně. Na vstupu 11 je HIGH. Tato hodnota se opět uloží do proměnné presentInput11. Znovu se zkontroluje, jestli nastala proti minulému cyklu změna – porovnají se proměnné presentInput11 a lastInput11. Pokud nejsou stejné, tak nastala změna oproti minulému cyklu. Zkontroluje se hodnota presentInput11. Pokud je HIGH, tak je jasné, že nastala náběžná hrana (předtím bylo LOW, teď je HIGH), pokud LOW, je detekována sestupná hrana. Spustí se příslušná funkce – třeba rozsvícení LEDky na výstupu 13.

V příkladu níže jsou detekovány náběžné hrany na vstupech 11 a 12. Pokud je detekována náběžná hrana (stisk tlačítka) na vstupu 11 je výstup 13 (LEDka) aktivován, pokud je detekována náběžná hrana (stisk tlačítka) na vstupu 12 je výstup 13 deaktivován. Kvůli možnosti zákmitů při stisku tlačítka je dobré tuto funkci (vše v loop()) nevolat v každém cyklu, ale např. jednou za 50 ms. To lze elegantně provést pomocí příkladu výše.

 

bool lastInput11; //proměnná s minulým stavem vstupu 11 
bool lastInput12; //proměnná s minulým stavem vstupu 12
bool presentInput11; //proměnná s aktuálním stavem vstupu 11
bool presentInput12; //proměnná s aktuálním stavem vstupu 12

void setup() {
pinMode(11, INPUT); //vstup 1 (tlačítko ON)
pinMode(12, INPUT); //vstup 2 (tlačítko OFF)
pinMode(13, OUTPUT); //výstup (signalizační LED)
}

void loop() {
//načtení vstupů do proměnných
presentInput11 = digitalRead(11);
presentInput12 = digitalRead(12);

if (presentInput11 != lastInput11) {
  //aktuální a minulý stav tlačítka není stejný
  //bylo stisknuto, nebo povoleno tlačítko na pinu 11
  if (presentInput11 == HIGH) {
   //aktuální stav je HIGH, tlačítko je stisknuto
   //je detekována náběžná hrana
   digitalWrite(13, HIGH); //zapnout LED na pinu 13
  }
  else {
   //aktuální stav je LOW, tlačítko je povoleno
   //je detekována sestupná hrana
  }
}

if (presentInput12 != lastInput12) {
  //aktální a minulý stav tlačítka není stejný
  //bylo stisknuto, nebo povoleno tlačítko na pinu 12
  if (presentInput12 == HIGH) {
   //aktuální stav je HIGH, tlačítko je stisknuto
   //je detekována náběžná hrana
   digitalWrite(13, LOW); //vypnout LED na pinu 13
  }
 }

lastInput11 = presentInput11; //uložení aktuálního stavu tlačítka do minulého stavu
lastInput12 = presentInput12; //uložení aktuálního stavu tlačítka do minulého stavu
}

 

Jednoduché uložení a načtení času z modulu reálného času DS3231 

Na internetu lze najít spoustu příkladů k modulu DS3231. Většina z nich (zejména českých) používá při načítání a ukládání času do modulu reálného času pointery. To může být pro začátečníka velice matoucí a nakonec degraduje pouze k jednoduchému překopírování kódu. Přitom je jejich použitá v tomto případě zcela zbytečné.

kusykodukarduinu-1

RTC modul DS3231

 

Na začátku programu se definuje adresa modulu a několik proměnných do kterých se budou ukládat jednotlivá data. Protože jsou proměnné definovány hned na začátku programu jsou globální – lze je použít v celém programu.

Následuje funkce SetRtc, které se předají jako parametr jednotlivé hodnoty. Ty jsou pomocnou funkcí převedeny na číslo ve formátu BCD.
Další funkce GetRtc (bez návratové hodnoty) načte z modulu reálných hodin čas, převede ho pomocí pomocné funkce do dekadického formátu a uloží do jednotlivých proměnných.

Nastavit a přečíst čas pak lze jednoduše zavoláním funkce.

Kód je podobný jako u všech ostatních příkladů. Ale všimněte si, že se u názvu proměnných nepoužívají symboly * a &, které značí práci s pointery.

 

//RTC DS3231 #define DS3231_I2C_ADDRESS 0x68 //adresa modulu 
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; //globální proměnné

//Nastavení hodin reálného času
void SetRtc(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) {
 Wire.beginTransmission(DS3231_I2C_ADDRESS);
 Wire.write(0); //nastvit do prvního registru nulu
 Wire.write(decToBcd(second)); //nastavit sekundy
 Wire.write(decToBcd(minute)); //nastavit minuty
 Wire.write(decToBcd(hour)); //nastavit hodiny
 Wire.write(decToBcd(dayOfWeek)); //nastavit den v týdnu (1=neděle, 2=pondělí)
 Wire.write(decToBcd(dayOfMonth)); //nastavit den v měsíci
 Wire.write(decToBcd(month)); //nastavit měsíc
 Wire.write(decToBcd(year)); //nastavit rok
 Wire.endTransmission();
}

//Přečtení hodin reálného času
void GetRtc() {
 Wire.beginTransmission(DS3231_I2C_ADDRESS);
 Wire.write(0); //zapsat nulu
 Wire.endTransmission();

 Wire.requestFrom(DS3231_I2C_ADDRESS, 7); //požadavek na 7 bajtů od modulu RTC
 second = bcdToDec(Wire.read() & 0x7f);
 minute = bcdToDec(Wire.read());
 hour = bcdToDec(Wire.read() & 0x3f);
 dayOfWeek = bcdToDec(Wire.read());
 dayOfMonth = bcdToDec(Wire.read());
 month = bcdToDec(Wire.read());
 year = bcdToDec(Wire.read());
}

// Konverze Dec na BCD
byte decToBcd(byte val) {
 return((val / 10 * 16) + (val % 10));
}

// Konverze BCD na Dec
byte bcdToDec(byte val) {
 return((val / 16 * 10) + (val % 16));
}

//POUŽITÍ JEDNOTLIVÝCH FUNKCÍ
//Nastavení času
SetRtc(0, 30, 12, 1, 5, 11, 17); //nastavení hodin na 5.11.2017 (neděle) na 12:30:00

//Načtení hodin (funkce nevrací hodnotu, ale nastavuje přímo proměnné)
GetRtc();

 

 

 



Napsal Petan před čtyřmi měsíci v kategorii . Připojeno 0 komentářů.
Přečteno 745x.

Na programy zde poskytované není žádná záruka na funkčnost (viz licence). Jednotlivé články, stejně jako celý obsah stránek není návodem a slouží pouze k studijním účelům. Zapojení výše mají pouze informativní charakter! Vždy se řiďte originálním návodem k použití! Na elektrickém (vyhrazeném) zařízení smí pracovat pouze osoba s příslušnou kvalifikací dle vyhlášky 50/78 Sb! Vše tedy děláte na vlastní nebezpečí! Autor stránek nebere žádnou zodpovědnost za případné újmy na zdraví, životě, majetku a jiné!

Jakékoliv části webu je zakázáno bez svolení autora a uvedení zdroje publikovat! Některé části článků mohou obsahovat texty, případně obrázky ze stránek Wikipedia a Wikimedia Commons. Tyto části jsou dostupné pod původní licencí Creative Commons.



Doposud nebyl připojen žádný komentář. Buďte první!

Připojte váš komentář!

* Hvězdičkou jsou označena povinná pole. Autor stránek odpovídá vždy do komentáře, ne na přiložený mail!