IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

10. diel - Sedemsegmentový display a polia pre Arduino

V tomto diele si k Arduino pripojíme 7-mi segmentový display. Že neviete, čo to je? Poznáte ho určite všetci, len si to momentálne neuvedomujete. Jedná sa o ten display, ktorý zobrazuje taká tá hranatá čísla a stačí mu k tomu 7 dielikov, teda segmentov. Niektoré displaya majú aj ôsmy segment, často ako desatinnú bodku za číslom, ale tou sa zaoberať nebudeme.

sedemsegmentový displej - Arduino

Už si spomínate?

A teraz trocha teórie. Čísla zo zobrazujú jednoducho tak, že rozsvietime daný segment. Takže pre 0 rozsvietime A, B, C, D, E, F, pre 1 B, C, pre 2 A, B, D, E, G a tak ďalej. Display potom má určité výstupy, ktorými sa treba riadiť. Môže to byť napríklad takto:

Výstupy sedmisegmentového displeja - Arduino

Ale nájdu sa aj výrobcovia, ktorí to majú inak. Preto vždy odporúčam si prejsť datasheet zakúpeného displaya. Potom už to nie je žiadna veda. COM, niekedy značený ako anóda alebo GND, pripojíme, ako už tušíte, na zem. A potom privedením prúdu do daných pinov rozsvietime daný segment. Donedávna som žil v ilúzii, že to takto funguje u všetkých displejov. Tie čo som používal som si vyrábal, pretože k zohnaniu boli len tie malé. Raz sa mi jeden ten malý dostal do ruky, nejaký zo starej práčky a ten to tak tiež mal. Potom som ale zistil, že u tých novších je to presne naopak - na COM sa privedie napätie a segmenty sa rozsvietia jednotlivým uzemnením daných pinov. To ale v našom programe vôbec nevadí, my len znegujeme dáta posielané k display, v danej časti to vysvetlím. Takže máme vyriešený display, ale chcelo by to nejaké softvérové riešenie, že? Ale ako na to?

Dajme tomu, že máme premenou číslo, v ktorej je číslo na zobrazenie. Takže čo treba toto:

void setup()
{
    pinMode(Asegment, OUTPUT);
    pinMode(Bsegment, OUTPUT);
    //a tak dále
}

