Princíp riadenia činnosti 7-segmentového LED displeja.

Cieľ lekcie 19:

  1. definovať vlastnosti vnútorného zapojenia 7-segment.LED displeja so spoločnou katódou, anódou
  2. vysvetliť princíp multiplexného riadenia displeja
  3. riadiť viacmiestny 7-segment. LED displej
  4. definovať kód na zobrazenie potrebnej číslice 0 ... F

V Časti III. - Úlohy praktických cvičení - sa zameriame na vyriešenie jednej úlohy


Návod na riešenie úlohy:

Spoznajte - Výkonový koncový stupeň - displej.

LED displej z 3 digitov obsahujúcich 7 svietiacich segmentov + 1 pre desatinnú bodku, ktorý budete používať na zobrazenie potrebujete detailne poznať. Takže prvou úlohou bude pozrieť si nápisy na ňom a vyhľadať dokumentáciu od výrobcu. Do rúk sa vám najskôr dostane typ KINGBRIGHT BC56-12GWA. Prvá úloha - nájdite ho v Internete. Možno sa dostanete k dokumentu, ktorý bude popisovať viacero podobných typov, preto sa musíte v dokumente zorientovať a nájsť najdôležitejšie informácie. Ak nenájdete pozrite si tento obrázok zapojenia súčiastky a vypíšte si dôležité informácie.

Ako ovládať BC56-12GWA?

Z obrázka vyplýva spôsob ako sa ovláda BC56-12GWA. Je jasné, že LED dióda svieti ak ňou tečie prúd od anódy ku katóde, t.j. anóda má vyšší potenciál a katóda nižší potenciál. Pri ovládaní ARDUINOM vyšší potenciál predstavuje logická 1 (t.j. +5V) a nižší potenciál predstavuje logická 0 (t.j. GND). Z obrázka v dokumentácii vidno, že každý digit má samostatnú spoločnú katódu pre všetky segmenty (spolu má digit 8 segmentov), ale anódy sú prepojené pre všetky digity paralelne (3 spoločné anódy na konkrétny segment). Pozrite si zapojenie ručne ovládaného displeja 3x7LED. Ak by sme uzemnili naraz všetky spoločné katódy a na anódové vstupy by sme priviedli kladný potenciál, rozsvietia sa všetky digity naraz a zobrazia to isté.  Jediný spôsob ako sa teda dá niečo zobraziť pre každý digit samostatne, ja taký pri ktorom v jednom čase budú na zem (GND) napojené len katódy jediného digitu. To sa dá uskutočniť jedine DYNAMICKY v krátkych časových intervaloch . Čo to znamená?

Naše oko má zotrvačnosť pri vnímaní dejov. Vďaka tomu vidíme film na obrazovke a svetlá so striedavým napájaním nám "viditeľne" neblikajú. Aby naše oko vnímalo dynamický dej spojito a neprerušovane - t.j. bez blikania - musí sa odohrávať v čase 1/100 sekundy. Algoritmus zobrazovania na displeji potom bude vyzerať takto:

  1. krok - pripojiť na všetky anódy kód, ktorý bude obsahovať vyšší potenciál (log 1) na tých anódach, ktoré chceme rozsvietiť. Na ostatných anódach zachováme potenciál katódy (žiadny potenciálový rozdiel - žiaden prúd - žiadne svetlo).
  2. krok - teraz môžeme spoločnú katódu digitu na 1/100 sekundy (10 milisekúnd) uzemniť (log 0) a nechať svietiť - záblesk digitu.
  3. krok - "vypneme" uzemnenie katódy digitu (privedením log 1) - všetko zhasne,
  4. krok - príprava nasledovného kódu na zobrazenie v poradí DIGIT1, DIGIT2 a DIGIT3 pre nový začiatok od bodu 1.

Ak zabezpečíme tento dynamický princíp prepínania digitov na displeji získame pocit neprerušovane zobrazovaného súboru troch čísel.

V nasledovnom programovom kóde sa bude opakovať sekvencia troch akcií, z ktorých každá sa bude trvať minimálne  10 milisekúnd. Rozanalyzujte tento jednoduchý program a vysvetlite, a experimentom odskúšajte. Budete ho potrebovať na ovládanie prepínania digitov.


         unsigned long timediff;;
         int cas=10;
         
         void loop()
         { 
            timediff=millis();                        // nastav nové počítanie času
            while(millis()-timediff<=cas) { AKCIA1 } //  Akcia1 bude trvať "cas" 
            
            timediff=millis();                        // nastav nové počítanie času
            while(millis()-timediff<=cas) { AKCIA2 } //  Akcia2 bude trvať "cas" 
            
            timediff=millis();                        // nastav nové počítanie času
            while(millis()-timediff<=cas) { AKCIA3 } //  Akcia3 bude trvať "cas" 
            }
        

 

Zapojenie LED

Na vyriešenie projektov potrebujete zvládnuť zapojenia LED diód v klasickom obvode pozostávajúceho zo: zdroja, ochranného odporu a aktívneho prvku  - LED diódy. Kombinácia napätia a ochranného odporu   rozhoduje o tom či LED bude fungovať alebo sa prepáli. Zdroj v našom prípade bude nahradený koncovým stupňom na výstupe Arduina, nakoľko tieto zvládajú cca 40mA prúdy potrebné na napájanie LED diód

Čo sa týka ARDUINA sú len dve možnosti použitia vstupno/výstupných digitálnych pinov:  OUTPUT (VÝSTUP) a INPUT (VSTUP). Piny DIGITAL označené na doske ARDUINA 0 až 13 predstavujú digitálne Výstupy a Vstupy. Konektor označený ANALOG IN obsahuje "analógové vstupy"  0 až 5, ale tieto sa dajú použiť aj ako DIGITAL 14 až 19.

Režim pinov sa nastavuje príkazmi. Na príklade si vysvetlíme ich význam:


              pinMode(13, OUTPUT);  // Nastav režim Vstupno/Výstupného pinu 13 na VÝSTUP
              pinMode(13, INPUT);   // Nastav režim Vstupno/Výstupného pinu 13 na VSTUP
    

Nasledovné inštrukcie vám pomôže pochopiť animácia s doskou ARDUINA.


              digitalWrite(13, HIGH);  // Nastav na pine 13 logickú úroveň HIGH t.j. log1
              digitalWrite(13, LOW);   // Nastav na pine 13 logickú úroveň LOW  t.j. log0 
    

Pre ovládanie displeja používajúceho paralelne 7 bitový kód by bolo výhodné, aby sa spínalo súčasne 7 segmentov digitu naraz. Doteraz sme preberali len tie metódy, ktoré s bitmi portu pracovali pomocou bitových operácií. Tieto majú svoj význam v prípade, keď máme nedostatok vstupno/výstupných bitov na jedinom porte Arduina.

Porty ARDUINA - možnosť paralelného zápisu kódu pre displej.

Konektor Arduina označený DIGITAL (0-13) je tvorená bitmi dvoch portov PORTD (0-7) a PORTB (0-5). Konektor ANALOG IN (0-5), použiteľný aj ako DIGITAL (14-19),  je tvorený bitmi PORTC (0-5). Spomenuté porty sú tvorené adresovateľnými registrami napojenými na vnútornú 8 bitovú dátovú zbernicu procesora, a súčasne na vstupno-výstupné piny (nožičky) mikropočítača. Smer prepojenia ich bitov určujú konfiguračné registre DDRB, DDRC a DDRD.  Viac o portoch  nájdete tu.

Obsah portov sa dá zmeniť jedinou VSTUPNO-VÝSTUPNOU operáciou naraz za kratší čas než je nastavenie 1 bitu bitovou operáciou. Jediné čo nás obmedzuje pri použití 7 bitového kódu je to, že jediný PORTD, ktorý má viac ako 6 bitov, má bity 0 až 3 využité na dôležité funkcie určené na externé prerušenie (int0= bit 2 a int1= bit 3) a na sériový prenos dát sériovou linkou (Rx= bit 0 a Tx= bit 1).  Ak tieto bity nechcete využívať,  máte na prenos 8 bitového kódu celý PORTD.

V prípade, že ovládané bity sa nachádzajú na dvoch rôznych portoch, môžeme použiť bitové operácie alebo komplikovanejšie hromadné operácie portov. Hromadné operácie portov, ktoré nastavia len časť bitov portu naraz, nesmú zasahovať do bitov určených pre iné účely. Pre naše potreby hromadného ovládania ukážeme si nastavenie 5 bitov PORTD (ktorý je za časťou konektora DIGITAL 0-7).  

