Cvičenie je zamerané na prácu s vývojovou doskou Arduino. Cvičenie je orientované na základné programovanie mikroprocesora. V rámci cvičenia budú demonštrované spôsoby obsluhy tlačidiel, sériovej linky, analógových a digitálnych vstupov a piezoelektrických akustických meničov. Po osvojení si učiva, študent dokáže realizovať základné zapojenia s využitím vývojovej dosky Arduino, skúšobného poľa a pasívnych elektronických prvkov (rezistor, dióda, a pod.). Študent dokáže napísať jednoduchý zdrojový kód pre obsluhu mikroprocesora Atmega328P.
- Digitálne vstupno-výstupné piny
- Analógové vstupné piny
- Všetko podstatné o tlačidlách
- Bzučiaky a zvuky
- Sériová linka
Odporúčaná literatúra a dôležité odkazy
[1] KPI-FEI-TUKE: Arduino UNO
[2] KPI-FEI-TUKE: Traffic Lights Controlled by Arduino
[3] Arduino.cc: Language Reference
[4] Unciarobotics.com: Arduino Push Button Debounce using Millis Function
Digitálne vstupno-výstupné piny
Ako už bolo na predchádzajúcom cvičení a tiež aj na prednáške, Arduino Uno obsahuje 14 digitálnych pinov zvyčajne označených ako D0 - D13. Tiež bolo povedané, že aj analógové piny A0 - A5, ktoré primárne slúžia ako analógový vstup je možné použiť ako digitálne. Pri práci s digitálnym pinom je potrebné si osvojiť minimálne tieto príkazy:
- pinMode(pin, nastavenie) - Spravidla túto funkciu voláme vo funkcii setup() a pomocou nej určujeme či budeme pin používať ako výstup - OUTPUT, vstup - INPUT alebo vstup so vstavaným pull-up rezistorom INPUT_PULLUP (vysvetlíme pri tlačidlách).
- digitalWrite(pin, hodnota) - Slúži na zápis logickej hodnoty na daný pin.
- digitalRead(pin) - Slúži na prečítanie logickej hodnoty z daného pinu.
Vstup či výstup?
Je potrebné si uvedomiť, že zle nastavený režim pinMode() môže spôsobiť nesprávne chovanie sa zariadenia a vo špecifických prípadoch aj jeho zničenie!
- Pokiaľ na digitálny pin chceme pripojiť záťaž ako je napríklad dióda, relé ale aj tranzistor (ten ozaj nepredstavuje veľkú záťaž) a podobne, je potrebné, aby z/do procesora tiekol pomerne veľký prúd (max 20mA!). Vtedy sa vnútorná štruktúra procesora musí na tento stav pripraviť. Docieli sa to tak, že daný pin sa prepne do režimu nízkeho odporu. Toto dosiahneme s príkazom pinMode(pin,OUTPUT). Pozor na stav ak je pin nastavený ako výstup a pripojí sa priamo na zem alebo +5V. Môže dôjsť k jeho zničeniu!
- Pokiaľ pin hodláme použiť ako digitálny vstup, spravidla nechceme, aby ním pretekal vysoký prúd. V elektronike je zvykom, že ak je niečo vstupom má to mať vysoký (teoreticky nekonečne veľký) odpor. Je to preto, že samotným meraním nechceme ovplyvniť chovanie sa meraného obvodu. V konečnom dôsledku na I/O pinoch procesora meriame hodnotu napätia, či už logickú 0 (0V) resp. 1 (5V), alebo analógovú hodnotu v nejakom rozsahu. Teda je nežiadúce, aby pinom tiekol prúd. Vtedy použijeme pinMode(pin, INPUT). Vstupu je stále možné v programe priradiť digitálnu hodnotu 0 alebo 1.
Nasledujúci príklad simuluje situáciu kedy je Arduino použité na monitorovanie či slučka obvodu nie je prerušená. Napríklad si môžeme predstaviť bezpečnostný snímač na okne či dverách. Ak sú dvere otvorené, tento sa rozpojí a preruší sa kontrolovaný obvod.
Analógové vstupné piny
Pre pochopenie a správnu prácu s analógovým vstupom je nutné porozumieť tomu ako vlastne tento vstup dokáže merať hodnotu napätia. Nechceme a ani nemôžeme ísť do detailov problematiky, ale určite musíme skonštatovať, že vo vnútri procesora Atmel ATMega328p je integrovaný Analógovo Číslicový Prevodník ( Analog Digital Coverter - ADC). Existuje viacero typov prevodníkov ale v ATMege je takzvaný Successive Approximation Register Analog to Digital Converter (SAR ADC) s rozlíšením 10 bitov. Rozlíšenie 10 bitov znamená, že hodnotu meraného napätia dokáže rozlíšiť na 1023 úrovní. Tomuto procesu sa tiež hovorí kvantizácia, o ktorej sa budete učiť neskôr. Ďalším dôležitým parametrom pri analógovom vstupe je referenčné napätie. Toto napätie môžeme priviesť na pin Aref ale tiež ho na tomto pine môžeme aj namerať. Arduino má toto napätie vnútorne nastavené na +5V ale programovo ho môžeme meniť príkazom analogReference(). Rôzne vývojové dosky majú rôzne možnosti voľby úrovne tohto napätia. Pre Arduino UNO a Nano sú nasledovné: 5V a 1.1V a prípadne ľubovoľná externá hodnota (z rozsahu 0 - 5V) privedená na pin Aref. Pre Arduino Uno hodnotu referenčného napätia nastavíme takto:
- analogReference(DEFAULT) - Referenčná hodnota bude nastavená na hodnotu 5V
- analogReference(INTERNAL) - Referenčná hodnota bude nastavená na hodnotu 1.1V
- analogReference(EXTERNAL) - Referenčná hodnota bude nastavená na hodnotu aká je pripojená na pin Aref.
Nastavením referenčnej hodnoty určíme v akom rozsahu chceme napätie merať. Toto je napríklad výhodné vtedy ak vieme, že budeme merať hodnoty napätia, ktoré nebudú prevyšovať hodnotu 1.1V. Vtedy je rozumné nastaviť referenciu na 1.1V. Získame tak oveľa presnejšie meranie. Ako už bolo uvedené vyššie. Vieme merať s presnosťou na 10 bitov. To znamená, že najmenšie možné napätie, ktoré vieme zmerať je rovné Aref/1023. Teda pre 1.1V/1023 = 0,0011V a pre 5V to bude 0,0049V.
Ďalej musíme poznamenať, že vnútorné referenčné napätie nemusí byť vždy identické s katalógovou hodnotou. Môže sa trochu líšiť a to môže zásadne ovplyvniť presnosť merania. Preto si pri návrhu zariadenia musíme odmerať skutočnú hodnotu referenčného napätia na pine Aref a vo výpočtoch potom používať túto hodnotu. Na Analógový a referenčný vstup nesmie byť pripojené napätie vyššie ako 5.5V dôjde k jeho zničeniu!
Príklad: Pomocou analógového vstupu A1 odmerajte hodnotu naň pripojeného napätia. Ak toto napätie prevyšuje 3V rozsvieti sa LED dióda pripojená k pinu D13.
void setup() { // Pin 13 bude výstup, lebo na neho pripojíme LED diódu, ktorú potrebujeme napájať pinMode(13, OUTPUT); // Zvolili sme referenčnú hodnotu napätia na +5V analogReference(DEFAULT); // Aby sa inicializovala referenčná hodnota je potrebné urobiť jeden analogRead. // Hodnota nás nezajíma ide len o to, aby sa nastavila referenčná hodnota analogRead(15); } void loop() { float Aref = 5.0; // Hodnotu napätia vo voltoch získame tak že meranú hodnotu z ADC predelíme hodnotou // 1023 a tento výsledok vynásobíme hodnotou Aref. Musí sa to pretypovať na float! // A1 je na pine č. 15, mohla by sa to zapísať aj ako A1. float voltage = (float) Aref*analogRead(15)/1023; if(voltage > 3.0) digitalWrite(13,HIGH); else digitalWrite(13,LOW); }
Ešte si ukážme príklad kedy sa zvolí vonkajšie referenčné napätie. Referenčné napätie bolo získané pomocou napäťového deliča, ktorý je vyhotovený pomocou potenciometra (pokojne aj 5kohm). V kóde vpravo došlo len k malej zmene. Takto nastavená referenčná hodnota umožňuje merať napätie z rozsahu 0-4V. Vyššie pripojené napätie na analógovom vstupe bude stále vyhodnotené ako 4V. Presnosť merania je teraz približne 0.004V.
Na záver je potrebné poznamenať, že ADC môže zavádzať rôzne nelinearity, offset a tiež šum. Tieto nedostatky sa odstraňujú kalibráciou, ktorá však nie je náplňou predmetu Programovanie.
void setup() { pinMode(13, OUTPUT); // Zvolili sme referenčnú hodnotu napätia z vonkajšieho zdroja analogReference(EXTERNAL); // Aby sa inicializovala referenčná hodnota je potrebné urobiť jeden analogRead. // Hodnota nás nezajíma ide len o to, aby sa nastavila referenčná hodnota analogRead(15); } void loop() { float Aref = 4.0; float voltage = (float) Aref*analogRead(15)/1023; if(voltage > 3.0) digitalWrite(13,HIGH); else digitalWrite(13,LOW); }
PULL UP
void setup() { pinMode(13, OUTPUT); pinMode(6, INPUT); } void loop() { if(digitalRead(6) == 0) digitalWrite(13, HIGH); else digitalWrite(13, LOW); }
PULL DOWN
void setup() { pinMode(13, OUTPUT); pinMode(6, INPUT); } void loop() { if(digitalRead(6) == 1) digitalWrite(13, HIGH); else digitalWrite(13, LOW); }
INTERNAL PULL UP
void setup() { pinMode(13, OUTPUT); pinMode(6, INPUT_PULLUP); } void loop() { if(digitalRead(6) == 0) digitalWrite(13, HIGH); else digitalWrite(13, LOW); }
Skutočný hardvér sa môže chovať inak ako simulačný softvér....
Doposiaľ sme uvažovali o ideálnych podmienkach a ideálnych súčiastkach. Problém však je ten, že nič nie je ideálne. Tlačidlá sú známe tým, že po stlačení dochádza k zákmitu (rýchle zopnutie a rozopnutie) to pri rýchlom Arduine znamená to, že v podstate to zaregistruje, že tlačidlo bolo stlačené mnohokrát. Riešiť sa to môže hardvérovo, pripojením kondenzátorov a podobne ale skôr sa stretneme so softvérovým riešením. To spočíva v tom, že po tom ako je prvý krát zachytené stlačenie tlačidla na nejakú dobu sa prestane sledovať jeho stav. Zvyčajne sa to robí veľmi zle cez funkciu delay(). Túto iba pre ukážku použijeme v nasledujúcom príklade. Pritom uvažujeme zapojenie s PULL DOWN rezistorom, ale v princípe sa to robí rovnako pre každý spôsob zapojenia tlačidla. To ako to urobiť elegantne s využitím funkcie milis() [4] si ukážeme na ďalšom cvičení.
void loop() { if(digitalRead(6) == 1) { digitalWrite(13, HIGH); delay(50); } else digitalWrite(13, LOW); }
Bzučiaky a zvuky
Častokrát je potrebné aby zostavené zariadenie vydávalo zvuky. Hlavne sa so zvukmi stretávame vo forme rôznych pípaní pri stlačení tlačidiel, zvučiek pri štarte alebo vypnutí zariadenia a podobne. K Arduinu je možné pripojiť takzvaný piezoelektrický menič - bzučiak. Tento sa pripája k portom, ktoré podporujú PWM. Na doske Arduino Uno sú označené symbolom '~'. Na týchto portoch je možné generovať tóny s frekvenciou od 31Hz do 65kHz (ľudské ucho počuje v závislosti od jedinca maximálne do 20kHz). Bzučiak sa pripája cez maličký cca 100 Ohm rezistor. V kóde môžeme použiť funkcie tone() a noTone().
- K prehrávaniu súvislého tónu použijeme funkciu tone(pin, frekvencia_v_Hz) napr. tone(9, 1000).
- Ak znenie tónu chceme ukončiť môžeme zavolať funkciu noTone(pin) napr. noTone(9).
- Tón je možné prehrať aj na presne definovanú dobu tone(pin, frekvencia_v_Hz, trvanie_v_milisekundách) napr. tone(9, 1000, 2000)
Sériová linka
Veľmi dôležitou súčasťou vývoja hardvéru a softvéru je aj jeho komunikácia s ostatnými zariadeniami. Medzi počítačom a Arduinom je táto komunikácia zabezpečená pomocou sériovej linky. Komunikácia cez sériovú linku je zabezpečená cez niekoľko príkazov, ktoré si ukážeme na príklade. Nakoľko ArduinoIDE neposkytuje možnosť ladenia kódu, sériová linka je veľmi výhodná. Detaily o protokole, ktorý sa používa pri sériovej komunikácii sa budete učiť až v druhom ročníku. Na predmete Programovanie sa máte hlavne naučiť sériovú linku použiť.
Príklad.
Vytvorte program, ktorý čaká na príkazy z PC a na základe týchto príkazov do PC zašle údaje o hodnotách na jeho digitálnych portoch D2 -D13 (príkaz S) a informáciu o sebe (príkaz I). Informácia o stave na portoch bude zaslaná v nasledujúcom formáte: "Z,1,1,0,1,0,0,0,0,0,0,0,0,K" a informácia o Arduine bude vyzerať nasledovne "ARD001".
Sériovú komunikáciu v prostredí ArduinoIDE vyvoláte kliknutím na ikonku lupy vpravo hore. V prostredí Tinkercad je dole tlačidlo pre "Serial monitor"
Príkazy ktoré budeme potrebovať:
- Serial.begin(prenosová_rýchlosť) napr. Serial.begin(9600) sa zadáva najčastejšie vo funkcie setup().
- Serial.available() - Ak je návratová hodnota nenulová, tak v buffery sériovej linky je správa, ktorá bola zaslaná Arduinu
- Serial.read() - Prečíta jeden znak obsahu buffera sériovej linky
- Serial.print("text") - Zapíše text na sériovú linku
- Serial.println("text") - Zapíše text na sériovú linku a zakončí ho terminátorom
Existuje viac príkazov, ktoré sa môžu hodiť. Tieto ale necháme na samoštúdium [3].
int A[14]; void setup() { Serial.begin(9600); // inicializacia seriovej komunikacie for(int i=2;i<14;i++) // Hromadne nastavenie pinov na INPUT { pinMode(i,INPUT); digitalWrite(i,HIGH); } } void loop() { int i; for(i=2;i<14;i++) // Prečítanie hodnoty pinov A[i-2] = digitalRead(i); // Arduino počúva na sériovej linke a reaguje na 2 príkazy // I - zašle svoj identifikátor // S - zašle hodnoty na portoch if(Serial.available()>0) // Ak je niečo na sériovej linke tak // sa skontroluje či ide o správne príkazy { char r = Serial.read(); if( r == 'I') // mód identifikácie { Serial.print("ARD001\n"); } if( r == 'S') // mód zaslania hodnôt portov { Serial.print("Z,"); for(i=0;i<12;i++) { Serial.print(A[i]); Serial.print(","); } Serial.println("K"); } } }
Úloha 1.
Napíšte program pre Vývojovú dosku Arduino, ktorý bude cez sériovú linku prijímať číslice od 0 do 9 a tieto potom zobrazí na 7 segmentový displej (spoločná katóda). Tento displej bude pripojený k pinom D6- D12. Jednotlivé segmenty sa budú spínať sekvenčne, ale veľmi rýchlo (povedzme 10ms na jeden segment). Vytvorte vlastnú funkciu, ktorá bude prevádzať dekadickú hodnotu na kód pre ovládanie 7 segmentového displeja.
Deklarácia funkcie Dec2Seven() je zobrazená nižšie. Skúste sa vyhnúť globálnej premennej, nie je to ale povinnosťou. Jednotlivé bity kódu uloženého v 1R poli - seven sa zapíšu na príslušné piny D6 - D12.
Napr. Ak má svietiť číslo 7, tak sú zapnuté segmenty - A, B a C, ktoré sú pripojené na piny 11, 12 a 6. Nezabudnite na 220 Ohm rezistor, ktorý je spoločný pre všetky diódy. Stačí iba jeden nakoľko sa to spína sekvenčne. Odporúčam to spraviť na reálnom Arduine, aby bolo aj vidieť keď zvolíte nízku obnovovaciu frekvenciu
void Dec2Seven(int cislo) // S globálnou void Dec2Seven(int cislo, int* seven) // bez globálnej { /* [C D E G F A B ]Segmenty displeja [6 7 8 9 10 11 12] Piny Arduina [0 1 2 3 4 5 6 ] Index prvku poľa seven 1 - [1 0 0 0 0 0 1 ] Hodnoty poľa seven 2 - [0 1 1 1 0 1 1 ] 3 - [... */
Úloha 2.
pozrite si náplň cvičenia na [2] a vypracujte úlohy, ktoré tam sú zverejnené.