Void loop()
{
    if (cislo == 0)
    {
    digitalWrite(Asegment, HIGH);
    digitalWrite(Bsegment, HIGH);
    digitalWrite(Csegment, HIGH);
    digitalWrite(Dsegment, HIGH);
    digitalWrite(Esegment, HIGH);
    digitalWrite(Fsegment, HIGH);
    digitalWrite(Gsegment, LOW); //kdyby byl zaplý
}
    if(cislo == 1)
    {
        digitalWrite(Asegment, LOW);
    //a tak dále…

Asi tušíte, že takto by to naozaj nešlo. A čo napríklad, keby sme mali číslica 2? A nedajbože dokonca 3!

if(cislo == 827) {
    digitalWrite(PrvniCisliceSegmentA, HIGH); //přeji příjemnou zábavu

Čo s tým? My si vytvoríme 2D pole, do ktorého si uložíme všetko čo potrebujeme a tie neskutočné digitalWrite sa urobia za nás.

Odbočka k premenným a poliam: Základná premennou treba int (nielen int), kam uložíme jedno číslo, nič viac. Potom existuje pole (nie to, kde sa pestuje kukurica) - taký 2-rozmerný int. Do toho môžeme uložiť hodnôt viac a potom je vyvolať. napríklad:

byte mojepole[pocethodnot] = { prvnihodnota, druhahodnota, tretihodnota }

vytvorí pole s 3 hodnotami. Dôležité je uviesť počet hodnôt do hranatých zátvoriek za názov. A ako hodnotu vyvolať?

Serial.println(mojepole[2]);

A čo to urobí? Vypíše hodnotu z poľa, konkrétne tretihodnota. Prečo? Veď som zadal 2? Pretože programátori začínajú vždy od nuly, takže prvnihodnota bude mojepole [0], druhahodnota bude mojepole [1] a tak ďalej. A čo 2D pole? To je akési pole polí

//           4             3
byte moje2dpole[pocetpoli][pocethodnotvpoli] = {
    {1,2,3},
    {4,5,6},
    {7,8,9},
    {10,11,12},
    };

A ako hodnoty vyvolať? moje2dpole [0] [1] nám dá druhú hodnotu v prvom poli, takže 2. Koniec odbočky.

To by sme mali ako-tak digitalWrite, ale čo pinMode? Predsa to nebudeme u každého segmentu vypisovať, že? Ak cítite pole, tak cítite správne. Urobíme si pole, do ktorého zadáme piny segmentov. Jednak aby sme nemuseli u každému písať pinMode a navyše nám pomôže pri zapínaní / vypínaní segmentov. Takže začneme tým, že si nastavíme tie piny. V tomto príklade použijem 2-miestny display, pre viacmiestne je to prakticky to isté, ak nie, tak to spomeniem až tam, kde to bude potrebné.

Ja mám vo zvyku si pri pracovanie so 7-mi segmentovkou pridať do kódu "nákres" toho displaya, potom sa s tým lepšie pracuje. A rovno si definujeme pole sa piny:

/*
    ---6--
   |      |
   5      1
   |      |
    --7---
   |      |
   4      2
   |      |
   ----3---
*/


// segmenty       1  2  3 4 5 6 7
byte Lcislo[7] = {11,10,9,5,6,7,8};

// segmenty       1 2 3 4  5  6 7
byte Pcislo[7] = {4,3,2,12,14,15,13};
/*
Nemusíte se přímo řídit těmi piny. Tady jsou uvedeny jako příklad z mého posledního projektu.
Ty čísla v poli opravdu záleží na tom, jaký pin si kam připojíte.
Jen musíte dodržet to pořadí, doporučuji držet se "nákresu" na začátku kódu.
*/

Máme zadané hodnoty. Ale čo s nimi? My si napíšeme funkciu, ktorá vezme polia s piny a na každom vyvolá pinMode.

void nastavvystup(byte piny[])
{
    for(int i = 0; i < 7; i++)
    {
        pinMode(piny[i], OUTPUT);
    }
}

Tu je použitý cyklus for. Ten najskôr založí premenou typu int a názvom ia dá jej hodnotu 0 (int i = 0). Názov i je také "nepísané pravidlo", používa sa v týchto veciach skoro vždy, keď je voľné. Potom je podmienka aj <7. To znamená, že for sa bude opakovať, dokiaľ bude aj menšie než 7. Posledný aj ++, znamená, že sa vždy po vykonaní for ki pripočíta jedna a for sa vykoná znova. Tým pádom sa for vykoná s hodnotami v i 0,1,2,3,4,5,6 - čo zodpovedá indexom pinov v našom poli. Takže len zavoláme pinMode, miesto pinu vložíme pole piny [], kam si vložíme poľa Lcislo a Pcislo, až budeme funkciu volať. A vďaka indexovanie sa nám zavolajú všetky piny v poli, takže máme vyriešený pinMode. Už stačí len v setupu funkcii vyvolať

nastavvystup(Lcislo);
nastavvystup(Pcislo);

a máme nastavený pinMode.

Takže to máme pripojený display. A čo na ňom zobraziť?

int cislo = 0;

Ale bolo by predsa nudné zobrazovať len nulu. Riešenie je len na vás, ja nechám náhodne generovať čísla v rozmedzí 0 - 99, čo je limit displaya. K tomu nám poslúži funkcie random. Takže si na začiatok LOOP pridáme oneskorenie a výber náhodného čísla:

delay(1000);
cislo = random(100); //náhodná od 0 do 99. pokud bychom chtěli třeba jen 20-99, tak random(20,100)
//z toho vyplývá - random(minimalni,maximalni);

A teraz zobrazovanie čísel na displayi. Ako som riek, použijeme na to 2D pole, do ktorého uložíme hodnoty pre jednotlivé čísla a to potom prejdeme, hodnoty z neho vytiahneme a pošleme funkciu digitalWrite. Objaví sa nám tu cyklu for, rovnako ako predtým. Tu musíme dať v 2D poli pozor na niekoľko vecí:

  1. Dodržujte pri zadávaní hodnôt piny v tom poradí, v akom sú v poliach na začiatku kódu - stále sa držte nákresu na začiatku
  2. Čísla zadávajte tak, aby zodpovedalo indexom - pod indexom 0 budú hodnoty na zobrazenie 0, pod 1 jedna a tak ďalej. Ušetríte si tým zbytočnú prácu pri volaní, pretože bude stačiť len zavolať s tým číslom, ktoré chceme zobraziť.
void zobrazcislo(byte piny[], int hodnota)
{
    datasegmentu[11][7] = {
    {1,1,1,1,1,1,0},  //nula    index 0
    {1,1,0,0,0,0,0},  //jedna   index 1
    {1,0,1,1,0,1,1},  //dva     index 2
    {1,1,1,0,0,1,1},  //tri     index 3
    {1,1,0,0,1,0,1},  //ctyri   index 4
    {0,1,1,0,1,1,1},  //pet     index 5
    {1,1,1,1,0,1,1},  //sest    index 6
    {1,1,0,0,0,0,1},  //sedm    index 7
    {1,1,1,1,1,1,1},  //osm     index 8
    {1,1,0,0,1,1,1},  //devet   index 9
    {0,0,0,0,0,0,0},  //nic     index 10
    };

    for(int i = 0; i < 7; i++)
    {
        digitalWrite(piny[i], datasegmentu[hodnota][i]);
        /*
            Pokud máte ten typ displaye, kde se přivede proud na COM a uzemněním se ovládá,
            tak stačí před datasegmentu[][] vložit vykřičník ->
            digitalWrite(piny[i], !datasegmentu[hodnota][i]);
            takže se bude na zaplá čísla posílat nula a na vyplá jedna. Nebojte se, nic
            nezkratujete. Jen si nejdříve zjistěte, jaký display máte.
            Pokud nejste líní, tak v 2D poli prohoďte všechny 1 a 0
        */
    }
}

Takže, to by bolo zobrazovanie. Ak si funkciu už teraz skúsite, ako parameter jej odovzdáte jedno pole sa segmenty a ako druhý číslo v rozmedzí 0-9, tak sa vám zobrazí. Ale čo dvojciferné čísla? V poli máme indexy len do desať, pričom desať display vymaže. Takže si vezmeme nejaké pekné dvojciferné číslo - napríklad 64 a na ňom si vysvetlíme, ako to bude fungovať. Spomínate si ešte na delenie so zvyškom? 64 deleno 10 je 6, zvyšok 4. Podobne na tú pôjdeme tu, len tu nám program vypočíta koľko je zvyšok a výsledok "zahodí". Takže, keď "vydelíme" 64 desiatich, vyjde nám 4. Máme hodnotu, ktorú zobrazíme na mieste jednotiek. A čo desiatky? Máte číslo, viete, že je dvojciferné, že na mieste jednotiek je 4 a potrebujeme vytiahnuť to z desiatok. Takže od neho odpočítame jednotky a vydelíme (už normálne) desiatich: (64-4) / 10 = 10.

Ak budeme mať 3-ciferné číslo, tak proste vydelíme 100 miesto 10, vykonáme rovnaký postup, vytiahneme z toho desiatky, na tých prevedieme to čo je popísané vyššie atď. Teraz to len pošleme funkciu na zobrazenie. Ale tak jednoduché to nebude. Musíme najskôr rozlíšiť, či je číslo jednociferné, to ho proste pošleme funkciu a nič s ním nerobíme. Ak je dvojciferné, tak ho preženie prepočtom, ak trojciferné, tak ho tiež prepočítame a tak ďalej. Aby sme si nerobili neporiadok v Loop, tak si na to vytvoríme ďalšiu funkciu, do ktorej to všetko zabalíme, ktorá obstará všetko a nám len bude stačiť ju zavolať po každom riadku, kde by mohlo dôjsť k zmene zobrazovanej hodnoty.

void zjisticislo()
{
     if(cislo < 10) //menší než deset
     {
       zobrazcislo(Pcislo, cislo);
       zobrazcislo(Lcislo, 10); //nastaví na první číslici nic, kdyby třeba zbyla 1 při přechodu 10 > 9
     }
     else if((cislo > 9 )& (cislo <100)) //pokud je 10-99(dvojciferné)
     {
       int jednotky = cislo % 10; //získá jednotky
       int desitky = (cislo - jednotky)/10; //odečte z dvojciferného jednotky a vydělí 10,aby získal desítky

       zobrazcislo(Lcislo, desitky);
       zobrazcislo(Pcislo, jednotky);
     }
     else
     {
       cislo = 0; //aby tam nedělaly špatná hodnoty bordel
     }
}

Veľkou výhodou je, že nemusíme funkciu odovzdávať parameter cislo, pretože sme ho deklarovali na začiatku kódu mimo nejakú funkciu, takže je prístupný všade. Ak budete chcieť stovky, tak to bude vyzerať nejako takto:

if((cislo > 99) & (cislo < 1000)
{
    dvojmistne = cislo % 100;
    stovky = (cislo - dvojmistne) /100;
    jednotky = dvojmistne % 10;
    desitky = (dvojmistne - jednotky) / 10;
    zobrazcislo(1cislo, stovky );
    zobrazcislo(2cislo, desitky );
    zobrazcislo(3cislo, jednotky );
}

Tisíce si snáď už zvládnete urobiť sami :) To by bolo všetko. Teraz už to len nejako poskladať dohromady. Ak ste neprišli ako na to, tak celý kód je buď k stiahnutiu alebo na konci článku.

Pôvodne som k demonštrácii zamýšľal použiť veľký 2x2 miestny display, ktorý momentálne staviam, ale zasiahli zákony schválnosti a ešte nie je hotový. Podarilo sa mi nájsť display z obrázku na začiatku, len mu nefungujú niektoré segmenty (bohužiaľ naozaj nič iného nebolo). Takže som zavrhol náhodné čísla a tú časť zamenil za:

void loop()
{
        for( int cislo = 0; cislo < 100; cislo++)
    {
        zjisticislo();
        delay(500)  ;
    }
}

čo zobrazuje postupne čísla od 0 do 99. Bude potrebné trochu fantázie, aby ste videli, že to funguje. Pre pomoc s fantáziou, toto obratené F 6 je 88 : D

Rozbitý sedemsegmentový displej - Arduino

Na videu nižšie je vidieť, ako na display "naskakujú" "čísla" od 0 do 99 (potom som to niekde uťal). Sľubujem, že až budem mať hotový ten funkčný, tak ten program spustím na ňom a dám ho sem :)

Ako som sľúbil, tu je k videniu program pre náhodné čísla na nejakom funkčnom displayi (ospravedlňujem sa za kvalitu, nič iné nebolo po ruke).
Kompletný zdrojový kód:
/*
    ---6--
   |      |
   5      1
   |      |
    --7---
   |      |
   4      2
   |      |
   ----3---
*/

// segmenty       1  2  3 4 5 6 7
byte Lcislo[7] = {11,10,9,5,6,7,8};

// segmenty       1 2 3 4  5  6  7
byte Pcislo[7] = {4,3,2,12,14,15,13};

int cislo = 0;
/*
Nemusíte se přímo řídit těmi piny. Tady jsou uvedeny jako příklad z mého posledního projektu.
Ta čísla v poli opravdu záleží na tom, jaký pin si kam připojíte.
Jen musíte dodržet to pořadí, doporučuji držet se "nákresu" na začátku kódu.
*/
void setup()
{
  nastavvystup(Lcislo);
  nastavvystup(Pcislo);
}

void loop()     //všiměte si, jak je loop krátký
{
  delay(1000);
  cislo = random(99);
  zjisticislo();
}

void zjisticislo()
{
 if(cislo < 10) //menší než deset
     {
       zobrazcislo(Pcislo, cislo);
       zobrazcislo(Lcislo, 10); //nastaví na první číslici nic, kdyby třeba zbyla 1 při přechodu 10 > 9
     }
     else if((cislo > 9 )& (cislo <100)) //pokud je 10-99(dvojciferné)
     {
       int jednotky = cislo % 10; //získá jednotky
       int desitky = (cislo - jednotky)/10; //odečte z dvojciferného jednotky a vydělí 10,aby získal desítky

       zobrazcislo(Lcislo, desitky);
       zobrazcislo(Pcislo, jednotky);
     }
     else
     {
       cislo = 0; //aby tam nedělaly špatné hodnoty nepořádek
     }
}

void nastavvystup(byte piny[])
{
    for(int i = 0; i < 7; i++)
    {
        pinMode(piny[i], OUTPUT);
    }
}

void zobrazcislo(byte piny[], int hodnota)
{
    byte datasegmentu[11][7] = {
    {1,1,1,1,1,1,0},  //nula    index 0
    {1,1,0,0,0,0,0},  //jedna   index 1
    {1,0,1,1,0,1,1},  //dva     index 2
    {1,1,1,0,0,1,1},  //tri     index 3
    {1,1,0,0,1,0,1},  //ctyri   index 4
    {0,1,1,0,1,1,1},  //pet     index 5
    {1,1,1,1,0,1,1},  //sest    index 6
    {1,1,0,0,0,0,1},  //sedm    index 7
    {1,1,1,1,1,1,1},  //osm     index 8
    {1,1,0,0,1,1,1},  //devet   index 9
    {0,0,0,0,0,0,0},  //nic     index 10
    };

    for(int i = 0; i < 7; i++)
    {
        digitalWrite(piny[i], datasegmentu[hodnota][i]);
    }
}

To by bolo pre dnešok všetko. Nabudúce sa pravdepodobne (plány nie sú isté) zameriame znova na konštrukciu jazyka, ktorou sa mi minule podarilo odbiť a pokiaľ na to zostane priestor, tak si vytvoríme knižnicu pre tento prípad, takže by sa nám kód obmedzil na niečo v štýle:

#include knihovnasedmisegmentaku.h
pole s piny;
2dpole s hodnoty segmentu;
<vlastni program>
zobrazcislo;

a nebolo by v ňom toľko deklaráciou funkcií, tie by boli pekne schované niekde inde.


 

Predchádzajúci článok
Arduino - Samostatný čip ATmega 328p
Všetky články v sekcii
Arduino
Preskočiť článok
(neodporúčame)
Arduino - Jazyk
Článok pre vás napísal Adam Ježek
Avatar
Užívateľské hodnotenie:
2 hlasov
Autor se převážně věnuje Arduinu a psaní tutoriálů z této oblasti, občas napíše příležitostně nějakou tu zprávičku. Většinu svého volného času momentálně věnuje Linuxu a/nebo Raspberry Pi. Také umí C#, HTML, CSS, PHP a Python.
Aktivity