Close

Užitečné funkce

Schéma propojení hrací kostky

V tomto díle si ukážeme, jak může Arduino vnímat čas. Podrobněji se podíváme na funkce čekání (delay) a další. Poté si ukážeme možnosti matematických operací a na závěr si představíme funkce pro generování náhodných čísel.

 

Čas

V základní výbavě mají desky Arduino čtyři funkce pro práci s časem. Jsou to funkce delay(), delayMicroseconds(), millis() a micros(). První dvě a druhé dvě fungují na stejném principu, jenom pracují s jinými jednotkami. Jsou to milisekundy a mikrosekundy. Důležité je si připomenout převodní vztah mezi jednotkami času kdy: 1 sekunda = 1 000 milisekund = 1 000 000 mikrosekund.

 

delay()

S touto funkcí jsme se již setkali. Má jediný parametr, a to čas čekání v milisekundách. Rozsah parametru je od 0 do 4,294,967,295. Velkou nevýhodou funkce delay i následující funkce delayMicroseconds() je fakt, že dojde k zastavení téměř veškeré činnosti (pozastavení čtení hodnot ze senzorů, nemožnost ovládat logické hodnoty na pinech atd.). Nedojde však k zastavení těch funkcí, které nejsou přímo závislé na procesoru. Jedná se zejména o příjem informací z Rx linky, kdy se přijatými byty naplňuje buffer a ke zpracování dojde až po skončení funkce delay a také o funkci analogWrite(). Generování PWM signálu totiž probíhá mimo hlavní blok procesoru.

int cekejSekund = 1;
long cekejMilisekund = cekejSekund*1000;

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  delay(cekejMilisekund);
  digitalWrite(13, HIGH);
  delay(cekejMilisekund);  
  digitalWrite(13, LOW);
}

 

delayMicroseconds()

Funkce je obdobná, jako delay(), jenom s tím rozdílem, že parametr je zde čas v mikrosekundách. Rozsah parametru je od 0 do 65,535.

 

millis()

Pomocí funkce millis() se dá zjistit hodnota uložená ve vnitřním časovači procesoru. Zde je uchována informace o délce běhu programu od jeho spuštění. Tato funkce tedy nepotřebuje žádný parametr a vrací počet milisekund od začátku programu. Tento počet však není nekonečný. Maximální vrácená hodnota je 4,294,967,295. Po překročení dojde k takzvanému přetečení časovače, který poté znovu začne počítat od nuly. Funkce millis() se využívá například tam, kde je třeba čekat, ale není žádoucí, aby byl přerušen chod programu.

//program, který pošle každou sekundu zprávu o počtu ms po sériové lince

long cas = 0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  if(millis() >= cas+100){
    cas = millis();
    Serial.println(cas);
  }  
}

Poznámka: K přetečení časovače dojde přibližně jednou za 50 dní. (4 294 967 295 ms = 4 294 967 s = 71 582 min = 1193 h = 49,7 dní)

 

micros()

Funkce micros() je stejná jako millis(), pouze vrací hodnotu v mikrosekundách. Rozsah hodnot je stejný, ale jelikož platí, že 1 milisekunda = 1000 mikrosekund, doba přetečení bude tisíckrát menší, tedy asi 71,5 minuty. Nutno dodat, že rozlišení funkce je u 16 MHz procesorů 4 mikrosekundy a u 8 MHz 8 mikrosekund. Výstupem funkce tedy bude násobek čtyř, nebo osmi.

 

Poznámka: Možná se ptáte, proč jsou maximální hodnoty parametrů, nebo vrácených čísel takové, jaké jsou. Je tomu tak, protože funkce pro práci s časem používají dva datové typy, které jsem ve článku Základní struktury jazyka Wiring neuvedl. Jsou to unsigned int a unsigned long. Datový typ unsigned int má stejný rozsah hodnot jako int, jenom je tento rozsah posunut směrem do kladných čísel. Typ int může uchovat čísla od -32,768 do 32,767. U typu unsigned int se nepracuje se zápornými čísly. Rozsah je u něj tedy od 0 do 65,535. Stejná situace je i u unsigned long, jen s větším rozsahem.

