10. díl - Sedmisegmentový display a pole pro Arduino

Hardware PC Arduino Sedmisegmentový display a pole pro Arduino

V tomto díle si k Arduino připojíme 7-mi segmentový display. Že nevíte, co to je? Znáte jej určitě všichni, jen si to momentálně neuvědomujete. Jedná se o ten display, který zobrazuje taková ta hranatá čísla a stačí mu k tomu 7 dílků, tedy segmentů. Některé displaye mají i osmý segment, často jako desetinnou tečku za číslem, ale tou se zabývat nebudeme.

Sedmisegmentový displej

Už si vzpomínáte?

A teď trocha teorie. Čísla ze zobrazují jednoduše tak, že rozsvítíme daný segment. Takže pro 0 rozsvítíme A, B, C, D, E, F, pro 1 B, C, pro 2 A, B, D, E, G a tak dále. Display pak má určité výstupy, kterými je třeba se řídit. Může to být třeba takto:

Výstupy sedmisegmentového displeje

Ale najdou se i výrobci, kteří to mají jinak. Proto vždy doporučuji si projít datasheet zakoupeného displaye. Pak už to není žádná věda. COM, někdy značený jako anoda nebo GND, připojíme, jak už tušíte, na zem. A pak přivedením proudu do daných pinů rozsvítíme daný segment. Donedávna jsem žil v iluzi, že to takto funguje u všech displayů. Ty co jsem používal jsem si vyráběl, protože k sehnání byly jen ty malé. Jednou se mi jeden ten malý dostal do ruky, nějaký ze staré pračky a ten to tak také měl. Pak jsem ale zjistil, že u těch novějších je to přesně naopak - na COM se přivede napětí a segmenty se rozsvěcí jednotlivým uzemněním daných pinů. To ale v našem programu vůbec nevadí, my jen znegujeme data posílaná k display, v dané části to vysvětlím. Takže máme vyřešený display, ale chtělo by to nějaké softwarové řešení, že? Ale jak na to?

Dejme tomu, že máme proměnou číslo, ve které je číslo k zobrazení. Takže co třeba 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 takhle by to opravdu nešlo. A co třeba, kdybychom měli číslice 2? A nedejbože dokonce 3!

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

Co s tím? My si vytvoříme 2D pole, do kterého si uložíme vše co potřebujeme a ty neskutečné digitalWrite se udělají za nás.

Odbočka k proměnným a polím: Základní proměnnou je třeba int(nejenom int), kam uložíme jedno číslo, nic víc. Pak existuje pole (ne to, kde se pěstuje kukuřice) - takový 2-rozměrný int. Do toho můžeme uložit hodnot více a pak je vyvolat. Například:

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

vytvoří pole se 3 hodnotami. Důležité je uvést počet hodnot do hranatých závorek za název. A jak hodnotu vyvolat?

Serial.println(mojepole[2]);

A co to udělá? Vypíše hodnotu z pole, konkrétně tretihodnota. Proč? Vždyť jsem zadal 2? Protože programátoři začínají vždy od nuly, takže prvnihodnota bude mojepole[0], druhahodnota bude mojepole[1] a tak dále. A co 2D pole? To je jakési pole polí

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

A jak hodnoty vyvolat? moje2dpole[0][1] nám dá druhou hodnotu v prvním poli, takže 2. Konec odbočky.

To bychom měli jakžtakž digitalWrite, ale co pinMode? Přece to nebudeme u každého segmentu vypisovat, že? Pokud cítíte pole, tak cítíte správně. Uděláme si pole, do kterého zadáme piny segmentů. Jednak abychom nemuseli u každému psát pinMode a navíc nám pomůže při zapínání/vypínání segmentů. Takže začneme tím, že si nastavíme ty piny. V tomto příkladu použiji 2-místný display, pro vícemístný je to prakticky to samé, pokud ne, tak to zmíním až tam, kde to bude třeba.

Já mám ve zvyku si při pracování se 7-mi segmentovkou přidat do kódu "nákres" toho displaye, pak se s tím lépe pracuje. A rovnou si definujeme pole se 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 co s nimi? My si napíšeme funkci, která vezme pole s piny a na každém vyvolá pinMode.

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

