LAKI PINGVINI<>
072017<><>

Arduino: LED segmenti i matrice

Brojke i slova

Ispisivanje teksta i cifara na jednostavan način

Sedmosegmentni displeji se vrlo često koriste u situacijama kada nam je potreban prikaz isključivo numeričkih informacija. Nisu skupi, malo troše i pružaju informaciju koja je jasno čitljiva i sa veće udaljenosti. Naziv duguju svom dizajnu u obliku broja osam, sastavljenom od sedam segmenata LE dioda. Tačnije, u pitanju je osam dioda, pošto se još jedna koristi za prikazivanje decimalne tačke, ukoliko je to potrebno.

Slično kolor LE diodama iz prošlog broja, sedmosegmentni displeji mogu biti građeni na osnovu zajedničke katode i zajedničke anode. Kod displeja sa zajedničkom anodom, sve anode pojedinačnih LED se ujedinjuju u jednu jedinstvenu anodu, to jest katodu, kod modela sa zajedničkom katodom. U pitanju je čisto stvar polariteta, a za korisnika je najvažnije da zna da se indikatori sa zajedničkom anodom priključuju na pozitivni napon, dok oni sa zajedničkom katodom idu na uzemljenje.

Na tržištu je moguće pronaći displeje koji prikazuju jedan, dva, tri ili četiri broja istovremeno. Mi ćemo za demonstraciju rada koristiti najprostije jednocifrene LED displeje sa zajedničkom anodom. Filozofija rada sa njima je veoma slična radu sa LE diodama. Pošto je u pitanju osam LED elemenata, potrebno je da ih sa džamper žicama priključimo na Arduino preko nezaobilaznih otpornika. Od prodavca smo dobili informaciju da radni napon segmenata iznosi 1,8 volti, dok je potrebna jačina struje u rasponu 15-20 miliampera. Formula koju smo koristi u prethodnom nastavku nam daje sledeći rezultat: R = (5 V – 1,8 V) / 0,015 A = 213 oma, pa će otpornici od 220 oma biti dobar izbor za dati primer.

 
Naveli smo da je naš displej sa zajedničkom anodom. Da bismo na njemu prikazali neki broj, potrebno je da uključimo segmente koji reprezentuju taj broj, a u našem slučaju to znači da te pinove treba da povežemo sa uzemljenjem, a pinove 3 i 8 sa naponom od pet volti.

Hajde da to objasnimo na primeru broja 7, i to bez upotrebe mikrokontrolera (Arduino na slici služi jedino za napajanje displeja). Shema nam govori da je potrebno dovesti uzemljenje do pinova 4, 6 i 7, što će izazvati pojavljivanje svetlosti u segmentima C, B i A.

 
Nije teško pogoditi da je za dinamičko prikazivanje brojeva potrebno povezati ove pinove sa izlazima Arduina.

Pre nego što pređemo na pisanje skeča, da vidimo kako bismo na najjednostavniji način mogli da prikažemo broj 7 iz gornjeg primera, pod uslovom da smo displej povezali onako kako je to prikazano u tabeli.

void setup() {

pinMode(1, OUTPUT);

pinMode(2, OUTPUT);

pinMode(3, OUTPUT);

digitalWrite(1, 0);

digitalWrite(2, 0);

digitalWrite(3, 0);

}

 
Pošto su nam potrebna samo tri segmenta (a, b, c) koji su povezani sa pinovima 1, 2 i 3 respektivno, njih prvo postavljamo u stanje izlaza, a zatim ta tri pina putem komande digitalwrite postavljamo u stanje logičke nule, što za uređaje sa zajedničkom anodom znači da ih uključujemo. Sledi primer prikazivanja brojeva u automatskom režimu:

//zajednicka anoda

byte brojevi[10][7] = {

{ 0, 0, 0, 0, 0, 0, 1 }, // = 0

{ 1, 0, 0, 1, 1, 1, 1 }, // = 1

{ 0, 0, 1, 0, 0, 1, 0 }, // = 2

{ 0, 0, 0, 0, 1, 1, 0 }, // = 3

{ 1, 0, 0, 1, 1, 0, 0 }, // = 4

{ 0, 1, 0, 0, 1, 0, 0 }, // = 5

{ 0, 1, 0, 0, 0, 0, 0 }, // = 6

{ 0, 0, 0, 1, 1, 1, 1 }, // = 7

{ 0, 0, 0, 0, 0, 0, 0 }, // = 8

{ 0, 0, 0, 0, 1, 0, 0 } // = 9

};

void setup() {

//svi potrebni portovi su...

for (byte x = 1; x < 8; x++) {

pinMode(x, OUTPUT); // ...izlaznog tipa

++x;

}

}

void loop() {

for (byte x = 0; x < 10; x++) {

delay(250);

ispisi_broj(x);

}

delay(2000);

}

void ispisi_broj(byte broj) {

byte pin = 1; //pocetni pin

//petlja za proveru svih segmenata

for (byte segment = 0; segment < 7; segment++) {

digitalWrite(pin, brojevi[broj][segment]);

pin++; //idemo na sledeci pin

}

}

I ovde se radi o dosta jednostavnom kôdu, gde u okviru matrice brojevi definišemo izgled svih deset arapskih brojeva. U slučaju da koristimo displej sa zajedničkom katodom, potrebno je postaviti inverzne vrednosti:

//zajednicka katoda

byte brojevi[10][7] = {

{ 1, 1, 1, 1, 1, 1, 0 }, // = 0

{ 0, 1, 1, 0, 0, 0, 0 }, // = 1

. . .

{ 1, 1, 1, 0, 0, 1, 1 } // = 9

}

U delu setup uključujemo izlaz na svim korišćenim portovima. Petlja loop svakih 250 milisekundi ispisuje po jedan broj od 0 do 9 i zatim pravi pauzu od dve sekunde pre ponavljanja. Najvažniji deo skeča se nalazi u okviru funkcije ispisi_broj, koja kao argument prima broj koji treba ispisati. Vrednost varijable pin definiše početnu vrednost od koje počinjemo brojanje korišćenih pinova. Zatim se, kroz petlju od sedam koraka (varijabla segment) očitava red matrice koji odgovara poziciji našeg broja.

Uvođenje heksadecimalnih brojeva je vrlo jednostavna stvar. U pitanju je brojni sistem sa osnovom 16, koji pored deset cifara koristi i latinična slova od A do F. Da ne bismo zauzimali mesto kôdom koji bi bio vrlo sličan prethodnom, samo ćemo reći da dimenzije višedimenzionalnog niza brojeva treba postaviti na [16][7] i dodati sledeće redove

{ 0, 0, 0, 1, 0, 0, 0 }, // = a

{ 1, 1, 0, 0, 0, 0, 0 }, // = b

{ 1, 1, 1, 0, 0, 0, 0 }, // = c

{ 1, 0, 0, 0, 0, 1, 0 }, // = d

{ 0, 0, 1, 0, 0, 0, 0 }, // = e

{ 0, 1, 1, 1, 0, 0, 0 }, // = f

Naravno, prilikom pozivanja petlje za ispisivanje brojeva, umesto broja 10 koristimo vrednost 16.

Ovo nije jedini način na koji se mogu zapisati oblici brojeva. Umesto dvodimenzionalnog niza sa vrednostima 0 i 1, mogli smo koristiti zapisivanje u obliku sedmobitnog binarnog broja ili još prostije, decimalnim i heksadecimalnim brojem. Tako bi, recimo, broj 1 imao oblik B0110000, što je isto kao i decimalna vrednost 48 ili heksadecimalno 0x30. Vrednosti u takvom obliku možemo smestiti u jednodimenzionalni niz i pozivati ih preko indeksa pozicije. Moguće je, na kraju krajeva, formirati brojeve navođenjem sekvence naredbi digitalWrite za svaki pojedinačni segment, kao što smo mi uradili prilikom ispisivanja broja 7 u prethodnom delu teksta.

 
Videli smo kako funkcioniše povezivanje jednocifrenog displeja, a sada ćemo videti kako treba povezati one sa više cifara. Oni su u stvari kombinovani od više pojedinačnih modula i svaki od njih ima po jednu zajedničku anodu ili katodu, dok su pinovi namenjeni segmentima zajednički za sve cifre zajedno. Možete se zapitati: „Kako onda kontrolišemo kojem od brojeva pripadaju koji segmenti?” Odgovor na to pitanje se krije u prikazu trenutno aktivnih brojeva u nekom kratkom vremenskom intervalu.

