Fórum témák
» Több friss téma |
Cikkek » Launchpad: ismerkedés az MSP430 mikrovezérlőkkel II. Launchpad: ismerkedés az MSP430 mikrovezérlőkkel II.
Szerző: icserny, idő: Nov 3, 2011, Olvasva: 22767, Oldal olvasási idő: kb. 17 perc
Az impulzus-szélesség moduláció (PWM) alapjaiA mikrovezérlők alkalmazásainál gyakran előforduló feladat, hogy hogy valamilyen mennyiséget (pl. kimenő feszültséget, vagy egy fogyasztó teljesítményét) folyamatosan vagy fokozatosan kell szabályozni. Mivel a Launchpad-hoz kapott mikrovezérlőinken nincs analóg kimenet, ezért élnünk kell azzal a lehetőséggel, hogy analóg feszültségjelei (teljesítményjelei) helyettesíthetők digitális impulzussorozat-jelekkel, amelyek hosszabb időtartamra vonatkoztatott átlagfeszültsége egyenértékű az analóg feszültségjellel. A digitális impulzussorozat frekvenciáját úgy kell (elegendően nagyra) megválasztani, hogy az, a vezérelt vagy szabályozott eszköz megfelelő működését biztosítsa. Például a szabályozott fényforrás folyamatos működésűnek látsszon (a szemünk ne vegyen észre villogást), vagy egy egyenáramú motor ne lökésszerűen változó szögsebességgel forogjon. A digitális jelsorozattal történő vezérlés egyik jól használható változatai az impulzus-szélesség moduláció (PWM = Pulse Width Modulation), amelynél olyan állandó periódusidejű (és frekvenciájú) jeleket keltünk, ahol az a szabályozás a jel kitöltési tényezőjének változtatásával történik. Néhány különböző kitöltési tényezőjű PWM jelet mutat be a következő ábra: 8_1. ábra: Azonos periódusú, de különböző kitöltésű impulzus-sorozatok A fentihez hasonló jeleket szoftveresen is könnyű előállítani, de a Timer_A egység Capture/Compare csatornáinak kombinált alkalmazásával hardveres támogatást is kapunk hozzá, ami számunkra azt jelenti, hogy a CPU szabad az egyéb feladatok végrehajtására, a jelek időzítése pedig pontosabb. PWM jelek előállításához a Timer-t "fölfelé számlálás adott értékig" módban használjuk (MCx = 1), s a kívánt értéket a TACCR0 regiszterbe írjuk. Ez az érték, és a bemenő jel frekvenciája szabja meg a a PWM jel periódusát, illetve frekvenciáját. A 0. Capture/Compare csatorna kimenete - a TACCR0 regiszter fentebb vázolt speciális szerepénél fogva - csak igen korlátozottan használható PWM-hez, pl. átbillentés módban (OUTMODx = 4, toggle mode) állandóan 50 %-os kitöltéssel rendelkező jelet kelthetünk vele. Az 1. Capture/Compare csatorna azonban többféle hasznos üzemmóddal is rendelkezik. Például reset/set módban (OUTMODx = 7) a TACCR1 regiszterbe írt szám elérésekor a TA1 kimenet törlődik (alacsony szintre vált), a számláló túlcsordulásakor pedig '1'-be áll. A kitöltési arányt a TACCR1 regiszterbe írt számnak és és TACCR0 + 1-nek a hányadosa adja meg. Egy másik lehetőség a set/reset (OUTMODx = 2) üzemmód használata, ami a 7-es módban előállított jelnek az inverzét állítja elő. Az alábbi ábrán egy konkrét példán keresztül mutatjuk be az egyes csatornák megszakítási és kimenő jeleinek alakulását a számlálás előrehaladásának függvényében. A számláló modul itt "fölfelé számlálás egy edott értékig" (MCx=1) módban van. A TAR jelzésű sor mutatja a számláló regiszter állapotát, TAIFG pedig a számláló túlcsordulását jelző bit bebillenésének helyét (amikor a számláló a maximális értékről nullára vált). CCIFG0 és CCIFG1 a Capture/Compare csatornák megszakításjelző bitjei. Amint látható, a CCIFG0 bit a TACCR0 regiszterben megadott szám (az ábrán ez az érték 4) elérésekor billen be, tehát egy óraütéssel hamarabb, mint TAIFG. A CCIFG1 bit pedig akkor billen '1'-be, amikor a számláló eléri a TACCR1 regiszterben megadott számot (az ábrán ez az érték 2). 8_2. ábra: A Capture/Compare csatornák kimenő jelei Az 1. Capture/Compare csatorna kimenő jelét az OUT1 képviseli. mely a leggyakrabban használt kétféle mód (Reset/Set és Set/Reset) esetére az ábrán látható módon alakul: OUTMODx=7 beállítás esetén a TAIFG bebillenésekor OUT1 magas szintre vált, a TACCR1-be írt szám elérésekor pedig törlődik. A Set/Reset módban (OUTMODx=3) ez pont fordítva történik, tehát a kimenő jel az előző eset inverze lesz. A 0. Capture/Compare csatorna kimenete OUT0, ennek jelakja a már említett Toggle módban egy 50 %-os kitöltésű négyszögjel. A kimenet ugyanis minden periódusban ellenkezőjére változik, így ugyanannyi ideig van bekapcsolt mint kikapcsolt állapotban. Természesen a fentieken kívül sokféle üzemmód és használati mód megvalósítható, melyek részletese megtárgyalása meghaladja ennek a cikknek a lehetőségeit. Az alábbiakban csak néhány egyszerű példát mutatunk be, amelyek talán segítenek a leggyakrabban használt módok megértésében. Szoftveres PWMAz alábbi programban a Timer egység nélkül, csupán szoftveres időzítésekkel állítunk elő időben változó kitöltésű négyszögjelet. A programban egyébként LED1 fényerejét változtatjuk folyamatosan, mintha a LED "lélegezne". A CPU órajele MCLK = 1 MHz lesz. 66,67 Hz PWM frekvenciát választva periódusonként 15 000 ciklus esik (150 db 0,1 ms-os óraütés), ez a bekapcsolt és kikapcsolt állapot összideje. A LED kb. 1,5 s-os félperiódusa alatt 100 lépésben lineárisan növekvő kitöltést adunk meg, a következő 100 periódusban pedig csökkenő kitöltéssel lépkedünk vissza. 8_1. lista: a pwm_soft program listája
A program elején definiálnunk kell egy késleltető eljárást, ugyanis a __delay_cycles() beépített függvényt csak konstans paraméterrel hívhatjuk meg (vagy fordítási időben kiértékelhető kifejezéssel), változóval nem. A késleltető eljárásban azzal trükközünk, hogy egy for ciklust szerveztünk, melynek ciklusmagja egy fix idejű (estünkben 100 µs) késleltetést tartalmaz, amit tetszőleges számszor ismételhetünk. A beépített függvény meghívása és a ciklus szervezése is időt vesz igénybe, ezért megpróbáltuk ezeket kompenzálni, a __delay_cycles() függvény paraméterének csökkentésével. Az inicializáló részben a gyárilag kalibrált 1 MHz-es DCO frekvenciát állítottuk be, s kimenetnek állítjuk a P1.0 lábat, amelyhez LED1 csatlakozik. A végtelen ciklus magjában két for ciklus található. Az elsőben fokozatosan növeljük a bekapcsolt állapot időtartamát, a kikapcsolt állapot időtartamával pedig mindig 150 ciklusra (150 x 100 µs = 15 ms-ra) egészítjük ki. A második for ciklusban ennek a fordítottját csináljuk: fokozatosan csökkentjük a bekapcsolt állapot időtartamát. A program legnagyobb előnye, hogy egyszerű és alkalmas a PWM szemléltetésére, ugyanakkor teljesen lefoglalja a CPU-t, az időzítése pedig pontatlan, mivel a kimenő jel átmeneteit szoftveresen kapcsolgatjuk. A LED sem pontosan azt csinálja, amit várunk tőle, mert a fényerő növekedése-csökkenése nem lineárisan történik. Ebben azonban nem a program, hanem a szemünk a ludas, amely logaritmikus érzékenysége miatt exponenciálisan növekvő teljesítmény hatására látna egyenletesen növekvő fényességet. A felmerült problémákat a következő programban próbáljuk orvosolni. PWM hardveres támogatássalAz alábbi programban a Timer_A modul nyújtotta hardveres támogatást használjuk a PWM jel előállítására. A program LED2 fényerejét folyamatosan változtatja, mintha a LED "lélegezne". A program előzményei és a kiinduláshoz felhasznált forrásai:
A Timer órajele SMCLK = 1 MHz lesz. 100 Hz PWM frekvenciát választva 10 000 impulzust kell leszámolni, tehát TACCR0 = 9999 legyen. A LED kb. 2 s-os félperiódusa alatt 200 lépésben négyzetesen növekvő kitöltést adunk meg, a következő 200 periódusban pedig csökkenő kitöltéssel lépkedünk vissza. Technikai okoból azonban nem hajthatunk végre 200 lépést, hanem csak 198-at, mert ha a TACCR1 < TACCR0 feltétel nem teljesül, akkor nem kapunk megszakítást az 1. sorszámú Capture/Compare csatornából, s "elakad" a programunk! Algoritmus: A kitöltést exponenciális helyett négyzetesen növekedő számokkal állítjuk be, tehát a 0, 1, 4, 9, 16, 25, ... stb. sorozatot állítjuk elő. Mivel 200 lépésnél ez már 40 000 volna, ami négyszer több a periódusnál, ezért az előállított négyzetszámot néggyel osztjuk. A négyzetszámok sorozatát Rusty Haddock programjához hasonlóan a páratlan számok felösszegzésével állítjuk elő, a néggyel történő osztást pedig logikai eltolással végezzük el. Ilyen módon kiküszöböltük a szorzás és az osztás műveleteket, ami azért fontos, mert ezeket a műveleteket a Launchpadhoz kapott mikrovezérlőink hardveresen nem támogatják, szoftveresen pedig túlságosan időigényes volnának. A kiszámolt kitöltési tényezőt a new_ccr1 nevű változóban fogjuk tárolni. Üzemmód beállítások
8_2. lista: a pwm_led program listája
A program elején most is beállítjuk a gyárilag kalibrált 1 MHz-es DCO frekvenciát, s ez lesz MCLK és SMCLK forrása/frekvenciája. A P1.6 lábat kimenetnek állítjuk be, s a P1SEL 6. bitjének '1'-be állításával a TA1 kimenethez rendeljük hozzá. Ez azt jelenti, hogy az adatlapban (vagy cikksorozatunk 1. részében a 4_1. ábrán) bemutatott bekötési rajzon feltüntetett alternatív funkciók közül P1.6-nál most TA0.1 lesz érvényben, a kimenetet tehát most Timer_A-nak az 1. számú Capture/Compare csatornája vezérli. A PWM módhoz TACCR0-ba írjuk a periódust megszabó számot. Talán túlzott finomkodásnak tűnik, hogy 10000 helyett 9999-et írunk bele, de szokjuk meg, hogy a periódus TACCR0 + 1! A TACCR1 regiszterbe pedig a kitöltést írjuk. A kitöltés 0 és TACCR0 közötti szám lehet. Előbbi a 0, utóbbi a 100 %-os kitöltési tényezőnek felel meg. Arra azonban kínosan ügyelnünk kell, hogy TACCR1 ne haladja meg TACCR0 értékét, mert akkor nem kapunk megszakítást (TAR sohasem éri el TACCR1 értékét)! TACTL beállításánál bemenő jelnek az SMCLK órajelet választottuk (TASSELx=2), s a "felfelé számlálás adott értékig" módot (MCx=1). Előosztást nem használunk, tehát IDx=0 (nem írtuk ki, mert úgyis nulla...). TACCTL1 beállításánál az OUTMODx=7 üzemmódot választottuk (TACCR1 elérésekor Reset, azaz TA1=0 lesz, TACCR0 elérésekor pedig Set, azaz TA1=1 lesz.. A TACCL1 regiszter beállításánál kell engedélyeznünk a CCIFG1 megszakítást is, a CCIE bit '1'-be állításával.. A beállítások után a főprogramnak nem lesz több dolga, ezért aludni küldhetjük (MCLK lekapcsolható). Vigyáznunk kell azonban arra, hogy SMCLK és DCO nem kapcsolható le, ezért itt most csak az LPM0 módot használhatjuk. Az energiatakarékos módot beállító __low_power_mode_0() függvényhívás egyúttal az interrupt engedélyezését is elvégzi (az SR státusz regiszter GIE bitjét is '1'-be állítja. A CCIFG1 megszakításhoz a megosztott használatú TIMERA1_VECTOR tartozik. Mivel más megszakítást nem használunk, most nem muszáj a TAIV regisztert nézegetni. Ekkor viszont nekünk kell gondoskodnunk a megszakítási jelzőbit törléséről. A TIMERA1_VECTOR megszakítást kiszolgáló eljárás elején módosítjuk a TACCR1 regisztert, de előbb megvárjuk, hogy a számláló meghaladja a beírandó értéket. Ha erre nem ügyelünk, a LED fényének zavaró villózásai jelzik, hogy bizonyos esetekben a kívánt jelalaktól eltérő tranzienseket produkálunk. A soron következő kitöltési tényező kiszámolása és elraktározása után gondoskodnunk kell a következő négyzetszám előállításáról. Az első 198 lépésben fölfelé lépkedünk a számokkal (azért nem megyünk el egészen 200-ig, mert ott már elérnénk vagy meghaladnánk a TACCR0-ba írt számot, s akkor már nem kapunk CCIFG1 megszakítást). A második 198 lépésben visszafelé lépkedünk a négyzetszámokon, tehát fokozatosan csökkentjük a kitöltést. Bár a program azt csinálja, amit vártunk tőle, de vannak gyengéi, amelyeket könnyen kitapasztalhatunk, ha a megszakítást kiszolgáló ta1_isr() függvényből kihagyjuk a while(TAR <= new_ccr1); várakozást, vagy megváltoztatjuk a beírt indexhatárokat (199, 397). Előbbi növelésénél elakad a program, mert nem keletkeznek megszakítások, utóbbi megváltoztatásánál pedig "nem találunk" vissza a számsorozat előállításhoz használt segédváltozók (next_sqr, sqr_step) kezdőértékeihez, s emiatt a számsorozat minden ciklusban eltolódik, s "elhülyül" a program. Csúnya megoldás az is, hogy a megszakítást kiszolgáló eljárásba várakozást teszünk, s bizonyos alkalmazásoknál zavaró lehet az is, hogy a 100 %-os kitöltés nem állítható be. Ezeknek a problémáknak egy részét legkönnyebben úgy kerülhetjük el, ha a kitöltés beállítását nem a CCIFG1, hanem a CCIFG0 megszakításban végezzük el! Itt viszont rögtön felmerül az újabb probléma: a 8_2. ábrán látható, hogy a CCIFG0 megszakítás és a TAR számláló túlcsordulás között csak egy óraütésnyi időkülönbség van. Az 1 MHz-es SMCLK használata esetén ez egyetlen utasításciklusnyi idő, ami alatt el sem jutunk a megszakítást kiszolgáló eljáráshoz, nem hogy beállíthatnánk a TACCR1 regiszter értékét. Így a nagyon kis kitöltések esetén sorra elkésünk a beállítással. Mi lesz ennek a következménye? Az, hogy a Reset/Set módra való tekintettel TAR túlcsordulásakor automatikusan '1'-be álla a TA1 kimenet, és a periódus végéig '1'-ben is marad (TA1 kimenőjele a 100 %-os kitöltésnek felel meg), mert amikor a TACCR1-el való egyezésnek be kellene következnie, akkor még nem írtuk bele a kívánt (kis) értéket. Ezen a problémát úgy tudjuk elkerülni, ha a CPU működését ütemező MCLK órajel sokkal nagyobb frekvenciájú, mint Timer_A bemenőjele. Egy kézenfekvő választás: MCLK = 1 MHz (DCO), Timer_A bemenőjele pedig ACLK (kvarccal 32 kHz, vagy VLO választásával 12 kHz). PWM kitöltés beállítása CCIFG0 megszakításkorAz alábbi program az előző példa módosított változata. Itt most Timer_A bemenőjele a VLO 12 MHz-es órajele lesz, s a TIMERA0_VECTOR(CCIFG0) megszakításakor frissítjük a PWM kitöltés értékét. Az ACLK = 12 kHz-es órajel miatt a 100 Hz-es PWM frekvencia biztosításához 1200 impulzust kell leszámolni, tehát TACCR0 = 119 legyen. A LED kb. 2 s-os félperiódusa alatt 200 lépésben négyzetesen növekvő kitöltést adunk meg, a következő 200 periódusban pedig csökkenő kitöltéssel lépkedünk vissza. Most végrehajthatunk 200 lépést, nem kell a TACCR1 < TACCR0 feltétel teljesülésével foglalkoznunk, mert a 0. sorszámú Capture/Compare csatornából garantáltan kapunk megszakítást, 100 %-os kitöltésnél sem "akad el" a programunk! Arra viszont ügyelnünk kell, hogy most kevesebb órajelből áll egy periódus, ezért csak kisebb felbontással (durvább lépésekben) tudjuk szabályozni a kitöltést. Ha nem akarjuk a 100 %-os kitöltést többszörösen meghaladni, akkor jóval nagyobb számmal kel leosztani a 200 lépésben 40 000-ig terjedő négyzetszámokat. Az alábbi programban 512-vel osztunk (9 bites eltolás jobbra), így 200 lépésnél 75 lesz a TACCR1 regiszter maximális értéke 120 helyett (nem érjük el a 100 %-os kitöltést). Tulajdonképpen 333-mal kellene osztanunk, de ez nem oldható meg gazdaságosan, ezért maradtunk a kisebb fényerőnél. Üzemmód beállítások
8_3. lista: a pwm_led2 program listája
A főprogramban az 8_2. programhoz képest megváltozott üzemmódok miatt az alábbi változtatásokra van szükség:
A megszakítás kiszolgálásánál a 8_2. programhoz képest a következő eltérések vannak:
Összegzés:Amint láttuk, ez a változat robosztusabb (nem akad el) és egyszerűbb, tisztább (nincs várakozás a megszakításban) . Ennek ára az volt, hogy a korábbi 10 000 lépés helyett itt legfeljebb 120 lépésben tudjuk változtatni a kitöltést. Nagyobb frekvenciájú Timer órajel esetén viszont gondban vagyunk a kitöltés alkalmas időben történő beállításával. John Davies "MSP430 Microcontroller Basics" c. könyvében kimerítően elemzi ezt a problémát (8.6.6 alfejezet). A problémák gyökere az, hogy TACCR1 regiszter nem kettős bufferelésű, a Timer_A típusú időzítő tehát nem ad elegendő hardver támogatást a PWM gond nélküli használatához. A nagyobb kiépítettségű MSP430 mikrovezérlők Timer_B típusú időzítője alkalmasabb lenne a PWM használatához. Két, független PWM csatorna egyidejű vezérléseA Launchpad kártyához kapott mikrovezérlőink hivatalosan csak egy PWM csatorna használatára alkalmasak (TAR számlálja a futó időt, TACCR0 állítja be a periódust, TACCR1 pedig a kitöltést). Egy kis szerencsével vagy trükközéssel azonban megoldható, hogy TACCR0 felszabaduljon, és egy második PWM csatorna vezérlésre használhassuk. Az egyik lehetőség az, ha úgy választjuk meg az órajelet és a PWM frekvenciát, hogy egy PWM periódus 2^16 impulzus legyen. Ekkor természetesen a PWM frekvencia mindkét csatornában azonos kell, hogy legyen. A másik, jóval rugalmasabb használatot jelentő lehetőség az, hogy a számlálót szabadonfutó módban járatjuk, s a TACCR0 és TACCR1 regiszterekbe mindig a következő rész-ciklus (a bekapcsolt vagy kikapcsolt állapot időtartama) időzítését írjuk ( a TAR regiszter pillanatnyi állapotához hozzáadva a kívánt várakozási időhöz tartozó óraütések számát). Erre mutatunk be most egy példát, amelyben LED1 1 s-os periódussal villog, állandó 1/60 kitöltéssel. Ez ugyan nem a szokványos módja a PWM használatának, de nem akartam bonyolítani a programot a kitöltési tényező változtatásával. LED2 a korábbiakhoz hasonlóan a "lélegző LED" műsorát adja elő: fényereje fokozatosan erősödik, majd elhalványul. Periódusa ~3,4 s Üzemmód beállítások
A fenti beállításokat tartalmazó főprogram az alábbi listában látható. A megszakításokat kiszolgáló eljárásokat külön listákon mutatjuk be. 8_4. lista: a pwm_cont program főprogramjának listája
A TIMERA0_VECTOR-hoz tartozó CCIFG0 megszakítás kiszolgálásakor a megszakításjelző bit automatikusan törlődik, nem kell foglalkoznunk vele. A Toggle üzemmód elvileg a kimenetet is beállítaná, de mivel LED1 nem olyan kivezetésre van kötve, ami definiálható volna TA0 kimenetként, ezért a LED állapotát nekünk kell átbillenteni szoftveresen. Ezen kívül arról kell még gondoskodnunk, hogy TACCR0 új értékét beállítsuk. Azt LED1 pillanatnyi állapota dönti el, hogy milyen ciklus következik, ezért a P1OUT regiszter 0. bitjét vizsgáljuk. A P1OUT_bit.P0 elérési formát az IAR fordítóhoz tartozó fejléc állományokban szerepló únió (union) deklarációk teszik lehetővé (a Code Composer Studio-ban nem találtam ilyen lehetőséget). Ezek nélkül csak a kevésbé szemléletes P1OUT&BIT0 kifejezést használhatnánk. Az 1 s-os periódusidőhöz tartozó 12 000 óraimpulzust az 1:60 arányú kitöltésnek megfelelően 200 : 11800 arányban osztottuk fel. A CCIFG0 megszakítást kiszolgáló eljárás listája így néz ki: 8_5. lista: a CCIFG0 megszakítás kiszolgálása a pwm_cont programban
A kitöltési tényezőt ahhoz hasonló módon állítjuk elő, ahogy a korábbi programok "lélegző LED" megoldásainál. Itt most valamivel rövidebb ciklusokat szerveztünk, mint az előző programban (170 lépés felfelé, 170 lépés lefelé), ezért elegendő a négyzetszámokat 256-tal történő osztással (8-bites eltolással) skálázni. Tapasztalati úton azonban arra a következtetésre jutottunk, hogy 2-nél rövidebb kitöltéssel már nem működik helyesen a program, a kiszámított kitöltéshez ezért mindig hozzáadunk kettőt.. A CCIFG1 megszakítást kiszolgáló eljárás listája így néz ki: 8_6. lista: a CCIFG1 megszakítás kiszolgálása a pwm_cont programban
További lehetőségekAz eddigi mintaprogramokban korántsem merítettük még ki Timer_A üzemmódjainak és felhasználási mó lehetséges módjait. Nem foglalkoztunk például sem a bemeneti jelrögzítés (Input Capture) móddal, sem a Compare egység speciális üzemmódjaival. Ezekre valószínűleg sor kerül még cikksorozatunk későbbi részeiben. Addig is nagy szeretettel ajánlom John Davies: MSP430 Microcontroller Basics ("MSP430 mikrovezérlő alapok") könyvét amelyik kimerítően foglalkozik ezekkel. Letöltések
Fórumok
Értékeléshez bejelentkezés szükséges! |
Bejelentkezés
Hirdetés |