Matematické funkce

Nyní si pojďme ukázat, jaké matematické operace Arduino podporuje. Než ale začneme, připomeňme si základní operátory.

 

Matematické operátory

Většina těchto operátorů je nám dobře známá z hodin matematiky. Jedinou výjimkou je operátor % (modulo), který vrací zbytek po celočíselném dělení.

1 + 2 = 3 //sčítání
2 - 1 = 1 //odčítání
2 * 3 = 6 //násobení
9 / 3 = 3 //dělení

//modulo
9 % 6 = 3
//na první pohled je tato operace poměrně zvláštní
//slovy se dá ale jednoduše vyjadřit jako:
9 děleno 6 je 1 zbytek 3
//operace modulo nám vrátí právě tento zbytek

Praktické využití nachází operace modulo mimo jiné i v určování dělitelnosti. Pokud je zbytek po dělení a číslem b nula, potom je a dělitelné b.

int a = 20;
int b = 5;

if(a % b == 0){
	//a je dělitelné b
}
else{
	//a není dělitelné b
}

 

min()

Funkce min slouží k výběru menšího z čísel. Vstupními parametry jsou dvě čísla a výstupem hodnota menšího z nich. Používá se například při hledání nejmenšího čísla v poli, nebo k omezení hodnot ze senzoru (aby nedošlo k překročení určité meze).

//hledání nejmenšího čísla v poli
void setup() {
  int delka = 10;
  int pole[] = {2, 4, -8, 3, 2, 100, 200, 50, 99, 358};
  int nejm = pole[0]; //nejmenší číslo
  
  Serial.begin(9600); 
  
  for(int i = 1; i < delka; i++){
    nejm = min(nejm, pole[i]);
  }
  
  Serial.println(nejm);
}
 
void loop() {
 
}

 

max()

Syntaxe této funkce je shodná s min(). Jejími parametry jsou také dvě čísla a vrácenou hodnotou je větší z nich. Může být použita například při hledání největšího čísla v poli.

//hledání největšího čísla v poli
void setup() {
  int delka = 10;
  int pole[] = {2, 4, -8, 3, 2, 100, 200, 50, 99, 358};
  int nejm = pole[0]; //největší číslo
  
  Serial.begin(9600); 
  
  for(int i = 1; i < delka; i++){
    nejm = max(nejm, pole[i]);
  }
  
  Serial.println(nejm);
}

void loop() {
 
}

 

abs()

Tato funkce vrátí absolutní hodnotu čísla. Vstupem je tedy číslo a výstupem jeho absolutní hodnota.

Poznámka: Absolutní hodnota čísla x je nezáporné reálné číslo. Pokud je x >= 0, abs(x) = x. Pokud je x < 0, potom abs(x) = -x. Laicky řečeno je absolutní hodnota vzdálenost čísla od nuly na číselné ose.

x = abs(150) //x = 150
x = abs(-150) //x = 150

 

constrain()

Tuto funkci si můžeme představit jako kombinaci min() a max() s vhodnými parametry. Slouží totiž k omezení rozsahu proměnné jak shora, tak zdola. Má tři parametry: upravovanou hodnotu, dolní mez a horní mez. Pokud vstupní číslo klesne pod spodní hranici, výstupem je hodnota spodní hranice. Překročí-li horní hranici, výslednou hodnotou je hodnota horní hranice. Pokud je hodnota v mezích, výstupem funkce bude stejná hodnota, jako na vstupním parametru.

x = constrain(upravovaná hodnota, dolní mez, horní mez);

x = constrain(1,10,100); //x = 10
x = constrain(150,10,100); //x = 100
x = constrain(50,10,100); //x = 50

 

map()