const int pinovi_segm[] = { 13,8,7,6,5,4,3,2 };

const int cifara= 4; //broj cifara na displeju

const int pinovi_cifre[cifara] = { 9,10,11,12 }; //pinovi za cifre

const int brojevi[10] = {252, 96, 218, 242, 102, 182, 62, 224, 254, 246}; //definicije brojeva 0-9

void setup(){ //inicijalizuj pinove na izlaz

for (int i=0; i < 8; i++) {

pinMode(pinovi_segm[i], OUTPUT);

}

for (int i=0; i < cifara; i++) {

pinMode(pinovi_cifre[i], OUTPUT);

}

}

void loop(){ //ispisi brojeve 0-1023

for (int x=0; x < 1024; x++) {

prikazi_broj(x);

delay (200);

}

delay (5000);

}

void prikazi_broj( int broj){

if (broj == 0) { //ako je 0, ispisi

ispis_broja( 0, cifara-1);

} else {

//sve cetiri cifre

for ( int cifra = cifara-1; cifra >= 0; cifra--) {

if (broj > 0) {

ispis_broja( broj % 10, cifra); //ostatak deljenja sa 10

broj = broj / 10; //sledeca cifra

}

}

}

}

void ispis_broja( int broj, int cifra){

//ispis broja na odredjenoj poziciji

digitalWrite( pinovi_cifre[cifra], HIGH ); //broj je vidjiv

//petlja za citanje segmenata za prikaz broja

for (int segment = 1; segment < 8; segment++) {

boolean jedinica = bitRead(brojevi[broj], segment);

//jedinica = ! jedinica; //ako je zajednicka anoda!

digitalWrite( pinovi_segm[segment], jedinica);

}

delay(5); //drzi upaljeno 5 milisekundi

digitalWrite( pinovi_cifre[cifra], LOW ); //iskljuci

}

Koncentrišimo se samo na najvažnije delove kôda. Funkcija prikazi_broj kao argument prima broj koji treba prikazati. Ako broj nije nula, petlja For će da obradi sve postojeće cifre. Svaki pojedinačni broj koji treba prikazati dobijamo preko ostatka deljenja sa 10. Na primer, ukoliko 251 podelimo sa 10, dobijamo 25 i ostatak 1. Jedinicu zapisujemo na displej i prelazimo na deljenje 25 sa 10, iz čega dobijamo ostatak 5, da bi nam na kraju ostao sam broj 2. Dobijeni brojevi se šalju funkciji ispis_broja, koja uključuje vidljivost pina i zatim kroz petlju For..Next čita bitove kojima popunjava segmente na displeju. Ukoliko koristimo module sa zajedničkom anodom, tada ćemo skinuti komentar sa linije koja invertuje bitove u petlji. Posle toga, pravimo pauzu od pet milisekundi i zatim isključujemo prikaz cifre. Pošto se sve to odvija brzo, ljudsko oko ne može primetiti razliku i čini nam se da su sve cifre prikazane konstantno.

Postoji i dosta jednostavniji metod koji koristi biblioteku pod nazivom SevSeg, i u tom slučaju kôd izgleda ovako:

#include <SevSeg.h>

SevSeg segm_disp; //inicijalizujemo biblioteku