Na hromadne zmeny časti bitov portu musíme používať konštanty (t.j. masky) a Booleovu algebru - konkrétne bitové operácie "&" (AND) a "|" (OR). Operácia sa vykonáva medzi 8 bitovými operandami (napr. maskou a obsahom registra) vždy len medzi bitmi tej istej rádovej pozície (t.j. 0 s 0, 1 s 1 atď.).  Obe operácie sú v niečom "agresívne" a v niečom "pasívne". Operácia AND je "agresívna" v bitoch obsahujúcich log0. Bit v maske obsahujúci log0 zmení výsledok operácie AND vždy na log0. Naopak bit v maske obsahujúci log1 ponechá po operácii AND stav ktorý má druhý operand - nezmenený. Operácia OR je "agresívna" v bitoch obsahujúcich log1.  Bit v maske obsahujúci log1 zmení výsledok operácie OR vždy na log1. Naopak bit v maske obsahujúci log0 ponechá po operácii OR stav ktorý má druhý operand - nezmenený.

Ako teda zmeniť časť bitov portu paralelnou operáciou? Každá zmena časti bitov portu sa musí uskutočniť vo dvoch etapách. 1. Nulovanie s AND a 2. zápis jednotiek s OR (alebo presne naopak: jedničkovenie s OR a zápis 0 pomocou AND). Na daný port najprv aplikujeme masku obsahujúcu binárne jednotky a nuly tak, že tam, kde budeme meniť obsah nastavíme log0 a aplikujeme operáciu AND. Následne tam kde chceme nastaviť log1 nastavíme v maske log1 a aplikujeme operáciu OR. 

Príklad:

Podrhnuté znaky v príklade sú "agresívne". Nasledovná ukážka najprv zapíše 8 bitový údaj a potom zmení horných 5 bitov bez toho aby ovplyvnil  najnižšie 3 bity aplikovaním operácie AND a následne OR (zápis operácie je v skrátenom tvare A = A oper B t.j.  A oper= B ):   


      PORTD  = B10101010; // bin. maska nastaví digitálne piny 7,5,3,1 na HIGH ostatné na LOW

      PORTD &= B00000111; // nastaví digitálne piny 7 až 3 na LOW ostatné ZACHOVÁ !!! 00000010

      PORTD |= B01010000; // nastaví digitálne piny 6 a 4 na HIGH ostatné ZACHOVÁ !!! 01010010 


DDRD je register určujúci smer prenosu pre Port D (Digitálne piny Arduina 0 až 7). Tieto bity v tomto registri riadia či piny v porte PORTD sú konfigurované ako vstupy alebo výstupy, napríklad:


     DDRD = B11111110;        // nastaví PORTD piny 1 až 7 ako výstup(HIGH), 
                               // a pin 0 ako vstup(LOW)

     DDRD = DDRD | B11111100; // bitová operácia OR (alebo) nastavila piny 2 až 7 ako výstupné  
                                // ale ponechala nastavenie pinov 0 a 1 pre sériovú linku Rx,Tx 
                                // DDRD bude B11111110 
	                  

 

Praktiky programovania - "Konverzia hodnoty na reťazec".

Ak budeme chcieť zobraziť číselný údaj, bude v počítači najskôr uložený ako binárne číslo typu int. Na displeji máme k dispozícii len 3 digity. Maximálne číslo 999. Z každého čísla vieme zobraziť len stovky, desiatky a jednotky. Ak by v údaji boli tisíce, neostáva nám nič iné - len zobraziť "Err" - chybový stav. Ne separovanie číslic z údaja nám poslúži jednoduchý program - separujJednotky(). Program používa špeciálnu premennú na ADRESOVANIE údajov typu "smerník" (angl. "pointer"). Výrazy priradené premennej pointer menia adresu na ktorú ukazuje. Ak chceme pracovať s ÚDAJOM na ktorý pointer ukazuje, potom sa pred názov pointra napíše znak * (nemýľte si ho s násobením!). Hviezdička a názov pointra  spolu vytvárajú  PREMENNÚ s ktorou sa narába ako s obyčajnou premennou.

Nasledovný program cez parameter adrPrem získa adresu premennej (znova opakujem - ide o premennú typu pointer - resp. "ukazujúca" na adresu). Pôvodný obsah na starej adrese prepíše príkazom *adrPrem= nový_obsah; . Hviezdička pred pointrom predstavuje význam "údajové miesto ktoré adresuje pointer". Aj keď inštrukcie funkcie sú uzavreté lokálne v jej bloku, pracujú s údajom nachádzajúcim sa mimo funkciu. Názov funkcie separujJednotky() použitý v nejakom výraze, bude obsahovať návratovú hodnotu v podobe odseparovanej hodnoty jednotky z premennej, na ktorú sa aplikovala funkcia. Pôvodný obsah celočíselnej premennej, predelený 10, sa zapíše na jej pôvodné miesto. Zvyšok po delení  - separovaná jednotka - sa vracia cez názov funkcie príkazom return((byte)jednotky);  (Poznámka: (byte) uvedený v príkaze pretypuje výsledok int na typ byte)


