11 minute read

programiranje mikroupravljača (26

Next Article
BBC micro:bit [30

BBC micro:bit [30

Shield-A, učilo za programiranje mikroupravljača (26)

ELEKTRONIKA

Advertisement

Nakon što smo predstavili I2C slave modul s mikroupravljačem ATtiny85 i RGB-diodom i za njega napisali odgovarajući program Bascom-AVR-a, u ovom ćemo nastavku opisati kako za isti modul napisati program Arduino IDE-a, a zatim ćemo predstaviti i odgovarajuće master programe za razvojni sustav Shield-A na obje platforme.

Arduino I2C slave

Želimo li koristiti mikroupravljače ATtiny, u Arduino IDE moramo instalirati dodatnu podršku. To može biti, npr., projekt ATTinyCore autora Space Konde, čije su naredbe kompatibilne s originalnim Arduino IDE, a nama interesantna biblioteka Wire radi pouzdano. Upute za instalaciju nalaze se na stranici https://github.com/SpenceKonde/ATTinyCore. Za programiranje mikroupravljača ATtiny upotrijebit ćemo Arduino Uno kao ISP programator.

Arduinovo rješenje programskog zadatka: Program Shield-A_22.ino

Programska logika rješenja Arduino IDE identična je kao u Bascom-AVR-u, a naredbe koje ne postoje u Arduino IDE napisat ćemo kao makronaredbe. Na početku programa definiramo korištenje biblioteke Wire i makronaredbama RED, GREEN i BLUE pridružujemo pinove kojima upravljamo intenzitetom pojedine boje: #include <Wire.h> #define RED PB1 #define GREEN PB3 #define BLUE PB4 Za pregledniji kod programa definiramo i makronaredbe za uključivanje i isključivanje Timera 0: #define STOP_TIMER0 (TCCR0B &= ~((1<<CS00)|(1<<CS01))) #define START_TIMER0 (TCCR0B |= (1<<CS00)|(1<<CS01)) I2C adresa je ‘0b1111000’ a polje u koje spremamo podatke primljene preko komunikacijske linije bit će i2c_rcv_byte[3]: byte I2C_addr = 0b1111000; volatile byte i2c_rcv_byte[3] = {0, 0, 0}; U funkciji setup() konfiguriramo Timer0, brojače povezane s prekidima i uključimo prekide: void setup() { cli(); TCCR0A = 0; TCCR0B = 0; START_TIMER0; TCCR0B |= (1<<CS00)|(1<<CS01); CLKPR |= (1 << CLKPCE); CLKPR = 0; TIFR |= (1 << OCF0A) | (1 << OCF0B); TIMSK |= (1 << OCIE0A) | (1 << OCIE0B); OCR0A = 0; OCR0B = 255; TCNT0 = 0; sei(); Biblioteci Wire moramo proslijediti odabranu I2C adresu te definirati da mora izvršiti funkciju receiveEvent() kada od mastera primi neke podatke: Wire.begin(I2C_addr); // join i2c bus with address Wire.onReceive(receiveEvent); // register event Još ćemo pinove kojima uključujemo pojedine boje RGB-diode konfigurirati kao izlazne i izvršiti početni demoprogram: pinMode(RED, OUTPUT); pinMode(GREEN, OUTPUT); pinMode(BLUE, OUTPUT); rgb_demo(); } // end setup() Demoprogram postupno pali i gasi svaku od boja RGB-diode i ovdje ga nećemo analizirati. U funkciji loop() ne moramo imati niti jednu naredbu, jer će se sve aktivnosti odvijati u prekidnim rutinama: void loop() { } // end loop() U funkciji receiveEvent() preuzimamo podatke sa sabirnice I2C i spremamo ih u polje i2c_rcv_byte[], te pokrećemo funkciju execute_i2c_command(): void receiveEvent(int howMany) { i2c_rcv_byte[0] = Wire.read(); i2c_rcv_byte[1] = Wire.read(); i2c_rcv_byte[2] = Wire.read(); execute_i2c_command(); } // end receiveEvent(int howMany) Ona je pisana za RGB LED sa zajedničkom katodom, kod koje za intenzitete pojedine boje možemo direktno koristiti vrijednosti pohranjene u polju i2c_ rcv_byte[] (kod RGB LED sa zajedničkom anodom te bismo vrijednosti prethodno trebali “invertirati”, tako da zaprimljenu vrijednost oduzmemo od broja 255): void execute_i2c_command(){ analogWrite(RED, i2c_rcv_byte[0]); if (i2c_rcv_byte[1] == 0) { STOP_TIMER0; digitalWrite(GREEN, LOW); } else { OCR0A = i2c_rcv_byte[1]; START_TIMER0; } analogWrite(BLUE, i2c_rcv_byte[2]);

} // end execute_i2c_command() Primijetimo kako crvenom i plavom bojom upravljamo pomoću naredbe analogWrite(), dok ćemo zelenom upravljati prekidima vezanim uz Timer0; prvi prekid isključuje LED-ice kada tajmer dostigne vrijednost upisanu u OCR0A, a drugi je uključuje kada tajmer dostigne u OCR0B upisanu vrijednost 255: ISR (TIMER0_COMPA_vect) { digitalWrite(GREEN, LOW); } // end ISR (TIMER0_COMPA_vect) ISR (TIMER0_COMPB_vect) { digitalWrite(GREEN, HIGH); } // end ISR (TIMER0_COMPB_vect) Arduino I2C master Kao I2C master koristili smo razvojni sustav Shield-A postavljen na Arduino Uno. “Master” programi koje ćemo predstaviti pisani su za takvo okruženje čija je shema prikazana na Slici 79. Oznake pojedinih komponenti potječu sa Shielda-A, a plavo obojane oznake odgovaraju oznakama s pločice Arduino Uno. Slika 80. pokazuje kako povezujemo Shield-A s RGB-modulom. Na slici su simbolično prikazane komponente Shielda-A koje koristimo za interaktivno podešavanje boja: alfanumerički displej, tipkala SW1 i SW2 te potenciometar RV1. Tipkalom SW1 odabiremo boju, potenciometrom ugađamo njen intenzitet, a tipkalom SW2 šaljemo odgovarajuću naredbu RGB-modulu. Slika 81. vizualizira izbornik na alfanumeričkom displeju: oznake u gornjem retku displeja označavaju crvenu, zelenu i plavu boju, a troznamenkasti brojevi ispod tih oznaka trenutni intenzitet pojedine boje. Četvrti broj u donjem retku ovisi o položaju klizača potenciometra RV1; zakretanjem njegove osovine od jednog do drugog krajnjeg položaja prikazana se vrijednost mijenja u rasponu 0-255, upravo kako nam odgovara za intenzitet neke od boja. Boju čiji intenzitet podešavamo biramo pomoću tipkala SW1 sljedećim redoslijedom: niti jedna -> crvena -> zelena -> plava -> sve tri -> niti jedna. To je na Slici 81. prikazano crvenim strelicama, a oznaka trenutno odabrane boje je na LCD-u ispisana velikim slovima. Kada odaberemo neku od boja i potenciometrom ugodimo željeni intenzitet, pritiskom na tipkalo SW2 upisujemo ga ispod oznake boje (to je prikazano plavim strelicama) i preko I2C sabirnice šaljemo odgovarajuću naredbu RGB-modulu. Strukturu ove naredbe opisali smo u prethodnom nastavku. Zakretanjem osovine potenciometra RV1 možemo po volji ugađati intenzitet odabrane boje, ali će se prikaz na LCD-u promijeniti i naredba RGB-modulu poslati tek kada pritisnemo tipkalo SW2. Držimo li tipkalo SW2 pritisnuto duže od jedne sekunde, program prelazi u automatski način rada (donji red na Slici 81.): u gornjem desnom uglu displeja ispisuje se oznaka “auto” a program svakih 50 ms očitava napon na klizaču potenciometra RV1, preračunava ga u intenzitet, ažurira intenzitet odabrane boje (ili svih boja) i šalje odgovarajuću naredbu RGB-modulu. Dok smo u automatskom načinu rada, zakretanje osovine potenciometra RV1 rezultira trenutnom promjenom prikaza na LCD-u i boje RGB-

Slika 79. Shema I2C mastera, koji ćemo koristiti za testiranje RGB-modula

Slika 80. Ovako povezujemo Shield-A s RGB-modulom Slika 81. Pomoću izbornika na alfanumeričkom displeju formiramo naredbu koju će master program poslati RGB-modulu

-diode na RGB-modulu. U “ručni” način rada vraćamo se kratkim pritiskom na tipkalo SW2.

23. programski zadatak

Treba napisati master program za sklop prema shemi sa Slike 79., koji će upravljati radom RGB-modula na prije opisani način! Rješenje Bascom-AVR-a: program Shield-A_23.bas Program koristi sljedeće varijable:

Rd, Gn i Bl sadrže trenutni intenzitet crvene, zelene i plave boje.

Adc_value sadrži trenutno očitanje A/D pretvarača.

Rgb_value služi za oblikovanje prikaza numeričkih vrijednosti na LCD-u.

Rgb_index određuje selektiranu boju i prikaz na LCD-u (0 = nijedna boja, 1 = crvena, 2 = zelena, 3 = plava, 4 = sve boje).

Auto_flag određuje način rada (0-100 = ručni, >100 = automatski). Na početku programa definiramo I2C adresu slave mikroupravljača, kojem će master slati poruke Const I2c_address = &B11110000 a zatim i brzinu I2C komunikacije te SCL i SDA pinove: $lib „i2c_twi.lbx“ Config Twi = 100000 ‚SCL = 100kHz Config Sda = Portc.4 Config Scl = Portc.5 I2cinit Uključivši I2C_TWI.lbx biblioteku omogućili smo Bascom-AVR-u da, umjesto standardne softverske emulacije, koristiti TWI sklop ATmega328P mikroupravljača. Njegovi su SDA i SCL priključci izvedeni na pinove PC4 i PC5, pa smo to morali poštovati u konfiguracijskim naredbama. Odabrali smo standardnu brzinu komunikacije od 100 kHz. A/D pretvarač konfigurirat ćemo tako da kao referentni napon koristi napon napajanja: Config Adc = Single, Prescaler = Auto, Reference = Avcc Zbog toga će se puni raspon A/D pretvorbe (0-1023) poklopiti sa zakretanjem osovine potenciometra RV1 od jedne do druge krajnje pozicije. Još ćemo na uobičajeni način konfigurirati LCD i ulazne pinove PC1 i PC2, nakon čega ulazimo u glavnu petlju Do-Loop. U njoj svakih 50 ms provjeravamo stanja tipkala SW1 i SW2 i izvršavamo potprogram Disp_adc a, ako je odabran automatski način rada, i potprogram Disp_send: Do Debounce Pinc.1 , 0 , Sw1_sub , Sub Debounce Pinc.2 , 0 , Sw2_sub , Sub Gosub Disp_adc If Auto_flag > 100 Then Gosub Disp_send End If Waitms 50 Loop U potprogramu Disp_adc mjerimo napon klizača potenciometra RV1, dijelimo izmjerenu vrijednost s 4 (time smo dobili raspon vrijednosti 0-255), i prikazujemo je na LCD-u: Disp_adc: Adc_value = Getadc(0) Adc_value = Adc_value / 4 Locate 2 , 13 Rgb_value = Str(adc_value) Rgb_value = Format(rgb_value , „000“) Lcd Rgb_value Return Ovisno o sadržaju varijable Rgb_index, u potprogramu Disp_send najprije ažuriramo intenzitet trenutno odabrane boje ili boja, Disp_send: If Rgb_index = 1 Then Rd = Adc_value Elseif Rgb_index = 2 Then Gn = Adc_value Elseif Rgb_index = 3 Then Bl = Adc_value Elseif Rgb_index = 4 Then Rd = Adc_value Gn = Adc_value Bl = Adc_value End If a zatim adresiramo RGB-modul i šaljemo mu trenutno podešene intenzitete svih triju boja: I2cstart I2cwbyte I2c_address ‚ Waitms 1 I2cwbyte Rd I2cwbyte Gn I2cwbyte Bl ‚ Waitms 1 I2cstop Naredbe Waitms nužne su samo ako slave treba više od 2 µs za obradu adrese, odnosno podataka koje mu je master poslao. Program našeg RGB-modula dovoljno je brz pa su naredbe Waitms zakomentirane. Bascom-AVR postavlja sistemski bit Err ako je došlo do greške u komunikaciji (npr., ako slave nije odgovorio ACK-om nakon svakog primljenog bajta). Provjerit ćemo bit Err i, ako je detektirana greška, ispisati odgovarajuću poruku: If Err = 1 Then Locate 2 , 1 Lcd „Err „ Waitms 250 Err = 0 End If Na kraju potprograma, ispisat ćemo na LCD-u trenutne intenzitete boja i vratiti se u glavnu petlju: Gosub Disp_rgb Return

Isti potprogram pozivamo i kada program detektira da je pritisnuto tipkalo SW2, Sw2_sub: Gosub Disp_send nakon čega mjerimo koliko je dugo tipkalo bilo pritisnuto: For Auto_flag = 0 To 100 If Pinc.2 = 1 Then Exit For Waitms 10 Next Ako je tipkalo bilo pritisnuto duže od 1 s, vrijednost brojača Auto_flag bit će 101 i program prelazi u automatski način rada; ako je pritisnuto kraće, program se vraća u ručni način rada. Poruka o trenutno važećem načinu rada ispisuje se na LCD-u, nakon čega se program vraća u glavnu petlju: Locate 1 , 13 If Auto_flag > 100 Then Lcd „auto“ Else Lcd „ „ End If Return Kako smo prije pokazali, vrijednost varijable Auto_ flag provjerava se i u glavnoj petlji pa se, ako je automatski način rada aktiviran, RGB-modulu šalje poruka svakih 50 ms. U suprotnom, poruka će se poslati samo kada pritisnemo tipkalo SW2, upravo kako je u programskom zadatku i predviđeno. Potprogram Sw1_sub izvršava se svaki put kada pritisnemo tipkalo SW1. U njemu mijenjamo vrijednost varijable Rgb_indeks u rasponu od 0 do 4, čime odabiremo novu boju u skladu s programskim zadatkom i prikazom na Slici 81. Ovaj potprogram i druge dijelove master programa koji nemaju direktnog doticaja s osnovnom namjenom ovdje nećemo analizirati. Program Shield-A_23.ino Na početku programa definiramo biblioteke koje ćemo koristiti, objekt pomoću kojeg upravljamo s LCD-om i potrebne varijable: #include <Wire.h> #include <LiquidCrystal.h> LiquidCrystal lcd(2, 3, 4, 5, 6, 7); byte rd, gn, bl, rgb_index, auto_flag; const byte I2c_address = 0b1111000; int adc_value; Nazivi i namjena varijabli isti su kao u rješenju Bascom-AVR-a. U funkciji setup() uključit ćemo pozadinsko svjetlo pomoću tranzistorske sklopke T2, definirati pinove tipkala, pokrenuti I2C komunikaciju, definirati koji LCD-displej koristimo, definirati početnu vrijednost varijable rgb_index i pokrenuti funkciju sw1_function() kako bismo prikazali vrijednosti varijabli na displeju. void setup() { pinMode(17, OUTPUT); digitalWrite(17, HIGH); pinMode(A1, INPUT_PULLUP); pinMode(A2, INPUT_PULLUP);

Wire.begin(); lcd.begin(16, 2); rgb_index = 4; sw1_function(); } // end setup() U funkciji loop() svakih 50 ms provjeravamo stanja tipkala SW1 i SW2 te izvršavamo potprogram disp_adc() a, ako je odabran automatski način rada, i potprogram disp_send(): void loop() { if (digitalRead(A1) == 0) { delay(25); if (digitalRead(A1) == 0) { sw1_function(); while (digitalRead(A1) == 0){ } } } if (digitalRead(A2) == 0) { delay(25); if (digitalRead(A2) == 0) { sw2_function(); while (digitalRead(A2) == 0){ } } } disp_adc(); if (auto_flag > 100) disp_send(); delay(50); } // end loop() U funkciji disp_adc() mjerimo napon klizača potenciometra RV1, dijelimo izmjerenu vrijednost s 4 (time smo dobili raspon vrijednosti 0-255), i prikazujemo je na LCD-u: void disp_adc(){ adc_value = analogRead(A0)/4; lcd.setCursor(12,1); if (adc_value < 100) lcd.print(„0“); if (adc_value < 10) lcd.print(„0“); lcd.print(adc_value); } // end disp_adc() U funkciji disp_send(), ovisno o sadržaju varijable rgb_index, najprije ažuriramo intenzitet trenutno odabrane boje ili boja, void disp_send(){ switch(rgb_index){ case 1: rd = adc_value; break; case 2:

gn = adc_value; break; case 3: bl = adc_value; break; case 4: rd = adc_value; gn = adc_value; bl = adc_value; break; } disp_rgb(); send_rgb(); } // end disp_send() a zatim prikažemo sadržaj boja na displeju pomoću funkcije disp_rgb(): void disp_rgb(){ lcd.setCursor(0,1); if (rd < 100) lcd.print(„0“); if (rd < 10) lcd.print(„0“); lcd.print(rd); lcd.setCursor(4,1); if (gn < 100) lcd.print(„0“); if (gn < 10) lcd.print(„0“); lcd.print(gn); lcd.setCursor(8,1); if (bl < 100) lcd.print(„0“); if (bl < 10) lcd.print(„0“); lcd.print(bl); } // end disp_rgb() i pošaljemo podatke preko protokola I2C pomoću funkcije send_rgb(): void send_rgb(){ Wire.beginTransmission(I2c_address); Wire.write(rd); Wire.write(gn); Wire.write(bl); Wire.endTransmission(); } // end send_rgb() Kada program detektira da je pritisnuto tipkalo SW2, najprije ćemo izmjeriti koliko je dugo tipkalo bilo pritisnuto: void sw2_function(){ for (auto_flag = 0; auto_flag <= 100; auto_flag++)

