10 minute read
mikroupravljača (22
ELEKTRONIKA
Shield-A, učilo za programiranje mikroupravljača (22)
Advertisement
U ovom ćemo nastavku pokazati kako razvojni sustav Shield-A može prikazati numeričku informaciju na 7-segmentnom displeju. Koristit ćemo konfiguraciju prema Slici 69., u kojoj su na konektor J1 Shield-A spojena dva 7-segmentna displeja, jedan sa šest znamenaka i jedan satni, s četiri znamenke.
Primijetite kako izvodi na displejima nemaju isti raspored, pa čak nisu ni jednako označeni, ali ipak imaju istu namjenu. Iz prošlog nastavka znamo kako mikroupravljač ne može adresirati ovakve displeje, pa će na oba biti ispisana ista informacija. Program će dobro raditi i ako na Shield-A spojite samo jedan od ova dva displeja, ili možda neki treći; bitno je samo da se na njemu nalazi integrirani krug TM1637. 19. programski zadatak: Za sklop prema Slici 69. napisati program koji će na 7-segmentnim displejima ispisivati vrijeme u desetinkama sekunde. Mjerenje počinje i zaustavlja se pritiskom na tipkalo SW1. Pritiskom na tipkalo SW2 mjerenje vremena počinje od nule. Na šestoznamenkastom displeju koriste se samo prve četiri znamenke, a poslije treće znamenke ispisuje se decimalna točka. Rješenje Bascom-AVR-a (program ShieldA_19a.bas) Kako Bascom-AVR ne poznaje komunikacijski protokol koji podržava čip TM1637, pokušat ćemo iskoristiti njegovu sličnost s I2C-protokolom. Iz prošlog nastavka znamo kako su signali START i STOP jednaki kod oba protokola, a slanje naredbi i podataka razlikuje se samo u redoslijedu kojim se šalju bitovi. Stoga ćemo naredbama i podacima koje želimo poslati u čip TM1637 prije slanja zamijeniti redoslijed bitova, nakon čega ih možemo poslati običnom naredbom I2cwrite. Ovaj posao obavlja potprogram Send_tm_byte. Naredbu ili podatak koji želimo poslati upisujemo u varijablu Tm_byte i zatim pozivamo navedeni potprogram. U njemu ćemo bit po bit prenositi sadržaj varijable Tm_byte u varijablu Etyb_mt, mijenjajući im pritom redoslijed, a zatim tako “izokrenuti” sadržaj poslati čipu TM1637: Send_tm_byte: Etyb_mt.0 = Tm_byte.7 Etyb_mt.1 = Tm_byte.6
Slika 69. Konfiguracija od dva 7-segmentna displeja koju ćemo koristiti u 19. programskom zadatku Etyb_mt.7 = Tm_byte.0 I2cwbyte Etyb_mt Return Još će nam trebati potprogrami za gašenje prikaza na displeju Disp_off: I2cstart Tm_byte = &B10000000 Gosub Send_tm_byte I2cstop Return kao i za ponovno uključenje prikaza: Disp_on: Set_int: I2cstart Tm_byte = Disp_int Or &B10001000
Gosub Send_tm_byte I2cstop Return Ovaj drugi potprogram ujedno služi i za postavljanje željenog intenziteta pa zato ima dva naziva. Krenimo sada od početka programa! Najprije moramo konfigurirati pinove mikroupravljača na koje su spojena tipkala SW1 i SW2 Config Portc.1 = Input Portc.1 = 1 Config Portc.2 = Input Portc.2 = 1 a zatim i komunikacijske pinove SDA i SCL: Config I2cdelay = 100 Config Sda = Portc.4 Config Scl = Portc.5 I2cinit Brzinu komunikacije morali smo usporiti naredbom Config I2cdelay; u praksi se pokazalo da neki moduli pri većim brzinama ne znaju prepoznati naredbe koje im mikroupravljač šalje. Kako bismo provjerili naredbe za postavljanje intenziteta te paljenje i gašenje prikaza na displeju, ispisat ćemo na njemu broj 8888. Kada bismo imali alfanumerički displej, učinili bismo to jednostavnom naredbom Lcd 8888. Ovdje ćemo se morati malo više pomučiti! Princip ispisa je sljedeći: u varijablu Broj upisujemo željenu vrijednost 8888 i zatim pozivamo potprogram Disp_broj. U njemu najprije raščlanjujemo zadani broj na četiri znamenke “8” i upisujemo ih u varijable Disp_digit(1) do Disp_digit(4): Disp_broj: Dim Broj_pom As Word , Mod_pom As Word Broj_pom = Broj For I = 4 To 1 Step -1 Mod_pom = Broj_pom Mod 10 Broj_pom = Broj_pom / 10 Varijabla Mod_pom sadrži posljednju osmicu u binarnom zapisu; kako bismo je mogli prikazati na 7-segmentnom displeju, moramo je pretvoriti u binarne kombinacije prikazane na Slici 67. ispod znamenke 8. To ćemo najlakše učiniti ako binarne kombinacije svih dekadskih znamenaka upišemo u tablicu 7segm_digits: Data &B00111111 ‚0 Data &B00000110 ‚1 Data &B01011011 ‚2 Data &B01001111 ‚3 Data &B01100110 ‚4 Data &B01101101 ‚5 Data &B01111101 ‚6 Data &B00000111 ‚7 Data &B01111111 ‚8 Data &B01101111 ‚9 i zatim svaki binarni broj dobiven postupkom raščlanjivanja iskoristimo kao indeks u naredbi Lookup: Disp_digit(i) = Lookup(mod_pom , 7segm_ digits) Next Lookup će iz tablice “izvući” indeksirani podatak i smjestiti ga u jednu od varijabli iz niza Disp_ digit. Postupak ćemo ponoviti kako bismo izdvojili sve četiri znamenke, i tek tada smo spremni za slanje podataka čipu TM1637. Najprije ćemo poslati naredbu “piši u displej registre” I2cstart Tm_byte = &B01000000 Gosub Send_tm_byte I2cstop a onda odrediti da upis počinje od prvog (nultog) registra: I2cstart Tm_byte = &B11000000 Gosub Send_tm_byte Naredbe “10000000”, “10001000”, “01000000” i “11000000” objašnjene su u prošlom nastavku. Sada možemo poslati sve četiri znamenke, završiti komunikaciju i vratiti se iz potprograma: For I = 1 To 4 Tm_byte = Disp_digit(i) Gosub Send_tm_byte Next I2cstop Return Vrlo sličan postupak odrađuje i prije spomenuta naredba Lcd, samo je to za nas nevidljivo. Sada nam je na displeju ispisana poruka “8888”. Promijenit ćemo intenzitet prikaza od najmanjeg do najvećeg For Disp_int = 0 To 6 Gosub Set_int Waitms 200 Next a zatim nekoliko puta naizmjenično ugasiti i upaliti prikaz na displeju: For I = 1 To 3 Gosub Disp_off Waitms 200 Gosub Disp_on
Waitms 200 Next Uvodni dio programa je završen i sada ulazimo u glavnu petlju. Ovdje ćemo provjeravati stanja tipkala SW1 i SW2 i, ako je neko od njih upravo pritisnuto, izvršiti pridružene potprograme: Do Debounce Pinc.1 , 0 , Sw1_sub , Sub Debounce Pinc.2 , 0 , Sw2_sub , Sub U potprogramu pridruženom tipkalu Sw1 mijenjamo sadržaj varijable Start_flag iz 0 u 1 ili obratno: Sw1_sub: If Start_flag = 0 Then Start_flag = 1 Else Start_flag = 0 End If Return Vrijednost 1 znači da štopericu treba pokrenuti, a 0 da štopericu treba zaustaviti. U potprogramu pridruženom tipkalu Sw2 samo brišemo vrijednost brojača desetinki sekunde, kako bi sljedeće mjerenje vremena krenulo od nule: Sw2_sub: Broj = 0 Gosub Disp_broj Return U nastavku glavne petlje moramo provjeriti je li štoperica pokrenuta i ako je, svakih 100 ms povećati vrijednost brojača te ga ispisati na displeju: If Start_flag = 1 Then Waitms 100 Incr Broj Gosub Disp_broj End If Loop Ako štoperica nije pokrenuta, zadržat će se trenutni prikaz na displeju dokle god ne pritisnemo neko od tipkala. Pogledajmo sada kako je isti programski zadatak riješen u Arduinu. Arduino ima biblioteku za module s integriranim krugom TM1637, pa bi sam program trebao biti puno jednostavniji. Provjerimo, je li zaista tako! Rješenje Arduina (program Shield-A_19a.ino) U svijetu Arduina postoji više biblioteka funkcija koje su pisane za TM1637, no do sada niti jedna nije pisana baš za naš zadatak. Iskoristit ćemo najjednostavniju, “Grove 4-Digit Display”. Proces instalacije pomoću upravitelja biblioteka (Library Manager) opisali smo u 20. nastavku. Na Slici 70. vidimo biblioteku nakon što smo filtrirali popis s “TM1637”. Biblioteka stvara novi objekt kojemu su pridružene sljedeće funkcije: • TM1637(uint8_t clkPin, uint8_t dioPin) – stvara novi objekt i pridružuje mu pinove za komunikaciju • init() – inicijalizira objekt • start() – pokreće komunikaciju s TM6137 • writeByte(int8_t wr_data) – zapisuje 8-bitni podatak u TM1637 • stop() – zaustavlja komunikaciju s TM1637 • clearDisplay() – briše sadržaj displeja • set(uint8_t svjetlina) – definira intenzitet svijetljenja displeja (0 je najslabije, 7 najjače) i nova vrijednost je aktivna tek kod sljedećeg ispisivanja na displeju te vrijedi za sve znamenke • point(boolean PointFlag) – definira hoće li biti prikazana decimalna točka kod ispisa sljedećih znamenki; vrijednost može biti 0 ili 1 • display(uint8_t BitAddr,int8_t DispData) – prikazuje znamenku na displeju. BitAddr je redni broj znamenke (prva znamenka se označava brojem 0), a DispData je znak koji želimo prikazati (znakovi koje možemo prikazati navedeni su u Tablici 1.). Primijetite da ne postoje funkcije za isključivanje i uključivanje displeja, stoga ćemo ih morati sami napisati. Također ne postoji funkcija za dohvaćanje trenutne razine intenziteta svjetla, pa ćemo morati koristiti globalnu varijablu kao u primjeru Bascoma. Program ćemo započeti navođenjem biblioteke koju ćemo koristiti: #include <TM1637.h>
Slika 70. Prozor za upravljanje bibliotekama
Ulazna vrijednost
Stvaramo novi objekt TM1637 s imenom disp, TM1637 disp(SCL,SDA); Arduino IDE ima definirane konstante SCL i SDA, koje u sebi sadrže nazive komunikacijskih pinova: SCL je A5 (PC5), a SDA je A4 (PC4). Definiramo globalne varijable za spremanje informacija o intenzitetu svjetla, je li štoperica pokrenuta, broj koji treba prikazati i iza koje znamenke se nalazi decimalna točka (podsjetimo se: prva znamenka se označava brojkom 0): byte disp_int = 2; byte start_flag = 0; int broj = 0; byte disp_dp = 2; Zatim ćemo u funkciji setup() definirati da pinove A1 (PC1) i A2 (PC2) želimo koristiti kao ulaze, te istovremeno uključiti njihov pull-up otpornik. Njima ćemo očitavati stanja tipkala SW1 i SW2: void setup() { pinMode(A1, INPUT_PULLUP); pinMode(A2, INPUT_PULLUP); Inicijaliziramo objekt za prikaz na displeju i definiramo intenzitet svjetla: disp.init(); disp.set(disp_int); Kod uključivanja šesteroznamenkastog displeja s TM1637, peta i šesta znamenka imaju uključene neke segmente pa isključujemo sve segmente tih znamenki: disp.display(4,0x7f); disp.display(5,0x7f); definiramo broj za prikaz, ispišemo ga našom funkcijom disp_broj(), koju ćemo poslije objasniti, i čekamo 200 milisekundi: broj = 8888; disp_broj(disp, broj, disp_dp); delay(200); Promijenit ćemo intenzitet prikaza od najmanjeg do najvećeg. Primijetite da nakon funkcije set() obavezno pozivamo funkciju display(), to je zato što se intenzitet prikaza mijenja kod izvršavanja funkcije display(): for (int i = 0; i <= 7; i++){ disp_int = i;
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 disp.set(disp_int); disp_broj(disp, broj, disp_dp);
Prikaz 0 1 2 3 4 5 6 7 8 9 A b C d E F delay(200); Tablica 1.: Znakovi koje možemo prikazati na 7-segmentnim displejima pomoću } naredbe display iz biblioteke Grove 4-Digit Display a zatim nekoliko puta naizmjenično isključiti i uključiti prikaz na displeju pomoću naših funkcija disp_off() i disp_on() : for (int i = 0; i <= 3; i++){ disp_off(disp); delay(200); disp_on(disp); delay(200); } Na kraju postavljamo brojač na nulu pomoću funkcije sw2_function(): sw2_function(); } // kraj setup() U funkciji loop() očitavamo stanje tipkala SW1 i, ako je pritisnuto, izvršavamo funkciju sw1_function() (ovaj algoritam opisan je u 6. nastavku): void loop() { if (digitalRead(A1) == 0) { delay(25); if (digitalRead(A1) == 0) { sw1_function(); while (digitalRead(A1) == 0){ } } } Isto tako, očitavamo stanje tipkala SW2 i, ako je pritisnuto, izvršavamo funkciju sw2_function(): if (digitalRead(A2) == 0) { delay(25); if (digitalRead(A2) == 0) { sw2_function(); while (digitalRead(A2) == 0){ } } } Ukoliko je štoperica uključena (vrijednost varijable start_flag je 1), čekamo 100 milisekundi i povećavamo brojač za 1. Ako broj postane veći od 9999, vraćamo ga na nulu. Na kraju ispisujemo broj na displeju: if (start_flag == 1) { delay(100); broj++; if (broj > 9999) { broj = 0; }
disp_broj(disp, broj, disp_dp); } } // kraj loop() U funkciji sw1_function() mijenjamo sadržaj varijable start_flag iz 0 u 1 ili obratno: void sw1_function(){ if (start_flag == 0){ start_flag = 1; } else start_flag = 0; } // kraj sw1_function() U funkciji sw2_function() brišemo vrijednost brojača desetinki sekunde i ispisujemo ga na displeju: void sw2_function(){ broj = 0; disp_broj(disp, broj, disp_dp); } // kraj sw2_function() Funkciji disp_broj() prosljeđujemo pokazivač na objekt TM1637 na kojem želimo prikazati broj (ovo je praktično kada u programu koristimo više displeja TM1637 spojenih na različite pinove), broj koji želimo prikazati i nakon koje znamenke želimo ispisati decimalnu točku. Funkcija ispisuje prve četiri znamenke, počevši od mjesta jedinica do mjesta tisućica. Za svaku znamenku prvo provjeri je li uz nju potrebno ispisati i decimalnu točku, a pojedinu znamenku iz broja izdvaja tako da izračuna ostatak nakon dijeljenja faktorom 10: void disp_broj(TM1637 &disp, int broj, byte disp_dp){ for (int i = 3; i >= 0; i--) { if (i == disp_dp) { disp.point(1); } else { disp.point(0); } disp.display(i, broj % 10); broj = broj / 10; } } // kraj disp_broj Funkciji disp_off() prosljeđujemo pokazivač na objekt displeja kojemu želimo isključiti prikaz. U njoj započinjemo komunikaciju s displejom, šaljemo naredbu za isključivanje displeja te zaustavljamo komunikaciju: void disp_off(TM1637 &disp){ disp.start(); disp.writeByte(0b10000000); disp.stop(); }// kraj disp_off Funkciji disp_on() prosljeđujemo pokazivač na objekt displeja kojemu želimo uključiti prikaz. U njoj započinjemo komunikaciju s displejom, šaljemo naredbu za uključivanje displeja zajedno sa željenim intenzitetom prikaza (zapisan je u globalnoj varijabli disp_int) i zaustavljamo komunikaciju: void disp_on(TM1637 &disp){ disp.start(); disp.writeByte(0b10001000 | disp_int); disp.stop(); }// kraj disp_on
Za one koji žele znati više
Slika 71. Konfiguracija Timera1 pomoću koje možemo ostvariti točno mjerenje