Fórum témák
» Több friss téma |
Cikkek » Launchpad: ismerkedés az MSP430 mikrovezérlőkkel I. Launchpad: ismerkedés az MSP430 mikrovezérlőkkel I.
Szerző: icserny, idő: Szept 7, 2011, Olvasva: 51204, Oldal olvasási idő: kb. 9 perc
Az I/O portok kezeléseAz első saját projektünkben már találkoztunk az általános célú digitális ki/bemenetek kimenetként történő használatával, amikor a LED-et villogtattuk. Digitális bemenetként is használhatjuk a P1 és P2 port lábait, például a P1.3 bemenetet az S2 nyomógomb állapotának figyelésére. A láb állapota nyilbánvalóan digitális, mert a nyomógomb vagy lenyomott állapotban van (ekkor a bemenet alacsony szintre kerül), vagy felengedett állapotban (ekkor a bemenetet az R34 ellenállás magas szintre húzza). További lehetőség, hogy a bemenő jel megváltozása programmegszakítást is okozhat, s megválaszthatjuk, hogy a megszakítás felfutó vagy lefutó élre történjen. Minden bemenethez felhúzást vagy lehúzást is engedélyezhetünk, ami a mikrovezérlőhöz csatlakozó áramkör egyszerűsítését teszi lehetővé (pl. közvetlenül csatlakoztathatunk nyomógombot valamelyik bemenetre). Az MSP430G2231 és MSP430G2211 mikrovezérlőink P1 és P2 portjának működését az alábbi regiszterek segítségével vezérelhetjük: A P1 porthoz tartozó regiszterek
|
Regiszter |
Cím | A regiszternév rövid magyarázata |
P1IN | 0x0020 | PORT1 input (P1 bemenet) |
P1OUT | 0x0021 | PORT1 output (P1 kimenet) |
P1DIR | 0x0022 | PORT1 direction (P1 adatáramlási irány) |
P1IFG | 0x0023 | PORT1 interrupt flags (P1 megszakítási kérelmet jelző bitek) |
P1IES | 0x0024 | PORT1 edge select (P1 fel- vagy lefutóél választása megszakításhoz) |
P1IE | 0x0025 | PORT1 interrupt enable (P1 programmegszakítás engedélyezés) |
P1SEL | 0x0026 | PORT1 selection (P1 funkcióválasztó bitek) |
P1REN | 0x0027 | PORT1 resistor enable (P1 fel-, vagy lehúzás engedélyezés) |
A P2 porthoz tartozó regiszterek
Regiszter |
Cím | A regiszternév rövid magyarázata |
P2IN | 0x0028 | PORT2 input (P2 bemenet) |
P2OUT | 0x0029 | PORT2 output (P2 kimenet) |
P2DIR | 0x002A | PORT2 direction (P2 adatáramlási irány) |
P2IFG | 0x002B | PORT2 interrupt flags (P2 megszakítási kérelmet jelző bitek) |
P2IES | 0x002C | PORT2 edge select (P2 fel- vagy lefutóél választása megszakításhoz) |
P2IE | 0x002D | PORT2 interrupt enable (P2 programmegszakítás engedélyezés) |
P2SEL | 0x002E | PORT2 selection (P2 funkcióválasztó bitek) |
P2REN | 0x002F | PORT2 resistor enable (P2 fel-, vagy lehúzás engedélyezés) |
Amint látjuk, a két port regiszterkészlete azonos. Megjegyezzük azonban, hogy az MSP430 mikrovezérlő család nagyobb kiépítettségű tagjainál, ahol kettőnél több I/O port is van, a magasabb sorszámú portokhoz már kevesebb regiszter tartozik. Külső jellel történő programmegszakítás ugyanis csak a P1 és P2 portokon lehetséges, s felhúzó ellenállás és az ehhez tartozó PxREN regiszter csak az MSP430F2xx családnál és az újabb MSP430x4xx típusoknál van.
Ahogy korábban már említettük, az I/O portok kivezetései bitenként beállíthatók kimenetnek vagy bemenetnek, tehát az I/O portok kezelése nem kizárólag bájtonkénti eléréssel történhet, hanem bitműveletekkel is. Sőt, talán ez utóbbi a gyakoribb eset...
Mivel P1 és P2 regiszterkészlete hasonló, csak a P1 port regisztereit ismertetjük részletesebben.
P1IN - a P1 port bemenő regiszterének nevezzük, de valójában nem regiszter (nem tartozik hozzá adattárolásra alkalmas eszköz), hanem a a mikrovezérlő P1.x jelzésű digitális bemeneteinek pillanatnyi állapotát olvashatjuk be ezen a címen. A P1IN regiszter olvasása előtt gondoskodnunk kell róla, hogy a vizsgálni kívánt bemenetek digitális I/O funkciója legyen kiválasztva (pl. P1SEL = 0; P1 portnál ez az alapértelmezett, P2-nél nem), s hogy a vizsgálni kívánt lábon az adatáramlási irány bemenetnek legyen beállítva (P1DIR megfelelő bitjeinek állapota '0' legyen).
P1OUT - a P1 port kimeneti adatregisztere. Elektronikában jártasak ezt egy D-típusú tárolónak képzeljék el, amely őrzi a beleírt adatot. Az adat vissza is olvasható, s ha valamelyik bitjéhez tartozó kivezetés digitális kimenetre van állítva (P1SEL.x=0 és P1DIR.x=1, akkor a kimeneten megjelenik a P1OUT.x-be írt állapot).
Egy fontos megjegyzés: Megjegyezzük, hogy az analóg-digitális átalakítóval rendelkező mikrovezérlőknél (esetünkben MSP430G2231 ilyen) egy adott láb digitális módba történő állításához az adott lábhoz tartozó analóg funkciót is le kell tiltani az ADC10AE regiszter megfelelő bitjének nullára állításával (pl. P1OUT.x esetén ADC10AE.x=0). Első projektünkben azért nem foglalkoztunk ezzel, mert bekapcsoláskor ez az alapértelmezett beállás. Mindenesetre jegyezzük meg, hogy a portlábak viselkedését mellékhatásként más perifériák vezérlő regiszterei is befolyásolják! Hogy pontosan melyek, azt a mikrovezérlő adatlapjának Application Information című fejezetének táblázatai írják le részletesen.
P1DIR - a P1 port kivezetésein az adatáramlási irányát szabja meg, amelyet bitenként beállíthatunk. Ha valamelyik bitet '0'-ba állítjuk, akkor a hozzá tartozó kivezetés bemenet lesz (bekapcsoláskor egyébként ez az alapértelmezett beállás), ha pedig '1'-be állítjuk, akkor kimenetként viselkedik. Összefoglalva tehát 0: bemeneti, 1: kimeneti irány. Mindez csak akkor igaz, ha a kivezetés digitális ki/bemenetnek van konfigurálva (azaz P1SEL megfelelő bitje '0'), ami a P1 portnál alapértelmezett beállás, de a P2 portnál nem.
P1SEL - a P1 port kivezetéseinek funkcióját választhatjuk ki vel: 0: digitális I/O (a P1 portnál bekapcsolás után ez az alapértelmezett beállás), 1: alternatív funkció. Az alternatív funkció választásakor (P1SEL.x = 1) további regiszterek beállítása kellhet ahhoz, hogy több alternatív funkció közül válasszunk. A P2 port esetében P2.6 és P2.7 alapértelmezetten az alternatív órajelgenerátor kivezetéséeiként működnek (ide forraszthatjuk be a kitben kapott 32 kHz-es kvarckristályt). A digitális I/O funkció beállításához nullázzuk P2SEL bitjeit (pl. P2SEL = 0)!
P1REN - ha valamelyik bitjét '1'-be állítjuk, akkor a P1 port megfelelő bemenetén engedélyezzük a beépített fel- vagy lehúzó ellenállást. Azt a P1OUT regiszter megfelelő bitje mondja meg, hogy a fel- vagy lehúzás legyen érvényben (1: felhúzás Vcc-re, 0: lehúzás Vss-re).
Figyelem: a fel- vagy lehúzást csak digitális bemenetnek konfigurált (P1SEL.x=0, P1DIR.x=0) kivezetésekre szabad engedélyezni!
A P1 port további regiszterei a megszakításokkal kapcsolatosak. Engedélyezhetjük, hogy külső események hatására a port valamelyik bemenete programmegszakítást kérjen (ha P1IE megfelelő bitje '1'). Megadhatjuk, hogy a megszakítási kérelem fel- vagy lefutó élre keletkezzen (ha P1IES megfelelő bitje '0', akkor felfutó élre, ha pedig '1', akkor lefutó élre keletkezik megszakítás). A megszakítási kérelem jelzőbitjét a P1IFG regiszter megfelelő bitjében találjuk meg (1: programmegszakítási kérelem jelzése, 0: nincs megszakítási kérelem).
A P2 port is hasonló regiszterkészlettel rendelkezik, mint a P1 port, ezért a fent leírtak értelemszerűen a P2 portra is vonatkoznak.
Első kísérlet: 8 db LED villog egyszerre
Kapcsolgassuk a P1 port összes lábát egyszerre! Nem árt, ha lehúzzuk a Txd és Rxd átkötéseket. Ha van 8 db LED kéznél, akkor kössünk a P1.0...P1.7 kimentek mindegyikére egy-egy LED-et egy áramkorlátozó ellenálláson keresztül, s húzzuk le LED1 és LED2 átkötését is! Ha nincs LED-ünk, akkor pedig hagyjuk fenn LED1 és LED2 átkötését, s csak ezt a két LED-et fogjuk villogtatni.
Hozzunk létre egy új projektet a 3. oldalon leírtak szerint! A projekt neve legyen ledflash_all. A projekt opciók beállítása után írjuk be az alábbi programot:
- #include "io430.h"
- #include "intrinsics.h"
- void main(void) {
- WDTCTL = WDTPW + WDTHOLD; //Letiltjuk a watchdog időzítőt
- P1DIR = 0xFF; //P1 minden bitje legyen digitális kimenet
- while(1) {
- P1OUT = ~P1OUT; //P1 bitjeit komplementáljuk
- __delay_cycles(500000); //Várakozás kb. 500 ms ideig
- }
- }
Mi a különbség az első LED villogtató programunkhoz képest? Az egyik különbség az, hogy P1DIR minden bitjét '1'-be állítjuk. A második különbség az, hogy a végtelen ciklusban P1OUT bitjeit egyszerre komplementáljuk. Ha a bitek 0-ban álltak, akkor '1'-be állítódnak és viszont.
Megjegyzés: a programban nem foglalkoztunk P1SEL beállításával, kihasználva, hogy bekapcsoláskor úgyis nullázódik. Ugyancsak nem foglalkoztunk P1OUT kezdőértékének beállításával, feltételezve, hogy bekapcsoláskor úgyis nullázódik.
Második kísérlet: 8 db LED villog, páronként ellenütemben
Kapcsolgassuk felváltva a P1 port összes kimenetét! Vegyük észre, hogy az előző program majdnem jó, hiszen a bitenkénti komplementálásnál nem foglalkozunk vele, hogy mi volt a bit előző állapota. Így elegendő csupán a kezdőfeltétel jól beállítani a főprogram inicializáló részében. Ha az alábbi sort szúrjuk be közvetlenül a P1DIR beállítása elé vagy után, akkor a páros és a páratlan sorszámú bitek felváltva (ellenütemben) villognak.
- P1OUT = 0x55;
Ha a 0x55 hexadecimális számot binárisan felírjuk (0b01010101), akkor rögtön látható, hogy miért pont ezt az értéket adtuk meg. Hasonlóan jó, csak ellenkező fázisú kezdőérték volna 0xAA (binárisan 0b10101010).
Ha a kísérlethez nem csatlakoztattunk külön LED-eket a Launchpad kártyához, hanem csak a gyárilag beültetett LED-eket használjuk, akkor a fenti beállítás helyett érdekesebb lesz az alábbi sor beszúrása:
- P1OUT = 0x0F;
A bináris alakból (0b00001111) látható, hogy ekkor a P1.0...P1.3 és a P1.4...P1.7 bitcsoportok villognak felváltva, tehát a piros és a zöld LED is ellenütemben villognak. A korábbi beállításban viszont azonos fázisban villognának, mivel LED1 (P1.0) és LED2 (P1.6) is páros sorszámú bitre vannak kötve.
Harmadik kísérlet: Knight Rider villogót készítünk
A P1 portra kötött 8 db LED villogtatásával Knight Rider sorozat autójának pásztázó fényjátékát utánozzuk. Egyidejűleg legfeljebb 3 db LED világít, de a széleken "kiúsztatjuk" a bitmintát, egészen addig, hogy csak a legszélső LED világítson. Bonyolult matematikai műveletek helyett egyszerűen minden bitmintát megadtunk egy konstans tömbben. Ezeket felváltva, előbb növekvő, majd csökkenő) sorrendben kell lejátszanunk, ezért két ciklust szervezünk. A növekvő ciklust egyszerűen for ciklusként adtuk meg. A csökkenő ciklusnál a leállás feltételét egyszerűbb volt egy while ciklusban megadni, ám ekkor az i változó kezdőértékének megadásáról és a léptetéséről nekünk kell gondoskodni.
- #include "io430.h"
- #include "intrinsics.h"
- const char a[]={0x01,0x03,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xC0,0x80};
- char i;
- void main(void) {
- WDTCTL = WDTPW + WDTHOLD; //Letiltjuk a watchdog időzítőt
- P1DIR = 0xFF; //P1 minden bitje legyen digitális kimenet
- while(1) {
- for(i=0; i<10; i++) {
- P1OUT = a[i]; //bitminta kirakása a P1 portra
- __delay_cycles(50000); //Várakozás kb. 50 ms ideig
- }
- i=10;
- while(i) { //a ciklus addig fut, amíg i!=0 teljesül
- i--; //ciklusváltozó léptetése
- P1OUT = a[i]; //bitminta kirakása a P1 portra
- __delay_cycles(50000); //Várakozás kb. 50 ms ideig
- }
- }
- }
Megjegyzések:
- Az i = 10 értékadás esetleg feleslegesnek tűnhet de nem illik arra számítani, hogy a for ciklus lejártával az i ciklusváltozó értéke megőrződik!
- A csökkenő sorozatnál a ciklusváltozó léptetése és a kimenet beállítása összevonható, de ügyeljünk arra, hogy a léptetésnek meg kell előznie az értékadást! Tehát az i--; sor elhagyása esetén az értékadást így írjuk
- P1OUT = a[--i];
Negyedik kísérlet: LED vezérlése nyomógombbal
Eddig csak kimenetként használtuk a P1 portot. Nézzünk most egy olyan példát, amiben bemenetet is kezelünk! A mikrovezérlőkhöz köthető legegyszerűbb bemeneti eszközök a nyomógomb, vagy a kapcsoló. A Launchpad kártya a RESET gombon kívül tartalmaz egy másik nyomógombot is, tehát kézenfekvő, hogy következő kísérletünkhöz ezt használjuk bemeneti perifériaként.
Írjunk programot, ami az S2 nyomógomb segítségével ki/be kapcsolgatja a LED1 fényemittáló diódát (első benyomáskor bekapcsolja a LED-et, a második megnyomásra pedig kikapcsolja)! A program megtervezésénél gondolnunk kell a nyomógomb pergésmentesítésére is!
A nyomógomb pergésmentesítését úgy oldjuk meg, hogy a nyomógomb lenyomása és felengedése után is várunk addig, amíg a kontaktus pergésének folyamata befejeződik (~ 20 ms). A várakozáshoz a ledvillogtatásnál már alkalmazott gyári késleltető eljárást használjuk, amivel az alapértelmezett 1 MHz-es CPU frekvencia esetén 1 µs egységekben adjuk meg a késleltetési időt.
- #include "io430.h"
- #include "intrinsics.h"
- void main(void) {
- WDTCTL = WDTPW + WDTHOLD; //Letiltjuk a watchdog időzítőt
- P1DIR |= BIT0; //P1.0 legyen digitális kimenet
- P1DIR &= ~BIT3; //P1.3 legyen digitális bemenet
- while(1) {
- while(P1IN & BIT3); //lenyomásra vár
- P1OUT ^= BIT0; //LED1 állapotának átbillentése
- __delay_cycles(20000); //20 ms pergésmentesítő késleltetés
- while(!(P1IN & BIT3)); //felengedésre vár
- __delay_cycles(20000); //20 ms pergésmentesítő késleltetés
- }
- }
A főprogram inicializáló részében gondoskodnunk kell róla, hogy P1.0 kimenet (P1DIR 0. bitje 1) legyen, P1.3 pedig bemenet (P1DIR 3. bitje 0) legyen. Bár bekapcsoláskor P1DIR minden bitje nullázódik, ez az alapértelmezett beállás, a programban mégis újra beállítottuk, hogy megmutassuk, hogyan nullázhatunk bitet vagy bitcsoportot a bitenkénti ÉS logikai művelet segítségéven, melynek a C nyelvben '&' a jele. A korábbiakhoz hasonlóan a '&=' azt jelenti, hogy a művelet egyik operandusa és az eredmény eltárolási helye megegyezik, tehát így is írhattuk volna:
- P1DIR = P1DIR & ~BIT3;
A BIT3 szimbólumot az io430.h header állomány definiálja, s a 8 értéket rendeli hozzá (binárisan felírva: 0b00001000). Ezt az értéket itt komplementálnunk kellett, hogy ott álljon nulla, ahol törölni akarjuk a bitet. A későbbiekben még sokszor lehet szükségünk több bit együttes törlésére, amit például így végezhetünk el:
- P1DIR &= ~(BIT3 + BIT5 + BIT);
A programban a (P1IN & BIT3) kifejezés a P1.3 bemenet állapotát vizsgálja, ami '1', azaz magas szint, ha az S2 nyomógomb felengedett állapotban van, s '0', azaz alacsony szint lesz, ha S2 lenyomott állapotban van. A bemenet felhúzásáról az R34 (47 kOhm) ellenállás gondoskodik, így a belső felhúzást nem kellett bekapcsolnunk.
A while(P1IN & BIT3); utasítás egy olyan ciklust jelent, amelyiknek üres a ciklusmagja ("nem csinál semmi"t), de addig cirkulál, amíg a feltétele igaz (S2 felengedett állapotban van). A program tehát csak akkor lép a következő utasításra, amikor az S2 gombot lenyomjuk.
Hasonlóan figyeljük a gomb felengedését, a while(!(P1IN & BIT3)); ciklussal, ami csupán annyiban különbözik az előzőtől, hogy felkiáltójellel negáltuk (ellenkezőjére változtattuk) a logikai feltételt. A program tehát mindaddig ebben a ciklusban cirkulál, amíg az S2 gomb lenyomott állapotban van, s csak akkor lép tovább, amikor a gombot felengedjük.
A cikk még nem ért véget, lapozz!