if (digitalRead(A2) == 1) { break; } delay(10); } Ako je tipkalo bilo pritisnuto duže od 1 s, vrijednost brojača auto_flag bit će 101 i program prelazi u automatski način rada; ako je pritisnuto kraće, program se vraća u ručni način rada. Poruka o trenutno važećem načinu rada ispisuje se na LCD-u, nakon čega se program vraća u glavnu petlju: lcd.setCursor(12,0); if (auto_flag > 100) { lcd.print(„auto“); } else { lcd.print(„ „); } disp_send(); } // end sw2_function() Funkcija sw1_function() izvršava se svaki put kada pritisnemo tipkalo SW1 te je algoritamski identična rješenju za Bascom-AVR i ovdje je nećemo analizirati. Slika 82. potvrđuje da opisani programi rade i u stvarnosti! No, kada sve već tako dobro radi, šteta

Slika 82. Provjera RGB-modula

Slika 83. Shield-A s I2C LCD-om i RGB-modulom bi bilo bolje ne iskoristiti I2C sabirnicu ‒ zato smo na shemi sa Slike 83. na nju još povezali i LCD sa serijskim I2C modulom. Pokušajte sami prilagoditi programe Shield-A_23.bas i Shield-A_23.ino za takvu konfiguraciju (pomoć potražite u 20. nastavku, gdje smo detaljno opisali kako koristiti serijske LCD-e)! Napomena: Programi Shield-A_22.ino, Shield-A_23. bas i Shield-A_23.ino mogu se besplatno dobiti od uredništva časopisa ABC tehnike. Vladimir Mitrović i Robert Sedak

This article is from: