7 minute read
Shield-B, razvojna pločica za Arduino Uno (6)
Za one koji žele znati više, u ovom ćemo nastavku detaljnije opisati načine na koje mikroupravljač može generirati impulse potrebne za upravljanje radom servomotora. Bascom-AVR i Arduino koriste tehniku prekida (interrupt). Generiranje impulsa postiže se programskim postavljanjem upravljačkih pinova mikroupravljača u stanje “0” i “1” u prekidnim rutinama, koje se periodički izvršavaju. U prekidnim rutinama također se određuje trenutak u kojem upravljački pin nekog od servomotora mora promijeniti stanje.
Slika 17. ilustrira princip prekida na primjeru nekog programa Bascom-AVR-a. Nakon inicijalizacije (C0), program se izvršava unutar beskonačne petlje Do-Loop redoslijedom C1-C5-C6-C1... U trenutku kada se dogodi prekid uzrokovan nekim vanjskim događajem, izvršenje glavnog programa prekida se i počinje se izvršavati potprogram (prekidna rutina), koji odrađuje što je već potrebno. U našem slučaju, prekide će generirati vremenski sklop ‒ tajmer ‒ a u prekidnim rutinama mijenjat će se stanja upravljačkih pinova.
Advertisement
Svaki potprogram završava naredbom povratka u glavni program (u ovom primjeru, Return), nakon koje će se glavni program nastaviti izvršavati od trenutka u kojem je bio prekinut. Redoslijed izvršavanja programa u slučaju prekida je C1-C2-C3C4-C5-C6-C1... Identična procedura odvija se i u programima Arduino, samo je sintaksa drukčija.
Prekidi se mogu dogoditi u bilo kojem trenutku, pa tako i onda kada se u registrima mikroupravljača nalaze neke važne vrijednosti. Zbog toga program mora, prije nego li počne izvršavati prekidnu rutinu, sačuvati vrijednosti svih registara, kako bi ih prije povratka u glavni program mogao vratiti i nesmetano nastaviti izvršenje programa od točke prekida.
Kako bi se motori zakretali glatko, bez skokovitih promjena položaja, i kako bi mogli stabilno držati položaj u koji su postavljeni, te prekidne rutine trebaju se izvršavati u istim vremenskim intervalima i vrlo učestalo: parametri koje smo postavili u programu Bascom-AVR iz prethodnog nastavka uzrokuju ponavljanje prekidnih rutina približno svakih 10 µs. Ako mikroupravljač radi samo ono što smo zadali našim programskim zadatkom, to neće predstavljati problem. Međutim, ako mikroupravljač treba obavljati neki “ozbiljan” posao i usput voditi brigu o nekoliko servomotora, ovi će prekidi biti veliko opterećenje za procesor i znatno će usporavati izvršenje glavnog programa.
Drugi način na koji možemo generirati potrebne upravljačke impulse je upotrebom tajmera. Jednu smo primjenu tajmera već upoznali kada smo pokazali kako širinski moduliranim impulsima možemo upravljati brzinom istosmjernog motora (pogledajte treći i četvrti nastavak). Ovdje ćemo primijeniti vrlo sličnu tehniku!
Upravljački pinovi za servomotore na razvojnom sustavu Shield-B, PB.1 i PB.2, pomno su odabrani: osim što rade kao “obični” digitalni ulazi ili izlazi, oni mogu biti povezani i na izlaze OC1A i OC1B Timera1 (Slika 18.). Shema je identična onoj sa Slike 7. i radi na istom principu, samo što ovdje koristimo Timer1 i drukčije su vrijednosti upisane u usporedne registre, OCR1A i OCR1B. Timer1 je konfiguriran tako da broji impulse frekvencije 2 MHz od 0 do 19999 i zatim unatrag prema 0. Jedan takav ciklus traje 20 ms, pa će frekvencija generiranih impulsa biti upravo 50 Hz. Širina impulsa ovisi o vrijednostima upisanima u usporedne registre:
Kada se tijekom brojanja prema gore izjednači vrijednost brojača s brojem u nekom od usporednih registara, mikroupravljač će postaviti pridruženi pin u stanje “0”.
Kada se tijekom brojanja prema dolje izjednači vrijednost brojača s brojem u nekom od usporednih registara, mikroupravljač će postaviti pridruženi pin u stanje “1”.
Vrijednosti koje upisujemo u usporedne registre odgovaraju željenom trajanju impulsa u mikrosekundama. Možemo upisati bilo koju vrijednost između 0 i 19999, ali će za naše potrebe odgovarati vrijednosti u rasponu od 700 do 2300: uz njih će na pridruženim izlaznim pinovima OC1A i OC1B (~9 i ~10 na pločici Arduino) nastati impulsi širine 700-2300 µs! Spojimo li servomotore prema shemi sa Slike 15., njima ćemo upravljati pomoću potenciometara RV1 i RV2 na isti način kao u 5. programskom zadatku. Pogledajmo odgovarajuće programsko rješenje!
Program Shield-B_5a.bas
Pretvarač A/D, kao i pripadajuće varijable Rv1 i Rv2, u koje pospremamo očitanja napona klizača istoimenih potenciometara, konfiguriramo na isti način:
Dim Rv1 As Word , Rv2 As Word
Config Adc = Single , Prescaler = Auto ,
Reference = Avcc
Timer1 može raditi na 16 različitih načina, od kojih je većina podržana iz programskog jezika
Bascom-AVR primjenom naredbe Config Timer1.
Nažalost, to se ne odnosi na način rada koji smo opisali i ilustrirali na Slici 18., pa ćemo željeni način rada moći postići samo upisom odgova- rajućih vrijednosti u njegove konfiguracijske registre:
Icr1 = 19999
Tccr1a = &B10100010
Tccr1b = &B00010010
Vrijednost u registru ICR1 određuje granicu do koje brojač broji, a kombinacije bitova u preostala dva registra određuju da tajmer radi upravo ono što nam je potrebno. Kako bi se impulsi pojavili na izlaznim pinovima mikroupravljača, potrebno ih je konfigurirati kao izlazne:
Config Portb.1 = Output
Config Portb.2 = Output
U glavnoj petlji najprije mjerimo napon na klizaču potenciometara RV1. Dobivene vrijednosti su u rasponu 0-1023, a nama je potreban raspon 700-2300. Zbog toga ćemo svaku očitanu vrijednost najprije pomnožiti s faktorom 61/39, kako bismo je sveli u raspon 0-1600, a zatim rezultatu dodati 700 i upisati ga u usporedni registar OCR1A:
Do
Rv1 = Getadc(0) * 61
Rv1 = Rv1 / 39 ‚0-1600
Ocr1a = 700 + Rv1 ‚700-2300
Time smo u OCR1A upisali broj u rasponu 700-2300, upravo koliko je potrebno za generiranje odgovarajućih upravljačkih impulsa. Postupak s drugim servomotorom je identičan, samo ćemo ovdje vrijednost za usporedni registar OCR1B izračunati na način da se osovina servomotora zakreće u suprotnom smjeru:
Rv2 = Getadc(1) * 61
Rv2 = Rv2 / 39 ‚0-1600
Ocr1b = 2300 - Rv2 ‚2300-700
Loop
Prednost ovog postupka je očigledna: nakon što smo zadali naredbu tajmeru, on će početi proizvoditi impulse željene širine bez ikakvog opterećenja za program, koji se može posvetiti rješavanju drugih zadataka. Nedostatak je u tome što ovako možemo upravljati radom samo dva servomotora, koji još uz to moraju biti spojeni na točno određene pinove mikroupravljača. Druga su dva tajmera koje mikroupravljač ATmega328P ima, Timer0 i Timer2, 8-bitni i praktično su neupotrebljivi na ovaj način. U njihove usporedne registre moguće je upisati vrijednosti manje od 256 pa će se finoća rastera kojim možemo generirati upravljačke impulse znatno pogoršati; pomoću njih bi servomotore bilo moguće skokovito postaviti u samo 10-ak položaja.
Na razvojnom sustavu Shield-B, servomotore možemo spojiti i na konektor J7, originalno predviđen za I2C komunikaciju (Slika 19.). Upravljačke signale generirat ćemo na pinovima PC4 (A4) i PC5 (A5) koji nisu povezani s tajmerom, pa ćemo morati koristiti tehniku prekida. Na početku članka objasnili smo kako to funkcionira, i pritom ukazali na probleme koje takav pristup uzrokuje.
Možemo li uopće napisati program Bascom-AVR, koji će efikasno upravljati servomotorima spojenima na pinove mikrokontrolera koji nisu povezani s tajmerom? Možemo, ako se ograničimo na dva motora i ako napišemo vlastite upravljačke rutine!
Osnovna ideja prikazana je na Slici 20. Timer1 radi na isti način kao na Slici 18., osim što ne proizvodi upravljačke impulse na svojim pinovima OC1A i OC1B; umjesto toga, u trenucima kada se brojač izjednači s vrijednostima u usporednim registrima OCR1A i OCR1B, pokreće pridružene prekidne rutine Comp1a_sub i Comp1b_sub
Prekidne rutine dijelovi su programa pridruženi nekom događaju i na slici su obojane žuto, kako bi ih razlikovali od posla prikazanog “plavim” simbolima, koji odrađuju sklopovi koji čine Timer1.
Tijekom jednog ciklusa brojanja, koji traje 20 ms, svaka prekidna rutina aktivira se dva puta: jednom kod brojanja prema naprijed, drugi put kad brojač broji unatrag. U prekidnoj rutini radimo vrlo jednostavan posao: prilikom svakog aktiviranja komplementiramo stanje upravljačkog pina (ako je u stanju “0”, postavljamo ga u “1”, i obratno). Pogledajmo kako je to riješeno u programu!
Program Shield-B_5b.bas
Uvodni dio programa i konfiguracija pretvarača A/D identični su kao u prethodnom programu. Tajmer ćemo ipak konfigurirati malo drukčije, jer sada ne smije proizvoditi impulse na svojim pridruženim pinovima:
Icr1 = 19999
Tccr1a = &B00000010
Tccr1b = &B00010010
Još ćemo pinove PC4 i PC5 konfigurirati kao izlazne
Config Portc.4 = Output
Config Portc.5 = Output i, što je vrlo važno, postaviti ih u ispravno početno stanje:
Reset Portc.4
Reset Portc.5
Kada to ne bismo učinili, umjesto pozitivnih mogli bismo dobiti negativne upravljačke impulse, na koje servomotor ne bi ispravno reagirao.
Još moramo definirati prekidne rutine (potprograme) pridružene prekidima. U našem primjeru, prekidi nastaju kada se stanje brojača izjednači s vrijednostima u usporednim registrima OC1A i OC1B, a pripadne prekidne rutine su Comp1a_sub i Comp1b_sub:
On Oc1a Comp1a_sub
Nosave
On Oc1b Comp1b_sub
Nosave
Opciju Nosave pojasnit ćemo na kraju članka. Iako su definirane, prekidne se rutine neće izvršavati dok im to ne omogućimo odgovarajućim naredbama:
Enable Oc1a
Enable Oc1b
Enable Interrupts
U ovom trenutku započinje proces ilustriran na Slici 20. Slijedi glavna programska petlja Do-Loop, identična onoj u prethodnom primjeru: mikroupravljač mjeri napone na klizačima potenciometara i konvertira ih u vrijednosti pogodne za upravljanje radom servomotora te ih posprema u upravljačke registre.
Prekidne rutine programiramo poslije glavne petlje. Kako u njima samo trebamo promijeniti stanje upravljačkih pinova, one bi mogle izgledati ovako:
Comp1a_sub:
Portc.4 = Not Portc.4
Return Comp1b_sub:
Portc.5 = Not Portc.5
Return Samo jednom naredbom postižemo željeni cilj! Međutim, tu postoji i jedan skriveni problem: naredbe Bascom-AVR tijekom izvršenja koriste neke registre opće namjene. Mi ne znamo koje registre koja naredba koristi, pa iz predostrožnosti moramo pospremiti vrijednosti svih registara prije izvršenja prekidne rutine, kako bismo ih na završetku mogli vratiti u originalno stanje. To je preduvjet da bi se “prekinuti” glavni program mogao nesmetano nastaviti izvršavati, jednom kada prekidna rutina odradi svoj zadatak. Pospremanje registara za nas će napraviti prevodilac Bascoma ako izostavimo opciju Nosave, ali to će onda znatno produžiti trajanje prekidne rutine. Jedno od osnovnih pravila dobrog programiranja je da prekidne rutine trebaju trajati što je moguće kraće. Zbog toga ćemo prekidne rutine napisati u asembleru. Evo kako to izgleda:
Comp1a_sub: sbic portc,4 rjmp +3 sbi portc,4 rjmp +2 cbi portc,4
Return
Ovdje nam nije interes upoznavati asembler, naglasit ćemo samo osnovne razlike! Programiramo li u asembleru, često moramo napisati više, ponekad i puno više programskih naredbi, kako bismo postigli rezultat koji postižemo jednom jedinom naredbom Bascom-AVR-a.
Međutim, programirajući u asembleru imamo potpunu kontrolu nad radom mikroupravljača, pa tako i nad registrima koje pojedina naredba koristi. U ovom smo primjeru upotrijebili samo naredbe koje ne koriste registre opće namjene, pa je upotreba opcije Nosave opravdana. Izvršenje ovako napisane prekidne rutine traje točno 1 µs i ni na koji način neće ometati izvršenje glavnog programa. Usporedbe radi, izvršenje ekvivalentne naredbe Bascom-AVR-a bez Nosave opcije bi trajalo 7,5 puta duže!
Budete li analizirali program Shield-B_5b.bas, primijetit ćete kako je u njemu predviđen još jedan prekid, Capture1, kojemu je pridružena prekidna rutina Capt1_sub. U njoj se postavljaju upravljački pinovi u stanje “0” u trenucima kada brojilo dostigne maksimalnu vrijednost, 19999. To je zamišljeno kao dodatno osiguranje kako bi impulsi uvijek bili ispravnog polariteta. U praksi se ovo pokazalo nepotrebnim, pa su pripadajuće naredbe u konačnoj verziji programa zakomentirane.
Napomene: Programe Shield-B_5a.bas i ShieldB_5b.bas možete besplatno dobiti od uredništva časopisa ABC tehnike!
Autori: Vladimir Mitrović, Robert Sedak