void setup() {

byte cifara = 4;

byte pinovi_segm[] = {2, 3, 4, 5, 6, 7, 8, 13};

byte pinovi_cifre[] = {9, 10, 11, 12};

segm_disp.begin(COMMON_CATHODE, cifara, pinovi_cifre, pinovi_segm);

segm_disp.setBrightness(50);

}

void loop() {

for (int x = 0; x < 1024; x++) {

segm_disp.setNumber(x, 3);

segm_disp.refreshDisplay();

delay (200);

}

delay (5000);

}

Skrećemo pažnju na to da na pratećem crtežu većina otpornika ima vizuelni produžetak koji omogućuje njihovo pravilno razmeštanje na prototipskoj pločici.

Integralno kolo MAX7219

 
U prošlom broju smo se upoznali sa pomeračkim integralnim kolom 74HC595, koje nam je omogućilo da znatno povećamo broj svetlećih dioda priključenih na naš Arduino. Sada ćemo se upoznati sa još jednim zanimljivim čipom koji se veoma često koristi u radu sa segmentima i matricama svetlećih dioda.

Reč je o posebnom integrisanom kolu koje poseduje izlaze za rad sa 8 cifara na displeju, od kojih svaka ima osam segmenata (sedam plus decimalna tačka).

Na šematskom prikazu čipa ljubičastom bojom su označeni pinovi (DIN, LOAD i CLK) koji se priključuju na Arduino putem SPI interfejsa, dok je plavom bojom označen pin DOUT, koji služi za slanje podataka prema sledećem čipu. U takvim slučajevima, na svim čipovima se koriste zajednički signali za LOAD i CLK. Umesto da za svaki pin koristimo otpornik, ovde to činimo preko samo jednog otpornika koji se sa jedne strane povezuje sa nožicom 18 (Iset), a sa druge na liniju napajanja. Optimalna vrednost otpornika varira u zavisnosti od radnih karakteristika displeja. Sledeća tabela daje pregled nekih standardnih vrednosti (u kiloomima):

U projektima se uz MAX7219 najčešće koriste i dva prateća kondenzatora, jedan od sto nanofarada i drugi od deset mikrofarada, koji povezuju napon od pet volti sa uzemljenjem. Čip podržava samo displeje sa zajedničkom katodom. Na linije zajedničkih katoda povezujemo linije DIG n, dok se segmenti svih brojeva vežu na magistralu koja vodi od pinova SEG x. Navodimo programski primer časovnika na osnovu gotovog modula sa osam cifara zasnovanih na čipu MAX7219, uz korišćenje biblioteke LedControl. Takvi moduli obično koštaju nešto malo više od jednog evra i nude mogućnost povezivanja više displeja u jedan niz.

#include <LedControl.h>

//DIN, CLK, Load/CS, broj cifara

LedControl s7 = LedControl(10, 13, 11, 8);

int stoA, stoB, sekA, sekB, minA, minB, casA, casB;

void setup()

{

pinMode(10, OUTPUT); //DIN

pinMode(11, OUTPUT); //LOAD

pinMode(13, OUTPUT); //CLK

s7.shutdown(0, false); //aktiviramo displej

Anuliranje(); //pocetne vrednosti na nulu

s7.setIntensity(0,5); //intenzitet svetla

}

void loop()

{

Casovnik(); //pocinjemo sa merenjem

}

void Casovnik()

{

delay(10); //interval 1/100 sekunda

s7.setDigit(0, 0, stoA++, false); //povecaj za 1

if (stoA == 10) { //ako dodje do 10

stoB++; //povecavamo drugu decimalu

stoA = 0; //a prvu vracamo na 0

}

if (stoB < 9) { //druga decimala <9?

s7.setDigit(0, 1, stoB, false); //ne, ispisi

} else {

stoB = 0; //da, ispisi 0

s7.setDigit(0, 1, stoB, false);

Sekunde(); //i prelazi na sekunde

}

}