// originál číselného údaja 10 násobne zmenší a vráti hodnotu jednotky
     byte separujJednotky(int* adrPrem) // adrPrem je adresa premennej odovzdana cez parameter
     {
       int jednotky; 
 
       jednotky= (*adrPrem) % 10;  // odpamätaj "jednotky" z číselného údaja na adrese adrPrem 
       *adrPrem= (*adrPrem) / 10;  // celočíselný podiel pôvodnej premennej vrať na jej miesto 
  
       return ((byte)jednotky);    // odovzdaj separované jednotky ako návratovú hodnotu funkcie
       }

Nasleduje príklad použitia funkcie. V globálnom priestore sa nachádzajú premenné jed, des, stov, tis a val obsahujúci číselnú hodnotu, ktorú chceme separovať na číslice. Program separuje z pôvodnej hodnoty vždy len jednotky a zvyšok vráti desaťnásobne zmenšený do pôvodnej premennej. Po separácii sa ocitnú samostatne jednotky v premennej jed, desiatky v  premennej des, stovky v premennej stov a tisíce v premennej tis:


     int jed,des,stov,tis,val=958; 

     enum { medzera=10, znak_E=11, znak_r=12 }; // vid kódová tabuľka

     void loop()
     {
       jed= separujJednotky(&val);
       des= separujJednotky(&val);
       stov=separujJednotky(&val);
       tis= separujJednotky(&val);
      if  (tis) { stov=znak_E; des=znak_r; jed=znak_r; } 
      vypisCislo(stov,des,jed,2000);
       }    
  

Výhoda predošlého algoritmu s pointrami je v tom, že sa dá aplikovať na ľubovoľne veľké číslo. Ak chcete obísť použitie pointrov, na separovanie "len štyroch číslic" z čísla sa dá použiť nasledovný podprogram:


     int jed,des,stov,tis,val=958; 

     void separujTSDJ(int cislo) 
     {  
       jed=(byte)(cislo % 10); // zvyšok po delení 10
       cislo=cislo/10;         // t-s-d (tisice-stovky-desiatky) celočíselný podiel
       des=(byte)(cislo % 10);
       cislo=cislo/10;         // t-s  tisíce a stovky
       stov=(byte)(cislo % 10);
       tis=(byte)cislo/10;     // t    tisíce
       }

  // ...   niekde v programe použijete
   separujTSDJ(val);       // funkciu na separovanie číslic z premennej val 
  // ...

Dynamický proces zobrazovania - multiplex.

Riešení úlohy, ktoré by používalo prídavné pamäte pri každom "digite" displeja, by bolo veľmi jednoduché. Pri takomto "statickom" riešení by stačilo vložiť do pamäte digitu kód a ten by nonstop zobrazoval. Obsah by sa zmenil obyčajným prepisom. Takéto riešenie sa často používa, ale je ekonomicky náročnejšie.  

Trochu komplikovanejšie, ale lacnejšie, je využitie vlastností displeja BC56-12GWA, ktoré statický režim ani neumožňuje. Dôvodom je fakt, že odpovedajúce segmenty (a,b,c,d,e,f,g a DT-bodka) všetkých troch digitov sú paralelne spojené a vytvárajú 8 trojíc vstupných bodov pre zobrazovaný kód. Naopak všetky katódy LED diód každého jednotlivého digitu (t.j. každý digit má 8 katód LED) sú spojené do jediného odvodu prúdu pre každý digit osobitne. Ak by potenciál na odtoku prúdu bol rovnaký ako na vstupe - stav HIGH - segment nebude svietiť. Tým že zabránime napojiť spoločné katódy digitu na GND, zabránime digitu aby svietil. Ak by sme pripojili súčasne dve spoločné katódy digitov na GND, svietil by na digitoch ten istý znak.

V programe bude potrebné riešiť úlohu krátkeho cyklického prepínania katód jednotlivých digitov na potenciál GND.  Toto zabezpečí logická hodnota LOW z portu ARDUINA. Tu je ukážka programu zabezpečujúceho krátky impulzu GND na prvý z digitov 3x7 LED displeja pomocou volania programu blikniDigit(dig1pin); . Ak v priebehu 4 milisekund blikneme tým istým digitom opakovane - naše oko si nevšimne žiadne zmeny!

