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. 

void setup()
{
  // Pin 13 bude výstup, lebo na neho pripojíme LED diódu, ktorú potrebujeme napájať
  pinMode(13, OUTPUT);
  // Pin 10 bude vstup. Pripojíme naň spínač cez, ktorý sa pin pripojí na zem 
  // alebo zdanlivo bude "vo vzduchu".
  pinMode(10, INPUT);
  // Ono by sa mohlo zdať, že pin nie je pripojený k ničomu ak je spínač v polohe vľavo.
  // Nie je to pravda. Nasledujúcim príkazom tento pin vo vnútri procesora pripojíme
  // cez vysoký odpor na +5V a teda ak je spínač v pozícií vľavo, na pine bude logická 1.
  // Ak je spínač v pozícii vpravo, spínačom potečie veľmi maličký prúd smerom na zem a 
  // na pine bude hodnota logickej 0. 
  digitalWrite(10, HIGH);
}
void loop()
{
  // Ak sa na pine nachádza logická 1 LED svieti.(Okno je otvorené - obvod bol prerušený )
  if( digitalRead(10) == 1)
    digitalWrite(13, HIGH);
  else // Ak sa na pine nachádza logická 0 LED nesvieti.(Okno je zatvorené)
    digitalWrite(13, LOW);
}

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); 
}

Všetko podstatné o tlačidlách 

Ako nie!

Na internete sa môžeme stretnúť s mnohými spôsobmi pripojenia tlačidiel na digitálne piny. Asi úplne jahorší spôsob je zobrazený na obr. vpravo. Takéto zapojenie sa môže chovať nepredvídateľne a navyše je to zaručený spôsob ako zničiť port ak sa nechá v režime výstupu! Digitálny pin doslova "visí vo vzduchu" pokiaľ nie je tlačidlo stlačené. Nikdy to takto nerobte!

Ako áno.

Existuje viacero správnych riešení ale najčastejšie sa stretnete s takzvaným PULL UP a PULL DOWN zapojením. Tieto zapojenia sa líšia tým ktorý stav je pine stabilný - teda prítomný v čase keď tlačidlo nie je zopnuté.

PULL UP - Pin je neustále pripojený cez rezistor (10 kOhm) na napájacie napätie +5V a môže ním pretekať veľmi malý prúd. V prípade stlačenia tlačidla sa pin pripojí k zemi. Obvodom zdroj>rezistor>tlačidlo>zem  tečie malý prúd ale na pine je meraná hodnota 0V. 

PULL DOWN - Pin je neustále pripojený na cez rezistor (10 kOhm) na zem (GND) a v prípade stlačenia tlačidla sa pin pripojí k napätiu +5V. Obvodom zdroj>tlačidlo>rezistor>zem  tečie malý prúd a na pine je meraná hodnota +5V. Pokiaľ tlačidlo nie je zopnuté obvodom netečie žiadny prúd.  

INTERNAL PULL UP - V tomto prípade je možné využiť vstavaný rezistor, ktorý je možné programovo pripojiť ku každému I/O vstupu. Obvod sa chová rovnako ako v prípade klasického PULL UP riešenia

Tieto tri spôsoby sú spolu s obslužným kódom zobrazené nižšie. Po stlačení tlačidla má dôjsť k rozsvieteniu diódy pripojenej na pin 13. 

ZLE RIEŠENIE

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);
}

Switch Debounce in Digital Circuits - GeeksforGeeks



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ťouJednotlivé 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 (wink) 

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é.



7 segment display pinout cc ca - theoryCIRCUIT - Do It Yourself Electronics  Projects

  • No labels