Stejně jako funkce constrain(), slouží i funkce map() k úpravě rozsahu proměnné. Na rozdíl od předchozí funkce však nedochází k oříznutí hodnot, ale k rovnoměrnému „roztažení“, nebo „zmáčknutí“ celé stupnice. Dá se použít například k úpravě hodnoty získané při čtení analogového vstupu (0 – 1023) a jejich použití ve funkci analogWrite, která pracuje s hodnotami od 0 do 255. Syntaxe je následující: x = map(hodnota, minimumPůvodníStupnice, maximumPůvodníStupnice, minimumNovéStupnice, maximumNovéStupnice);

//úprava jasu LED pomocí potenciometru

int analog, pwm;

void setup() {
  Serial.begin(9600);
}

void loop() {
  analog = analogRead(A0);
  pwm = map(analog, 0, 1023, 0, 255);
  Serial.print("Analog: ");
  Serial.print(analog);
  Serial.print(" PWM: ");
  Serial.println(pwm);
  
  analogWrite(11, pwm);
}

 

pow()

Funkce pro mocnění čísla na jiné číslo. Vstupními parametry jsou číslo a mocnina.

pow(10,3) = 1000;
pow(10,4) = 10000;
pow(2,5) = 32;
//ukázka použití

int a = 10;
int b = 3;
float c;

void setup() {
  Serial.begin(9600);
}

void loop() {
  c = pow(a,b);
  Serial.println(c);
  delay(1000);
}

 

sqrt()

Funkce sqrt() vrátí druhou odmocninu vstupního čísla.

sqrt(25) = 5;
sqrt(256) = 16;
sqrt(10000) = 100;

 

Goniometrické funkce

Než si představíme jednotlivé funkce, povězme si něco o jednotkách úhlů. My jsme totiž většinou zvyklí měřit úhel ve stupních. Plný úhel je zde 360°. Matematicky přesnější je ale použití radiánů. Tyto jednotky vycházejí z jednotkové kružnice. Plný úhel (360°) je roven 2*PI radiánů, 180° = PI radiánů atd. Platí mezi nimi převodní vztah: radiány = (stupně * PI)/180. Goniometrické funkce nacházejí uplatnění při výpočtu stran a úhlů v trojúhelnících a dalších rovinných i prostorových útvarech. Všechny goniometrické funkce jsou periodické – po určitém intervalu se jejich hodnoty opakují.

 

sin()

Funkční hodnota funkce sinus je dána jako poměr strany protilehlé ku přeponě v pravoúhlém trojúhelníku. Amplituda funkce je 1 a perioda 360°, čili 2PI radiánů. Grafem funkce je tzv. sinusoida. Parametrem funkce je úhel v radiánech a výstupní hodnotou je hodnota sinu pro daný úhel. Následující příklad vypíše část sinusoidy otočenou o 90° pomocí pomlček přes sériovou linku.

Graf funkce sinus

Funkce sinus

float pi = 3.14159265359;
int amplituda = 10; //aby byla sinusoida viditelná, zvětšíme její amplitudu 10x
float perioda = 2*pi;
float hodnota;


void setup() {
  Serial.begin(9600);  
  //tento cyklus nalezne hodnotu funkce sinus po desetinnách PI
  for(float i = 0; i <= perioda; i+=(pi/10)){
    //počet vygenerovaných pomlček
    hodnota = sin(i)*amplituda + amplituda;
    
    for(int j = 0; j < hodnota; j++){
      Serial.print('-');  
    }
    Serial.println(' ');
  }
}

void loop() {
}

 

cos()

Funkce cos() slouží k určení kosinu dané hodnoty. Je definovaná jako délka přilehlé ku přeponě v pravoúhlém trojúhelníku. Jejím grafem je kosinusoida. Sinusoida a kosinusoida jsou si podobné. Kosinusoida je vlastně sinusoida posunutá o PI/2 radiánů doprava. Mezi funkcemi sin() a cos() platí převodní vztah sin(x) = cos(x-PI/2). Perioda = 2PI, amplituda = 1. Na příkladu můžete vidět výpis kosinusoidy.

Graf funkce cosinus

Funkce cosinus

float pi = 3.14159265359;
int amplituda = 10;
float perioda = 2*pi;
float hodnota;