enum { dig1pin=4, dig2pin=3, dig3pin=2 };    // pridelenie spoločných katód displeja digitom


  void blikniDigit(int digit)
  {

    digitalWrite(digit,LOW);
    delay(1);                                   // krátky záblesk zobrazovaného digitu
    digitalWrite(digit,HIGH);
    }

Dostávame sa do dosť komplikovanej situácie. Spomeňme si  že hlavný program má  svoju cyklickú časť v ktorej musí vykonávať všetky prácu. Teraz vzniká problém, že musí obsluhovať dej prepínania jednotlivých digitov v čase menej ako 1/100 sekundy a okrem toho vykonávať ešte ďalšiu činnosť. Pri poruche tohto režimu by ste mohli zaregistrovať "kmitanie obrazu" displeja. Cyklická časť programu by mohla vyzerať nasledovne:


void loop()
{
  int scan; 
   
  scan=analogRead(0);         // nasnímaj hodnotu analógového senzora (0 až 1023)
  delay(5);                   // mala pauza na dokončenie merania  
  vypis3CifCislo(scan);       // zobraz údaj na displeji 
  }

V ukážke vidíte jednak snímanie hodnoty senzora a jednak zobrazovanie nasnímanej veličiny na displeji. Dej prebiehajúci 1/100 sekundy trvá 10ms. Takže všetky operácie pred a za funkciou vypis3CifCislo(scan); v cykle loop() môžu trvať maximálne 10ms ak zahrnieme aj delay(5); . Podľa rovnakých pravidiel musí fungovať samotná funkcia  vypis3CifCislo(scan);. Očakávame od tejto funkcie že okrem zobrazovania bude vyhodnocovať aj maximum hodnoty, ktorú vie zobraziť na 3 miestnom displeji (t.j. 999) a pri hodote 1000 a viac vypíše hlásenie "Err" . Pozrite si nasledovný program:



enum { medzera=10, znak_E=11, znak_r=12 }; // vid kódová tabuľka

int vypis3CifCislo(int hodnota)
{
  separujTSDJ(hodnota);                 // rozseparuj hodnotu na 4 údaje tis, stov, des a jed

  if (tis)                             // ak tis sa nerovná 0 
  { 
    stov=znak_E;                       // budeme zobrazovať správu "Err" 
    des=znak_r;
    jed=znak_r;
    }
  vypisCislo(stov,des,jed,1000);      // potom budeme zobrazovať 3 digity celých 1000 milisec
  }
  

Dostávame sa k jadru problému vypisCislo(stov,des,jed,1000); . Tu musíme vyriešiť problém s časom. Číslo sa bude zobrazovať 1000 milisec. Ale počas celého procesu musíme zároveň prepínať jednotlivé digity v čase maximálne 10milisec. Tu však nemôžeme použiť klasický delay(x); pretože počas celého času musia prebiehať všetky procesy v Arduine. No aj na to máme riešenie:


// program bude dynamicky zobrazovať 3 digity po dobu cas

// pridelenie pinov Arduina spoločným katódam displeja 3x7 LED
enum { dig1pin=4, dig2pin=3, dig3pin=2 }; 


void vypisCislo(byte cislo1, byte cislo2, byte cislo3, int cas)
{
  unsigned long startTime;         // premenná na meranie času
  
  startTime=millis();             // nastav počiatočný stav merania času
  resetDisplay(dig1pin);        // pin katódy pre digitC1 = zhasni
  resetDisplay(dig2pin);       // detto digitC2
  resetDisplay(dig3pin);      // detto digitC3
     
  while(millis()-startTime<=cas)  // porovnaj počiatočný čas s aktuálnym či sme neprečerpali čas
  { 
    setDisplay(kod7segZnaku[cislo1]);// nastav bity s kódom digitu na anodách displeja
    blikni(dig1pin);             // blikni krátky záblesk (t.j.katoda) digitu C1 po dobu delay(1)
    
    setDisplay(kod7segZnaku[cislo2]); // detto pre digit C2
    blikni(dig2pin);
    
    setDisplay(kod7segZnaku[cislo3]); // detto pre digit C3
    blikni(dig3pin);
    }  
  }
  

Funkcia millis() v príklade vracia veľkú hodnotu (unsigned long) lineárne narastajúceho času v milisekundách vždy keď ju o túto hodnotu požiadame. Zmenu hodnoty poskytovaného času zabezpečuje automaticky vnútorný generátor hodín mikropočítača. V programe nájdete funkciu resetDigit(). Má za úlohu vypnúť digity LED displeja na začiatku zobrazovania.


void resetDigit(int digit)
{
  digitalWrite(digit,HIGH);   //zhasni LED odpojením katódy od potenciálu 0V log0
  }
  