void Sekunde(){

sekA++; //dodaj sekundu

if (sekA == 10) { sekB++; sekA = 0;} //doslo do 10?

s7.setDigit(0, 2, sekA, true); //ispisi prvu decimalu

if (sekB < 6) { //druga decimala dosla do 60?

s7.setDigit(0, 3, sekB, false); //ne, ispisi

} else { sekB = 0; sekA = 0; //da, vrati na 0

s7.setDigit(0, 3, sekB, false); //ispisi...

Minute(); //prelazi na minute

}

}

void Minute(){

minA++; //sve isto kao gore

if (minA == 10) {minB++; minA = 0;}

s7.setDigit(0, 4, minA, true);

if (minB < 6) {

s7.setDigit(0, 5, minB, false);

} else { minB = 0; minA = 0;

s7.setDigit(0, 5, minB, false);

Casovi(); //prelazimo na casove

}

}

void Casovi(){

casA++;

if (casA == 10) {

casB++; casA = 0;}

s7.setDigit(0, 6, casA, true);

if (casB < 6) {

s7.setDigit(0, 7, casB, false);

} else { //doslo do kraja, sve na 0

casB = 0; casA = 0; Anuliranje();}

}

void Anuliranje(){ //na pocetne vrednosti

s7.setDigit(0, 0, 0, false);

for (int dig = 1; dig < s7.getDeviceCount(); dig++) {

s7.setDigit(0, dig, 0, (dig % 2 == 0) ? true : false);

}

}

 
Naredbom LedControl(10, 13, 11, 8) govorimo Arduinu da se radi o prikazu osam cifara, a da će biti korišćeni data pinovi 13(CLK), 11(LOAD) i 10(Din). Zatim, definišemo po dve varijable za praćenje vremenskih intervala na dve decimale, i to tako što je niža decimala označena sa A, dok je viša označena slovom B.

U Setup() bloku naredbom shutdown uključujemo displej, izvodeći ga iz režima uštede energije zadavanjem parametra false. Zatim vršimo inicijalizaciju ekrana i na kraju naredbom setIntensity postavljamo osvetljenost displeja (moguće vrednosti 0-15). Imajte u vidu da svetliji ekran troši više struje. Blok loop() poziva našu funkciju Casovnik, koja počinje sa brojanjem stotinki i kada dođe do 99, prelazi na sekunde, pa kada izbroji 60 sekundi na minute, zatim i na časove.

 
Na isti način se ovaj čip može koristiti i za prikazivanje informacija na matricama do 8x8 tačaka, a takve matrice se, opet, mogu povezati u kompleksnije blokove koji omogućavaju formiranje prezentacionih panela. Korišćena je biblioteka MaxMatrix, koju je moguće preuzeti sa linka goo.gl/a1N4t4. Nju treba da postavimo unutar foldera C:\Users\ime_korisnika\Documents\Arduino\libraries.

#include <MaxMatrix.h>

#include <avr/pgmspace.h>

const unsigned char PROGMEM karakter[] = {

3, 8, 0, 0, 0, 0, 0, //space

1, 8, 95, 0, 0, 0, 0, //!

...

4, 8, 62, 65, 65, 62, 0, //0

3, 8, 66, 127, 64, 0, 0, //1

4, 8, 98, 81, 73, 70, 0, //2

...

4, 8, 126, 17, 17, 126, 0, //A

4, 8, 127, 73, 73, 54, 0, //B

4, 8, 62, 65, 65, 34, 0, //C

...

5, 8, 99, 20, 8, 20, 99, //X

5, 8, 7, 8, 112, 8, 7, //Y

4, 8, 97, 81, 73, 71, 0, //Z

...

...

5, 8, 68, 40, 16, 40, 68, //x

4, 8, 156, 160, 160, 124, 0, //y

3, 8, 100, 84, 76, 0, 0, //z

...

};

char tekst[]="Veliki pozdrav za sve citaoce casopisa Svet kompjutera!";

byte bafer[10]; //radni bafer

MaxMatrix m8x8(10, 11, 13, 1); //DIN, CS, CLK, broj modula

void setup(){

m8x8.init(); //inicijalizacija modula

m8x8.setIntensity(0); //jacina svetlosti 0-15

}