void setup() {
  Serial.begin(9600);  
  for(float i = 0; i <= perioda; i+=(pi/10)){
    hodnota = cos(i)*amplituda + amplituda;
    
    for(int j = 0; j < hodnota; j++){
      Serial.print('-');  
    }
    Serial.println(' ');
  }
}

void loop() {
}

 

tan()

Poslední goniometrickou funkcí, se kterou umí Arduino pracovat je funkce tangents. Ta je dána jako poměr protilehlé strany ku přilehlé v pravoúhlém trojúhelníku. Má periodu jednoho PI. Rozsah funkčních hodnot je od plus do minus nekonečna.

Graf funkce tangens

Funkce tangens

 

Náhodná čísla

Stroje na rozdíl od člověka neumí jednoduše vytvořit náhodné číslo. Za „náhodným“ číslem totiž většinou stojí složitá série algoritmů, která je však statisticky předpovidatelná. Takovýmto číslům se říká pseudo-náhodná. Na první pohled jako náhodná opravdu vypadají, ale ve skutečnosti nejsou. Principy generování opravdu náhodných čísel jsou většinou založeny na měření fyzikální veličiny, která je považována za náhodnou (pohyby plynů a kapalin, fázový šum v laseru…). To je ale pro Arduino poměrně složité.

 

random() a randomSeed()

Tato funkce slouží ke generování pseudo-náhodných čísel. Může mít jeden, nebo dva parametry.

random(max); //vygeneruje "náhodné" číslo mezi 0 a max-1
random(min, max); //vygeneruje "náhodné" číslo mezi min a max-1

Ke správné funkci generátoru je ještě potřeba použít funkci randomSeed(). Ta slouží k nastavení výchozí hodnoty pro generátor. Má pouze jeden číselný parametr. Jako hodnota parametru se používá funkce analogRead() u pinu ke kterému není nic připojeno. Dochází kolem něj totiž k zachytávání elektromagnetického šumu, který může sloužit jako náhodná vstupní hodnota. Celý program by tedy mohl vypadat takto:

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(A0));  
}

void loop() {
  delay(500);
  Serial.println(random(256));
}

 

Příklad

Pomocí funkce random() si vytvoříme jednoduchou hrací kostku. Výsledek budeme zobrazovat pomocí LED diod uspořádaných stejně, jako černé body na hrací kostce. Využijeme také tlačítko. Vždy po jeho zmáčknutí se vygeneruje nové číslo.

Budeme potřebovat:

  1. Nepájivé kontaktní pole a vodiče. Třeba ty z našeho Starter kitu, ve kterém najdete i další komponenty potřebné pro tento příklad.
  2. 7x LED dioda
  3. 7x 330 ohm resistor
  4. tlačítko
  5. 10 kohm resistor

Vše zapojíme podle obrázku. Není důležité, na jaký pin připojíme jakou LED diodu. Vše se dá jednoduše upravit v programu.

Schéma propojení hrací kostky

Hrací kostka

Před uploadem programu do Arduina musíme upravit pole s informacemi o pinech LED diod a o tlačítku. Jedná se o pole leds[] a proměnnou tlacitko. Led diody na kostce jsou očíslovány následovně (číslo LED odpovídá jejímu indexu v poli):

Čísla jednotlivých políček hrací kostky

Schéma kostky

Zdrojový kód programu by poté mohl vypadat například takto.

//čísla LED s odpovídajícím indexem
byte leds[7] = {2,3,4,5,6,7,8}; 

//logické stavy LED použitých u jednotlivých čísel
byte cisla[7][7] = {{}, /*prázdné pole - 0 se nezobrazuje*/
                    {0,0,0,1,0,0,0}, /*1*/
                    {1,0,0,0,0,0,1}, /*2*/
                    {1,0,0,1,0,0,1}, /*3*/
                    {1,0,1,0,1,0,1}, /*4*/
                    {1,0,1,1,1,0,1}, /*5*/
                    {1,1,1,0,1,1,1}}; /*6*/