Premenné a dátové typy použité pre aplikáciu

 Znaky 7 segmentového displeja sú tvorené 7 LED diódami (Pozn.: pričom je tam aj 8. LED - bodka pri pravej päte) sa vytvárajú kombináciou 1 a 0(svieti/nesvieti). Na takomto displeji vieme zobraziť, tak aby sa dali od seba rozlíšiť, nasledovné znaky:  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, b, C, d, E, F, o, r, u, n a prázdny znak (medzeru). Na uloženie 8 bitovej kódovej tabuľky máme v C jazyku k dispozícii štruktúru - pole bajtov.  Vzor výberu z poľa vidíte na nasledovnej definícii premennej kod, nastavenú na konkrétnu hodnotu danú tabuľkou a indexom:

 byte kod=kod7segZnaku[index]; 

, kde kod je premenná typu byte rovnako ako prvky poľa. V hranatej zátvorke uvedený index je premenná alebo konštanta adresujúca prvok poľa počnúc hodnotou 0. Každý bajt v poli bude predstavovať 7 bitov segmentov digitu - t.j. číselného znaku (Pozn.: hexadecimálna sústava používa navyše aj digity A=10, B=11, C=12, D=13, E=14 a F=15, na zobrazovanie ktorých je 7 segmentový displej práve veľmi vhodný.). Nasleduje príklad poľa obsahujúceho kódy znakov "0" až "9", medzeru a dva znaky "E" a "r". Tie posledné použijeme na vypísanie chybového stavu "Err" od slova Error (angl. "Chyba"). Tu je vzor ako na to:


// POLE KÓDOV ZNAKOV ČÍSLIC a PÍSMEN "E", "r" a MEDZERA=off t.j. vypnuté                  
byte kod7segZnaku[13]=
             {// gfedcbaN  - priradenie k segmentom N- nepoužitý
                B01111111, //0
                B00001101, //1
                B10110111, //2
                B10011111, //3
                B11001101, //4
                B11011011, //5
                B11111011, //6
                B00001111, //7
                B11111111, //8
                B11011111, //9
                B00000001, //10 off = všetky anódy nastav na log0 - t.j. zhasni (vypíš medzeru)
                B11110011, //11 E   písmeno E na sedemsegmentovke
                B10100001  //12 r   písmeno r na sedemsegmentovke
                };
    

Výhodou tohto poľa je to, že hodnota číslice zodpovedá indexu s ktorým sa údaj dá vybrať z tabuľky. Ak bity budú pekne uložené vedľa seba na porte PORTD (konektor Arduina DIGITAL 7 až 0) potom jeho použitie môže byť takéto:


    PORTD=kod7segZnaku[5];        // vypíš kód znaku 5 na PORTD (DIGITAL 7-0)

Sú však prípady, keď potrebné bity nenájdete na jednom porte, ale potrebujete ich poskladať na viacerých portoch. Vtedy potrebujete separovať jednotlivé bity z kódového slova. Na to existuje viacero metód, ale teraz tu sa dozviete výnimočnú a elegantnú metódu používanú profesionálmi akými chcete byť.

Celá myšlienka spočíva v tom, že na element v poli kódovej tabuľky typu byte, chceme nahliadnuť ako na ešte menšie prvky - samostatné bity, aby sme ich potom samostatne nastavovali na bitoch portov Arduina. Na tento účel vieme vytvoriť dátový objekt nazývaný štruktúrovaná premenná ktorej priradíme názov napr. POLE8BIT ktorá bude obsahovať jednobitové premenné s názvami segmentov displeja a celá štruktúra sa bude ukladať práve do 1 bajtu:


// definícia TYPU 1 bajtovej štruktúrovanej premennej obsahujúca 1 bitové premenné 

struct POLE8BIT 
{ byte N:1;  // N ako nepoužitý
  byte a:1;
  byte b:1;
  byte c:1;
  byte d:1;
  byte e:1;
  byte f:1;
  byte g:1;
  } ;
 

Teraz využijeme možnosť inej štruktúry v C jazyku, ktorá do jedného a toho istého dátového priestoru umiestni dve premenné tak, že sa prekryjú. Takáto štruktúra sa nazýva - union. Priradíme jej názov napr. KODY7SEGM:


  
typedef union  // definícia TYPU umožňujúca bitový aj bajtový prístup k rovnakým dátam
{                  // union predstavuje dve premenné uložené na to isté miesto
  POLE8BIT segment;   // premenná typu pole 8 bitov vložených do 1 bajtu
      byte kod8bit;    // celočíselná kladná premenná veľkosti 1 bajt
  } KODY7SEGM;
  