Zde je použitý cyklus for. Ten nejdříve založí proměnou typu int a názvem i a dá jí hodnotu 0 (int i = 0). Název i je takové "nepsané pravidlo", používá se v těchto věcech skoro vždy, když je volné. Poté je podmínka i < 7. To znamená, že for se bude opakovat, dokud bude i menší než 7. Poslední i++, znamená, že se vždy po vykonání for k i přičte jedna a for se provede znovu. Tím pádem se for provede s hodnotami v i 0,1,2,3,4,5,6 - což odpovídá indexům pinů v našem poli. Takže jen zavoláme pinMode, místo pinu vložíme pole piny[], kam si vložíme pole Lcislo a Pcislo, až budeme funkci volat. A díky indexování se nám zavolají všechny piny v poli, takže máme vyřešený pinMode. Už stačí jenom v setupu funkci vyvolat

nastavvystup(Lcislo);
nastavvystup(Pcislo);

a máme nastavený pinMode.

Takže to máme připojený display. A co na něm zobrazit?

int cislo = 0;

Ale bylo by přece nudné zobrazovat jen nulu. Řešení je jen na vás, já nechám náhodně generovat čísla v rozmezí 0 - 99, což je limit displaye. K tomu nám poslouží funkce random. Takže si na začátek loopu přidáme prodlevu a výběr 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 nyní zobrazování čísel na displayi. Jak jsem řek, použijeme na to 2D pole, do kterého uložíme hodnoty pro jednotlivá čísla a to poté projedeme, hodnoty z něj vytáhneme a pošleme funkci digitalWrite. Objeví se nám tady cyklu for, stejně jako předtím. Zde musíme dát v 2D poli pozor na několik věcí:

  1. Dodržujte při zadávání hodnot piny v tom pořadí, v jakém jsou v polích na začátku kódu - stále se držte nákresu na začátku
  2. Čísla zadávejte tak, aby odpovídalo indexům - pod indexem 0 budou hodnoty k zobrazení 0, pod 1 jedna a tak dále. Ušetříte si tím zbytečnou práci při volání, protože bude stačit jen zavolat s tím číslem, které chceme zobrazit.
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 bylo zobrazování. Pokud si funkci již teď zkusíte, jako parametr jí předáte jedno pole se segmenty a jako druhý číslo v rozmezí 0-9, tak se vám zobrazí. Ale co dvojciferná čísla? V poli máme indexy jen do deseti, přičemž deset display vymaže. Takže si vezmeme nějaké pěkné dvojciferné číslo - třeba 64 a na něm si vysvětlíme, jak to bude fungovat. Vzpomínáte si ještě na dělení se zbytkem? 64 děleno 10 je 6, zbytek 4. Podobně na tu půjdeme tady, jen zde nám program vypočítá kolik je zbytek a výsledek "zahodí". Takže, když "vydělíme" 64 deseti, vyjde nám 4. Máme hodnotu, kterou zobrazíme na místě jednotek. A co desítky? Máte číslo, víte, že je dvojciferné, že na místě jednotek je 4 a potřebujeme vytáhnout to z desítek. Takže od něj odečteme jednotky a vydělíme(už normálně) deseti : (64-4)/10 = 10.

Pokud budeme mít 3-ciferné číslo, tak prostě vydělíme 100 místo 10, provedeme stejný postup, vytáhneme z toho desítky, na těch provedeme to co je popsané výše atd. Teď to jen pošleme funkci k zobrazení. Ale tak snadné to nebude. Musíme nejdříve rozlišit, zda je číslo jednociferné, to ho prostě pošleme funkci a nic s ním neděláme. Pokud je dvojciferné, tak ho proženeme přepočtem, pokud trojciferné, tak ho také přepočítáme a tak dále. Abychom si nedělali nepořádek v loopu, tak si na to vytvoříme další funkci, do které to všechno zabalíme, která obstará vše a nám jen bude stačit ji zavolat po každém řádku, kde by mohlo dojít ke změně zobrazované 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
     }
}

Velkou výhodou je, že nemusíme funkci předávat parametr cislo, protože jsme ho deklarovali na začátku kódu mimo nějakou funkci, takže je přístupný všude. Pokud budete chtít stovky, tak to bude vypadat nějak 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 snad již zvládnete udělat sami :) To by bylo vše. Teď už to jen nějak poskládat dohromady. Pokud jste nepřišli jak na to, tak celý kód je buď ke stažení nebo na konci článku.

Původně jsem k demonstraci zamýšlel použít velký 2x2 místný display, který momentálně stavím, ale zasáhly zákony schválnosti a ještě není hotový. Povedlo se mi najít display z obrázku na začátku, jen mu nefungují některé segmenty (bohužel opravdu nic jiného nebylo). Takže jsem zavrhl náhodná čísla a tu část zaměnil za:

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

což zobrazuje postupně čísla od 0 do 99. Bude potřeba trochu fantazie, aby jste viděli, že to funguje. Pro pomoc s fantazií, tohle obracené F 6 je 88 :D

Rozbitý sedmisegmentový displej

Na videu níže je vidět, jak na display "naskakují" "čísla" od 0 do 99 (pak jsem to někde utnul). Slibuji, že až budu mít hotový ten funkční, tak ten program spustím na něm a dám ho sem :)

Jak jsem slíbil, zde je k vidění program pro náhodná čísla na nějakém funkčním displayi(omlouvám se za kvalitu, nic jiného nebylo po ruce).

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 bylo pro dnešek vše. Příště se nejspíše (plány nejsou jisté) zaměříme znovu na konstrukci jazyka, kterou se mi minule povedlo odbýt a pokud na to zbude prostor, tak si vytvoříme knihovnu pro tento případ, takže by se nám kód omezil na něco ve stylu:

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

a nebylo by v něm tolik deklarací funkcí, ty by byly hezky schované někde jinde.


 

Stáhnout

Staženo 151x (2.59 kB)
Aplikace je včetně zdrojových kódů

 

  Aktivity (1)

Článek pro vás napsal Adam Ježek
Avatar
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.

Jak se ti líbí článek?
Celkem (8 hlasů) :
4.8754.8754.8754.8754.875


 


Miniatura
Všechny články v sekci
Arduino
Miniatura
Následující článek
Arduino - Jazyk

 

 

Komentáře

Avatar
Zdravim
Redaktor
Avatar
Zdravim:

To vypadá mnohem logičtěji, než když sme to na škole dělali v assembleru :D

 
Odpovědět 22.9.2014 14:57
Avatar
vodacek
Redaktor
Avatar
vodacek:
if(cislo < 10) //menší než deset
{
   ...
}
else if((cislo > 9 )& (cislo <100))

část

(cislo > 9 )&

je zbytečná, čísla menší jak 10 ti vyžere vrchní podmínka, takže na ně nepotřebuješ znovu testovat

 
Odpovědět 22.9.2014 16:19
Avatar
Adam Ježek
Tým ITnetwork
Avatar
Odpovídá na vodacek
Adam Ježek:

Ano, ale jednak projistotu a jednak do tutorialu bych to tam dal vsechno

Odpovědět 22.9.2014 17:36
Programátor dělá co může. Počítač co chce. | Pokud mi dáš mínus, tak prosim, napiš proč!
Avatar
Adam Ježek
Tým ITnetwork
Avatar
Adam Ježek:

Článek byl editován

Odpovědět 27.10.2014 14:59
Programátor dělá co může. Počítač co chce. | Pokud mi dáš mínus, tak prosim, napiš proč!
Avatar
Krtek
Člen
Avatar
Krtek:

Začínal jsem s picaxem, a když jsem chtěl něco zobrazit na 7-segmenťáku, musel jsem to všechno pracně rozepisovat v kódu (nehledě na to, že jsem s tím naprosto dokonale zaplác půl paměti)

 
Odpovědět 6.12.2014 8:57
Avatar
zbynek danek
Člen
Avatar
zbynek danek:

Zdar, našel jsem malou chybu, u sedmičky. máš {1,1,0,0,0,0,1}, ale má být {1,1,0,0,0,1,0}, Moc díky za super návod.

 
Odpovědět 7. května 21:57
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 6 zpráv z 6.