void loop(){

ispisi_tekst(tekst, 50);

}

void skrolovanje_levo(char znak, int brzina){

if (znak < 32) return; //ako nije vidljiv, izlazak

memcpy_P(bafer, karakter + 7*(znak-32), 7); //kopiraj definiciju

m8x8.writeSprite(32, 0, bafer);

m8x8.setColumn(32 + bafer[0], 0);

for (int x=0; x<bafer[0]+1; x++)

{ //pomeramo bafer ulevo

delay(brzina); //pauza

m8x8.shiftLeft(false, false); //pomeri levo

}

}

void ispisi_tekst(char* znak, int brzina){

while (*znak != 0){ //dok ne dodjemo do kraja niza

skrolovanje_levo(*znak, brzina);

znak++; //sledeci znak

}

}

Varijabla karakter sadrži definiciju prvih 127 znakova skupa ASCII, i to tako što se za zapis definicije znaka koristi pet bajtova. Prva dva bajta svakog reda govore biblioteci stvarne dimenzije znaka. Recimo, broj 1 ima širinu od tri i visinu od osam piksela (dve nule s desna se ne računaju). Gornji levi ugao displeja predstavlja najniži bit prve kolone piksela, što važi i za svaku preostalu kolonu. Da bismo uštedeli na prostoru izbacili smo veći deo definicije karaktera, a kompletan skeč je moguće preuzeti sa adrese pastebin.com/u3Fse7iB. Reč PROGMEM govori kompajleru da sadržaj varijable smesti u fleš-memoriju, pošto je fiksnog karaktera, a da bismo je koristili, potrebno je da u program uvrstimo fajl <avr/pgmspace.h>. Varijabla tekst sadrži niz znakova koji nameravamo da ispišemo na displeju, dok bafer ima funkciju pomoćne varijable.

 
U narednoj liniji kreiramo instancu objekta sa parametrima pinova korišćenih za DIN, CS i CLK signale i sa ukazivanjem broja korišćenih panela. Zatim, objekat m8x8 u bloku Setup inicijalizujemo i postavljamo jačinu svetlosti na najmanju moguću. Petlja loop() sve vreme poziva funkciju ispisi_tekst, prosleđujući joj kao argumente zadani tekst i brzinu skrolovanja (pomeranja). Najvažniji deo skeča predstavlja funkcija skrolovanje_levo, koja preuzima pojedinačne znakove i vrši njihovo pomeranje. Ako je ASCII vrednost poslanog znaka manja od 32, sledi izlazak iz funkcije zato što se ti karakteri ne mogu prikazati na ekranu. Zatim, od ASCII vrednosti oduzimamo 32 kako bismo lakše pronašli definiciju karaktera i to kopiramo u bafer. Slede funkcije writeSprite i setColumn, koje ispisuju znak iz bafera.

Postoje i matrice sačinjene od LE dioda u boji, za čije korišćenje je potrebno konstruisati sklop sa upotrebom tri integrisana kola MAX7219 (po jedno za svaku od tri boje), ali zbog relativne kompleksnosti i nedostatka prostora, nismo ih obradili u okviru ovog teksta.

Igor S. RUŽIĆ

 
WebTorrent 0.18 (BETA)
Zint Barcode Studio 2.5.1
Filesystem Hierarchy Standard
Arduino: LED segmenti i matrice
Šta mislite o ovom tekstu?
Home / Novi brojArhiva • Opšte temeInternetTest driveTest runPD kutakCeDetekaWWW vodič • Svet igara
Svet kompjutera Copyright © 1984-2018. Politika a.d. • RedakcijaKontaktSaradnjaOglasiPretplata • Help • English
SKWeb 3.22
Opšte teme
Internet
Test Drive
Test Run
PD kutak
CeDeteka
WWW vodič
Svet igara



Naslovna stranaPrethodni brojeviOpšte informacijeKontaktOglašavanjePomoćInfo in English

Svet kompjutera