Ako vidíte premenná segment je typu  štruktúry pozostávajúcej z 8 bitov a táto sa prekrýva s bajtovou premennou s názvom kod8bit. Keď zapíšeme niečo do premennej kod8bit, môžeme pristupovať k obsahu pomocou štruktúrovanej premennej segment, ktorá má podštruktúru s názvami bitov presne podľa anód sedem segmentového displeja. Štruktúrovanú premennú používate v kombinácii s podštruktúrou takto: segment.a, segment.b, ... a každý názov predstavuje  jeden bit. Konečný efekt v programe bude nasledovný:

 


// tento program umožňuje na účely riadenia displeja použiť ľubovoľnú kombináciu bitov Arduina

// pridelenie pinov Arduina anódam segmentov displeja 3x7 LED 
enum { segApin=12, segBpin=11, segCpin=10, segDpin=9, segEpin=8, segFpin=7, segGpin=6 }; 

void setDisplay(byte zobrKod)    
{
  KODY7SEGM kod;                 // union premenná prekrývajúca bajt kod8bit a pole bitov segment
  
  kod.kod8bit=zobrKod;        // vloženie formou celého bajtu aby sa následne konal výber po bitoch 
  
  digitalWrite(segApin, kod.segment.a );  // na pin konektora DIGITAL Arduina pridelený segmentu
  digitalWrite(segBpin, kod.segment.b );  // LED pošli bit segmentu podľa obsahu 8 bitového kódu
  digitalWrite(segCpin, kod.segment.c );
  digitalWrite(segDpin, kod.segment.d );
  digitalWrite(segEpin, kod.segment.e );
  digitalWrite(segFpin, kod.segment.f );
  digitalWrite(segGpin, kod.segment.g );
  }
  
  
void setDisplay(byte zobrKod) // alternatívny program využívajúci štandardnú funkciu bitRead(); 
{
  digitalWrite(segApin, bitRead(zobrKod,1) );  // na pin konektora DIGITAL Arduina pridelený segmentu
  digitalWrite(segBpin, bitRead(zobrKod,2) );  // LED pošli bit segmentu podľa obsahu 8 bitového kódu
  digitalWrite(segCpin, bitRead(zobrKod,3) );
  digitalWrite(segDpin, bitRead(zobrKod,4) );
  digitalWrite(segEpin, bitRead(zobrKod,5) );
  digitalWrite(segFpin, bitRead(zobrKod,6) );
  digitalWrite(segGpin, bitRead(zobrKod,7) );
  }


V programe vyššie máte dva spôsoby toho istého programu setDisplay(). V prvom vidíte elegantné riešenie práce s bitmi. Cely bajt najprv vložíte ako jeden kód a následne vyberáte jednotlivé bity podľa názvov segmentov. Program je čitateľný, zrozumiteľný a profesionálny! Ak ste ho pochopili pootvorili ste dvere do triedy profesionálnych programátorov. Ak navyše ovládate prácu s pointrami a objektové programovanie otvoria sa vám dvere dokorán. Druhý model setDisplay() využíva štandardnú funkciu bitRead(bajt, číslobitu);  ktorá sa nachádza v štandardnej zostave knižničných funkcií C jazyka Arduina. Údaj číslobitu čísluje bity bajtu sprava doľava počnúc číslom 0.

Riešenie displeja pomocou procesu bežiaceho na pozadí iných procesov.