byte tlacitko = 9; //pin s tlačítkem

byte randn; //proměnná pro náhodnou hodnotu
 
void setup() {
  Serial.begin(9600);  
  randomSeed(analogRead(A0)); //inicializace generátoru
  for(int i = 0; i <= 7; i++){
    pinMode(leds[i], OUTPUT);
    digitalWrite(leds[i], HIGH); //kontrola funkce LED
  }
  pinMode(tlacitko, INPUT);
  delay(1000);
  for(int i = 0; i <= 7; i++){
    digitalWrite(leds[i], LOW); //vypnutí všech LED
  }
}
 
void loop() {
  if(digitalRead(tlacitko) == 1){
    for(int i = 0; i <= 7; i++){
      digitalWrite(leds[i], LOW); //vypnutí všech LED
    }
    randn = random(1,7);
    
    for(int i = 0; i <= 6; i++){
      if(cisla[randn][i] == 1){
        digitalWrite(leds[i],HIGH);  
      } 
    }
    
    delay(1000);
  } 
}  

Pokud vše proběhlo bez chyby, měla by se hodnota na kostce změnit vždy po zmáčknutí tlačítka.

 

Zdroje obrázků

[sinusoida]

[kosinusoida]

[tangentoida]

V případě jakýchkoliv dotazů či nejasností se na mě neváhejte obrátit v komentářích.

Zbyšek Voda

Zbyšek Voda

Už nějaký čas se zajímám o věci kolem Internetu věcí a otevřeného hardware a software. Tak jsem se také v roce 2010 dostal k Arduinu, pro které dodnes programuji a taky píšu články o práci s ním. Baví mě vymýšlet, jak staré věci používat novým způsobem.
Zbyšek Voda

Latest posts by Zbyšek Voda (see all)

4 Comments on “Užitečné funkce

Chosé
8.9.2016 at 22:36

Jde proměnná millis() nějak jednoduše vynulovat?

Zbyšek Voda
Zbyšek Voda
11.9.2016 at 17:39

Dobrý den,
nijak jednoduše to nejde a navíc to asi ani nechcete. Některé knihovny totiž jsou na této funkci závislé, takže by se mohly vynulováním rozbít.
Spíš je dobré se naučit, jak ošetřit přetečení – viz například https://www.baldengineer.com/arduino-how-do-you-reset-millis.html.

Jan
26.10.2015 at 13:27

Zdravim pane Zbysku.

Rad bych se Vas zeptal, zda by jste mi pomohl s kodem pro Arduino Uno.

Rad bych si udelal generator funkci (SQR, Sin, Tri) a mam k tomu AD9058. modul.
Krome vyse zminenych wave forms potrebuji plynule ladit frequenci a plynule ladit duty cycle.

K tomu jeste jedna funkce -trigger- ktera by mi dala moznost jen jednoho impulsu (monostable) a nebo nekolika impulsu (volba delky trvani nebo pocet pulsu) pri dodani exteniho trigger impulsu.

Ja jsem naprosty zacatecnik, nicmene jsem sto to po delsi dobe narogramovat. Problem vidim s16ti pin LCD displayem, U nej bych se urcite zasekl.

Rad bych se Vaszeptal, zda by jste mi umel v teto zalezitosti pomoci pripadne, za nejakou rozumnou castku napogramovat.

Za odpoved predem dekuji a jsem s pozdravem.
Jan

Zbyšek Voda
Zbyšek Voda
26.10.2015 at 14:10

Dobrý den Jane,
u toho LCD ani nemusíte tolik pinů zapojovat 🙂
V tomto článku: http://arduino.cz/arduino-a-displeje-ii/ se problematice LCD displejů věnuji, tak tam zkuste mrknout. Určitě na to přijdete 🙂

Kdyby se něco nedařilo, nebo byste potřeboval cokoliv zkonzultovat, napište na naše fórum, nebo se mi ozvěte na zbysekvoda@gmail.com.

Ať se daří!
Zbyšek

Napsat komentář