Pokročilejší struktury jazyka Wiring
V tomto díle navážeme na informace z minula a ukážeme si další možnosti jazyka Wiring. Na začátku si řekneme, co jsou to konstanty a jak je používat. Poté si ukážeme, jak pracovat s analogovým vstupem a výstupem, pomocí něhož se dají získávat data z různých analogových senzorů. Nakonec se dostaneme k velmi důležité součásti jakéhokoliv programovacího jazyka, kterou jsou podmínky.
Obsah článku:
Konstanty
Konstanty si můžeme představit jako proměnné, které mají přednastavenou hodnotu, definovanou tvůrci Arduina. Mají za úkol zpřehlednit práci a přiblížit kód lidskému jazyku. My jsme s některými z nich pracovali již v minulém dílu. Můžeme je rozdělit do tří skupin.
Logické konstanty
Jsou pouze dvě hodnoty, a to pravda a nepravda. V programování jim odpovídají konstanty true a false. Používají se tam, kde je třeba rozhodovat pouze mezi dvěma stavy.
- false
- Konstanta false má hodnotu 0.
- true
- U konstanty true je situace trochu komplikovanější. Mohlo by se zdát, že má hodnotu 1, ale není to úplně přesné. Při logických operacích totiž jazyk Wiring vnímá jakékoliv nenulové číslo typu integer jako true.
Typ digitálního pinu
V minulém díle jsme při určování, zda se bude pin chovat jako vstup, nebo jako výstup, používali ve funkci pinMode() dvě konstanty – OUTPUT a INPUT. Dnes si k nim přidáme ještě třetí možnost INPUT_PULLUP.
- OUTPUT
- Při použití této konstanty je pin nastaven jako výstup. Ten snese proud do 40 mA. Při stavu HIGH tedy tento výstup může poskytnout proud do 40 mA a při stavu LOW může stejně velký proud přijmout.
- INPUT
- Nastaví pin jako vstup. Ten se používá ke čtení hodnot z digitálních senzorů (nejjednodušší jsou tlačítka), ale i ke komunikaci. Použití s tlačítkem jsme si ukázali v minulém dílu. V jeho zapojení si všimněme, že je tento pin stále připojen ke GND přes 10k ohm resistor. Při nezmáčknutém tlačítku je tedy výsledek funkce digitalRead() hodnota LOW. Po zmáčknutí tlačítka dojde k připojení k +5V a změny hodnoty funkce na HIGH.
- INPUT_PULLUP
- Funguje podobně, jako INPUT. Rozdíl je v tom, že dojde k připojení interního rezistoru. Ten je uvnitř čipu zapojen mezi digitálním vstupem a +5V. Výchozí hodnota funkce digitalRead() je tedy HIGH. Když chceme hodnotu změnit, musíme vstup připojit na GND. Při použití příkladu s tlačítkem má tedy funkce hodnotu HIGH, když je tlačítko uvolněno a LOW, když je zmáčknuto.
Hodnota napětí na pinu
Jsou pouze dvě možné hodnoty, jaké může mít proud při čtení a zápisu pomocí funkcí digitalRead() a digitalWrite(). Jsou to hodnoty LOW a HIGH.
- HIGH
- Při čtení pomocí funkce digitalRead() je vyhodnocena hodnota napětí jako HIGH, pokud je větší než 3V. Když použijeme funkci digitalWrite(pin, HIGH), na výstupu bude právě 5V.
- LOW
- Při čtení je stav napětí vyhodnocen jako LOW, pokud je jeho velikost menší než 2V. Při zápisu je hodnota 0V. Fakticky ale může pin ve stavu LOW „přijmout“ napětí do velikosti 5V (což u HIGH nelze).
Analogový vstup a výstup
S digitálním vstupem a výstupem jsme se setkali už v minulém článku. Co když ale potřebujeme pracovat i s analogovými hodnotami? Na to má Arduino ve výbavě užitečné funkce. Ke čtení a zápisu se zde používají funkce analogRead() a analogWrite(). Ty jsou však limitovány pro použití pouze na určených pinech. Pojďme si je postupně představit.
analogWrite()
Jak už z názvu vyplývá, jedná se o funkci sloužící k nastavení „analogové“ hodnoty na pinu. Můžeme ji použít pouze na pinech označených PWM (u Arduina UNO jsou to piny: 3, 5, 6, 9, 10, 11). Používá se u ní syntaxe analogWrite(číslo_pinu, hodnota), kdy hodnota může být v rozsahu 0 až 255. Slovo analogové jsem dal do uvozovek, protože se ve skutečnosti o žádné analogové hodnoty nejedná. Pokud bychom chtěli skutečně analogovou hodnotu v rozsahu například 0-5V, museli bychom použít externí D/A převodník. Tato funkce totiž na vybraných pinech generuje PWM signál, což je jakási digitální „náhražka“ analogového signálu. Ta v praxi funguje tak, že rychle střídá 0 a 5V. To se projeví sníženou ‚účinností‘. LED svítí slaběji (ve skutečnosti rychle bliká a střídá pouze dva stavy napětí a snížená intenzita je způsobena setrvačností oka), motor se točí pomaleji atd. Podle poměru času, ve kterém je na výstupu +5V ku stavu 0V se pak odvíjí intenzita svícení LED diody a podobně. Při volání funkce analogWrite(pin, 127) je tedy přibližně 50% času nastaveno napětí +5V a 50% času 0V. Průběh napětí při různých hodnotách můžete vidět na obrázku.
analogRead()
Funkce analogRead() slouží ke čtení analogové hodnoty na vstupech k tomu určeným. Jsou to tedy piny označené písmenem A (například A2). Čtení analogových hodnot je užitečné u různých senzorů (teplota, vlhkost atd.). Většina desek Arduina má rozlišení 10 bitů, což odpovídá hodnotám od 0 do 1023. Například u Arduino DUE se ale můžeme setkat až s 12-bitovým rozlišením. Zde se dá nastavit požadované rozlišení pomocí funkce analogReadResolution(). My se ale budeme zabývat obyčejným Arduinem. Syntaxe je jednoduchá: proměnná = analogRead(pin). Nejjednodušším příkladem použití je měření hodnot na potenciometru. Pokud bychom chtěli měřit například stav fotoresistoru, museli bychom ho zapojit do děliče napětí s vhodným resistorem. Použití obou analogových funkcí si ukážeme na zapojení s LED diodou a potenciometrem.
Příklad
Jako příklad si ukážeme zapojení, ve kterém budeme regulovat jas LED diody pomocí potenciometru.
Budeme potřebovat:
- Deska Arduino
- Nepájivé kontaktní pole s vodiči
- LED dioda
- potenciometr
- 330 ohm resistor
Na trhu nalezneme celou řadu potenciometrů. Nejčastěji se používají ty s odporem kolem 10k ohm s lineárním průběhem. Pokud máme všechny komponenty připravené, můžeme je zapojit podle následujícího schématu:
V programu si musíme dát pozor na hodnoty, se kterými funkce pracují. Z funkce analogRead() vychází hodnoty 0 až 1023, kdežto analogWrite() čeká na rozsah hodnot 0 až 255. Musíme tedy zajistit převod hodnot. To je v tomto případě jednoduché, protože 256 (28) se vejde do 1024 (210) čtyřikrát. Nejjednodušším způsobem je tedy vydělení hodnot z analogRead() čtyřmi. Kód tohoto příkladu bude také velmi jednoduchý. Měření budeme provádět v těle funkce loop().
Poznámka: Existuje i elegantnější způsob převodu hodnot, který si ukážeme v některém z budoucích článků.
byte led = 6; //pin s LED diodou byte pot = A0; //pin s připojeným potenciometrem int val; //proměnná připravená k uchování hodnot void setup() { //sem nic nepíšeme } void loop() { val = analogRead(pot)/4; //čtení hodnoty na A0 a úprava rozsahu analogWrite(led, val); //generování PWM }
Podmínky
Pokud chceme, aby se určitá část kódu provedla pouze v určitých případech, přicházejí na řadu podmínky. Existují tři základní struktury, které rozdělují program podle zadaných podmínek. V lidské řeči by se daly vyjádřit jako:
- Pokud platí podmínka, udělej to a to.
- Pokud platí podmínka, udělej to a to. Pokud neplatí, udělej to a to.
- Pokud je hodnota proměnné xy, udělej to a to. Pokud je yz, udělej to a to. Pokud je xz, udělej to a to…
Než se však pustíme do psaní podmínek, musíme se podívat na způsob, jakým se dají v jazyce Wiring zadávat.
Porovnávací operátory
- A == B – A je rovno B
- Vrátí hodnotu true, pokud A má stejnou hodnotu, jako B.
- A != B – A není rovno B
- Vrátí hodnotu true, pokud má A jinou hodnotu než B.
- A < B – A je menší než B
- Vrátí hodnotu true, pokud je A menší než B.
- A > B – A je větší než B
- Vrátí true, pokud je A větší než B.
- A <= B – A je menší nebo rovno B
- Vrátí true, pokud je A menší nebo rovno B.
- A >= B – A je větší nebo rovno B
- Vrátí true, pokud je A větší nebo rovno B.
10 == 5 //není pravda 10 != 5 //je pravda 10 < 5 //není pravda 10 > 5 //je pravda 10 <= 5 //není pravda 10 >= 5 //je pravda
Složené podmínky
Někdy dospějeme do situace, ve které je potřeba pracovat s nějakou složitější podmínkou. K tomuto účelu slouží tzv. logické operátory. Můžeme si je představit jako definici vztahu mezi více porovnávacími operátory.
- X && Y – a (konjunkce)
- Výsledkem je true pouze v případě, když jsou true X i Y.
- X || Y – nebo (disjunkce)
- Výsledkem je true v případě, kdy je alespoň jedna z X a Y true.
- !X – negace
- Výsledkem je true, pokud je X false a naopak.
(1 == 2) && (2 == 2) //false (1 == 1) && (2 == 2) //true (1 == 2) || (2 == 3) //false (1 == 2) || (2 == 2) //true (2 == 2) || (2 == 3) //true (2 == 2) || (3 == 3) //true !(1 == 1) //false !(1 == 2) //true !(false) //true
if()
Ve většině programovacích jazyků se pro zápis podmínek používá slovo if. V jazyce Wiring je možné použít několik způsobů zápisu. Ty ale vždy začínají: if(podmínka).
//podmínky s jedním příkazem if(x > 120) digitalWrite(LEDpin, HIGH); if(x > 120) digitalWrite(LEDpin, HIGH); if(x > 120){ digitalWrite(LEDpin, HIGH); } //podmínky s více příkazy - je nutné použít složené závorky if(x > 120){ digitalWrite(LEDpin1, HIGH); digitalWrite(LEDpin2, HIGH); ... }
else if()
Pokud chceme do podmínky přidat více možností, používá se zápis else if().
if ((A < 800) && (A > 500)){ //příkazy } else if ((A < 500) && (A > 200)){ //příkazy }
else
K části else se nepíší další podmínky. Slouží k určení příkazů, které se provedou, pokud ani jedna z předchozích podmínek není splněna.
if ((A < 500) && (A > 200)){ //příkazy } else if ((A < 500) && (A > 200)){ //příkazy } else{ //příkazy }
Switch
Switch je speciální druh podmínky. Speciální je v tom, že se zabývá pouze proměnnou a její hodnotou. Program prochází každou větev konstrukce switch a testuje hodnotu proměnné. Další rozdíl je v tom, že se může provést i více větví (což u if nelze). Pokud ale chceme, aby po provedení větve program pokračoval až za koncem konstrukce switch, musíme použít na konci větve příkaz break;
Syntaxe je následující:
switch (proměnná){ case 1: //pokud je hodnota proměnné 1, provede se tato část kódu break; //po provedení této části konstrukce switch končí case 2: //pokud je hodnota proměnné 2, provede se tato část kódu break; default: /* pokud se hodnota proměnné nerovná žádné z nabízených možností, provede se tato část */ }
Příklad
Na závěr si trochu pohrajeme s podmínkami. Vytvoříme aplikaci, která bude číst hodnotu z potenciometru a podle ní vybere led diodu. K tomuto příkladu budeme potřebovat stejné vybavení, jako u toho minulého, jen s větším počtem LED diod a resistorů. Pro předváděcí účely jsem zvolil pět diod.
Kód vyhodnocující data z potenciometru by mohl vypadat takto:
byte led[] = {0,1,2,3,4}; //pole s piny připojených LED diod byte pot = A0; int val; void setup() { pinMode(led[0], OUTPUT); pinMode(led[1], OUTPUT); pinMode(led[2], OUTPUT); pinMode(led[3], OUTPUT); pinMode(led[4], OUTPUT); } void loop() { val = analogRead(pot); if(val > 800){ digitalWrite(led[0],HIGH); } else if(val > 600){ digitalWrite(led[1],HIGH); } else if(val > 400){ digitalWrite(led[2],HIGH); } else if(val > 200){ digitalWrite(led[3],HIGH); } else{ digitalWrite(led[4],HIGH); } delay(250); digitalWrite(led[0],LOW); digitalWrite(led[1],LOW); digitalWrite(led[2],LOW); digitalWrite(led[3],LOW); digitalWrite(led[4],LOW); }
Když se nyní podíváte na kód, jsou zde vidět opakování, ve kterých se mění pouze jedno číslo. Jak si v takovýchto případech ulehčit práci si ukážeme v příštím dílu.
Zdroje obrázků
- Sledovač slunce s Arduinem - 23.3.2022
- Programovatelný kytarový pedál s Arduinem - 26.2.2020
- Arduino infračervený teploměr vytištěný na 3D tiskárně - 11.2.2020
fimi
21.7.2019 at 11:26Dobrý den, není mi jasné u analogového vstupu, zda čtené hodnoty s odvozují od konstantního napětí 5,0V nebo od nějakého referenčního napětí ?
Děkuji.
Zbyšek Voda
21.7.2019 at 16:29Dobrý den,
ve výchozím stavu je použita reference podle napájecího napětí desky (5V, nebo 3.3V).
Také lze přepnout na interní referenci 1.1V, popřípadě 2.65V (záleží na desce).
Třetí možnost je použít externí referenci (pin AREF). I tato reference musí mít maximum napájecího napětí (ne víc, jinak by mohlo dojít k poškození).
Více je toto popsáno zde: https://www.arduino.cc/en/Reference.AnalogReference
pedyngro
21.4.2017 at 17:38Proč v setupu není A0 jako input?
Zbyšek Voda
21.4.2017 at 17:42pinMode se nastavuje u vstupů, které chcete používat jako digitální – tedy s funkcemi digitalRead a digitalWrite.
U analogových vstupů toto nastavovat nemusíte.
Jarmil
30.4.2016 at 10:06Dobrý den měl bych dotaz k tomuto článku. Používáte na zápis z potenciometru datovou proměnou typu byte ta má rozsah 0-255 ale z převodníku lze získat až 1023 protože je 10bitobý. Sice se to potom dělí 4 ale to je až po načtení proměnné. Není to chyba? Děkuji za odpověd.
Zbyšek Voda
4.5.2016 at 13:53Dobrý den, ať se dívám, jak se dívám, tak nikde nevidím, že bych do proměnné typu byte ukládat větší hodnotu 🙂
Možná Vás zmátlo:
byte pot = A0;
int val;
Proměnná pot je typu byte, ale obsahuje jenom informaci, na kterém pinu je připojený potenciometr.
Hodnota z potenciometru (která je, jak říkáte 10 bitová) se ukládá do proměnné val, která je typu int a tam se v pohodě vejde 🙂
Ondřej
17.4.2016 at 0:00Dobrý den, v odstavci analogwrute je patrne chyba ve vete: Používá se u ní syntaxe digitalWrite(číslo_pinu, hodnota), kdy … Nemělo by tam být analogWrite(číslo_pinu, hodnota)?
Zbyšek Voda
17.4.2016 at 10:23Dobrý den, máte pravdu. Opravuji, děkuji 🙂
hxh
3.12.2015 at 15:46Dobrý den. Vážený pane, prosím o radu. V projektech jsem si našel teploměr. Vše jsem sestavil a nevím jak dál. Nedokážu přidat funkci, která by mi umožnila třeba za použití podmínek v závislosti na teplotě zapínat na př.digitalWrite(led,HIGH); a tím za požití relátek zapínat jiná zařízení. Ještě jedna otázka: Předpokládám, že v každé knize je zapsán nějaký program. Lze knihu otevřít a podívat se, co je tam napsáno. Jak? Asi se Vám budou zdát moje otázky primitivní, ale začátky samoukovy jsou vždy velmi těžké. Zdraví hxh.
Zbyšek Voda
7.12.2015 at 18:05Dobrý den, předpokládám, že v tom projektu je uložená nameřená teplota v nějaké proměnné. Když třeba chcete, aby se sepnul pin 1, když je teplota větší, než 35°, napíšete:
if(teplota > 35){
digitalWrite(1, HIGH);
}
tímto způsobem můžete udělat rozhodování pro více teplot, pinů apod.
Mluvíte o více knihách, ale my na těchto stránkách máme jen jednu knihu. Ta vám přijde ve formátu PDF, který můžete otevřít programem, který umí číst PDF soubory – například Adobe Acrobat Reader a další. Poté si můžete vybraný kód zkopírovat a vložit do Arduino IDE.
hxh
9.12.2015 at 14:50Děkuji za vysvětlení a trpělivost. Zdraví hxh.
hxh
19.11.2015 at 10:20*/Napsal jsem si takový primitivní prográmek. A když jsem jej
spustil, byl jsem velmi překvapen. Přesto, že jsem napsal
Serial.print(Sviti led c. 1); očekával bych, že výpis bude na jednom řádku.
Ale opak je pravdou. Každý výpis řádek ukončí, jako kdybych napsal Serial.println. Co je ještě
podivnější, je ta skutečnost,že aniž bych cokoliv zadal, výpis
sám napíše „Nesviti zadna dioda“.
int led1=13;
int led2=12;
int led3=11;
byte odpor=A0;
int hodnota;
void setup() {
pinMode(led1,OUTPUT);
pinMode(led2,OUTPUT);
pinMode(led3,OUTPUT);
Serial.begin(9600);
}
void loop() {
odpor = analogRead(odpor);
if(odpor > 300){
digitalWrite(led1,HIGH);
Serial.print(„Sviti led c.1“);
}
else if(odpor > 400){
digitalWrite(led2,HIGH);
Serial.print(„Sviti led c.2“);
}
else{
digitalWrite(led3,HIGH);
Serial.print(„Sviti led c.3“);
}
delay(250);
digitalWrite(led1,LOW);
digitalWrite(led2,LOW);
digitalWrite(led3,LOW);
}
Zbyšek Voda
19.11.2015 at 10:31Dobrý den,
to my spíš přijde pravděpodobnější, že se vám program nepovedlo nahrát a hláška „Nesvítí žádná LED“ je součástí programu, který jste měl nahraný předtím. Ten se nepovedlo přehrát, takže pořád ještě v Arduinu běží.
Zkuste do Arduina nahrát pouhé blikání LEDkou a uvidíte, jestli bude ještě něco vypisovat.
Honza
10.9.2015 at 21:15Dobrý den,
měl bych dotaz k příkladu na regulaci jasu LED pomocí potenciometru výše. Vše se v pořádku povedlo, jen mě zaujala jedna věc. když jsem z desky úplně vyjmul potenciometr, nebo jinak přerušil obvod k analog. pinu ( snad kromě odstranění napájecího kab. 5V), LED sice méně, ale stále svítila, jak je to možné?
předpokládal jsem, že v případě přerušení obvodu bude hodnota PWM 0, a tedy LED zhasne..
Díky za odpověď
Zbyšek Voda
10.9.2015 at 21:38Dobrý den,
řekl bych, že to bude tím, že jakmile vyjmete potenciometr z obvodu, stane se z analogového pinu, na kterém byl potenciometr připojen, jakási anténa. Ta pak chytá náhodný elektromagnetický šum, který je kolem vstupu, což se projeví blikáním LED diody.
Funkce analogRead pak čte hodnoty, které zapínají LED.
Když si vypíšete analogRead přes sériovou linku, budou se zobrazovat náhodné hodnoty (většinou s hodnotou kolem 600).
pepca
16.5.2015 at 12:04Jak do té podmínky vložit proměnou?
Zkoušel jsem tohle jenže to nefunguje:
if(c = Serial.read()) {
Zbyšek Voda
16.5.2015 at 12:53Dobrý den,
tak, jak to máte v kódu pouze přiřazujete do proměnné c hodnotu přečtenou ze Serial.read(). V závorce je tedy výraz přiřazení, který je v tomto případě skoro vždy vyhodnocen jako true. Při přiřazování dochází k „šíření“ přiřazované hodnoty výše. Ta je ve většině případů u Serial.read() nenulová, takže je celý výraz vyhodnocen jako true (v případě, že funkce Serial.read() vrátí 0 je výsledkem false).
Musíte specifikovat zadání. Pokud si chcete přečíst znak (nebo cokoliv) ze sériové linky, přiřadit tuto hodnotu proměnné, a pak ji porovnávat s nějakou hodnotou, bude kód vypadat takto:
char c = Serial.read();
if(c == 'a'){...}
Pokud budete mít v proměnné uloženou hodnotu, kterou pak budete porovnávat se znakem ze sériové linky, bude to vypadat takto:
char c = 'a';
if(c == Serial.read()){...}
Je vícero možností, co si pod otázkou „Jak do té podmínky vložit proměnou?“ dovedu představit. Specifikujte zadání a já vám pomůžu 🙂