Častou potrebou je riešiť aplikáciu, ktorá využíva displej. Programátor, však nemá priestor na riešenie drajvera pre displej (obslužný program displeja) nakoľko očakáva už hotové riešenie. V tomto našom prípade odovzdá jeden proces druhému hodnotu v premennej int value;  ktorý sa odseparuje do premennych int tis, stov, des, a jed; . Tam ich nájde proces ktorý sa bude nachádzať v programe specDelay() ktorý popri tom, že vytvára požadovanú pauzu v programe, navyše bude zabezpečovať užitočné služby ako napr. zobrazovanie na displeji.


    /*******************************************************************************
     *   Načítaj údaj zo sériovej linky (SerialMonitor) a nastav čas blikania LED. *
     *   Celý čas zobrazuj údaj aj na 3x7LED displeji                              *
     *******************************************************************************/
 
     const int ledBit = 3;     // výstupný pin pre LED - Aplikácia blikania
     int polCas=500;           // polcas blikania LED v milisec
     
     char inByte = 0;         // premenná pre prichádzajúci bajt
     int  value;
     byte zmena=0;            // sprava pre iný proces, že došlo ku zmene premennej value

     
     void doSerial()                   // OBSLUHA SÉRIOVÉHO KANÁLA 
     {      
      if (Serial.available())              // Hlavný cyklus prebieha pokiaľ sa neobjaví znak
       {
         value=0;                           // priprav sa na ďalšie číslo      
         zmena=1; 
         delay(100);                        // v tej chvíli počká lebo predpokladá viac znakov    
         while (Serial.available() > 0)    //  a v cykle začne vyberať z buffra znak za znakom,      
         { 
           inByte = Serial.read();
           value=value*10+(inByte-'0'); // extrahuj z číslice jej hodnotu pridaj jednotky
           Serial.write(inByte);        // vypíš na Ser.Mon.znak ktorý čítaš z buffra sériovej linky
           Serial.print(".");
           }
         // odteraz je hodnota value nastavená 
         Serial.print(" Nacital ");
         Serial.println(value,DEC);
         }
       }
       
     void setup()
     {
       Serial.begin(9600);    // inicializácia sériovej linky 
       resetDisplay(dig1pin);        // pin katódy pre digit1 = zhasni
       resetDisplay(dig2pin);       // detto digit2
       resetDisplay(dig3pin);      // detto digit3
       }
       
     
     void specDelay(unsigned long myDelay)
     { 
       unsigned long startTime;
       
       startTime=millis();                   // nastav nové počítanie času
       while(millis()-startTime<=myDelay)   // cyklus obsluhujúci služby min po dobu myDelay
       {                                                
         doSerial();                        // ošetrenie sériovej linky - nastaví value
         if (zmena)
         {
           polCas=value;
           separujTSDJ(value);
           if (tis)              // zobrazovanie tisicov vyvolá zobrazenie "Err"
           {
             stov=znak_E;
             des=znak_r;
             jed=znak_r;
             }
           zmena=0;
           }
         setDisplay(stov);
         blikniDigit(dig1pin);
         setDisplay(des);
         blikniDigit(dig2pin);         
         setDisplay(jed);
         blikniDigit(dig3pin);
         // ...  miesto pre ďalšie akcie v reálnom čase
         }                      
       }
            
     void loop()
     {  
        // ...
        digitalWrite(ledBit,HIGH);  // zapni LED 
        specDelay(polCas);          // Počkaj  
        digitalWrite(ledBit,LOW);  // vypni LED 
        specDelay(polCas);         // Počkaj 
        // ...  
        }
                                                                        SeriaInBlinkLED3x7displ

 

Vašou úlohou je teraz poskladať celú aplikáciu dokopy. 

 


Vzorové príklady vo vývojovom prostredí Arduina ((Examples/1.Basics/... )

  1. DigitalWrite - Digitálny výstup
  2. Zobraz polohu bežca odporového senzora (potenciometra) -  Digitálny výstup na 3x7 LED displej
  3. Vzorový program - 3x7 LED displej so SerialMonitorom

 

Kontrolné otázky

  1. Čo je to port a kde nájdete port s označením PORTD na doske Arduina?
  2. Čo to znamená spoločná katóda resp. spoločná anóda pri LED displejoch?
  3. Čo si predstavujete pod pojmom statické a dynamické riešenie?
  4. Aký dostatočný krátky čas musíte dodržiavať medzi zmenami zobrazenia, aby si to naše oko nevšimlo?
  5. Prečo displej typu KINGBRIGHT BC56-12GWA nedokáže staticky zobrazovať 3-ciferný údaj?
  6. Akým príkazom naplníte PORTD jednou alebo max. dvomi operáciami?
  7. Akým algoritmom odseparujete hodnoty jednotlivých rádov z celočíselnej premennej obsahujúcej viacciferný údaj?
  8. Vysvetlite význam inštrukcie if (!tis) prikazA else prikazB ak tis je premenná typu byte?
  9. Ako pred plníte binárne konštanty do premennej typu pole bajtov?
  10. Vysvetlite príkaz  while(millis()-timediff<=cas) {Prikaz} ?
  11. Čo je to štruktúrovaná premenná?
  12. Ako sa líši premenná typu union od klasických premenných int, char, float, a pod.?
  13. Vysvetlite prečo privedením log1 na katódy LED displeja vypnete zobrazovanie?

 

 
   
 
  Moderné vzdelávanie pre vedomostnú spoločnosť / Projekt je spolufinancovaný zo zdrojov EÚÚÚ
Predmet: Elektrotechnika / Autor: Ing. Jaroslav Janoušek