Arduino: LED segmenti i matrice
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. 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); } //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. { 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. 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 } 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 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); } } #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. Igor S. RUŽIĆ |
| ||||||||||||||||||||||||||||||||||||
Home / Novi broj | Arhiva • Opšte teme | Internet | Test drive | Test run | PD kutak | CeDeteka | WWW vodič • Svet igara Svet kompjutera Copyright © 1984-2018. Politika a.d. • Redakcija | Kontakt | Saradnja | Oglasi | Pretplata • Help • English | |
SKWeb 3.22 |