Fórum témák

» Több friss téma
Cikkek » WILL-I - Robot, AVR mikrovezérlővel
WILL-I - Robot, AVR mikrovezérlővel
Szerző: Fizikus, idő: Dec 1, 2009, Olvasva: 60817, Oldal olvasási idő: kb. 7 perc
Lapozás: OK   6 / 12

AVR programozás

A végső lépés a robotunk programozása, de mielőtt ezt elkezdhetnénk, telepítenünk kell az AVR Studio 4 és a WinAVR programokat. A WinAVR a telepítés során automatikusan beleintegrálja magát az AVR-Studióba és ezután már C-nyelven is programozhatjuk a mikrovezérlőnket. A programozást nagyon megkönnyíti, ha feltelepítjük és használjuk az avr-libc, AVR C program könyvtárat, ami egy csomó hasznos kódot tartalmaz. A WinAVR C-nyelvű fejlesztőkörnyezet telepítéséről és használatáról szóló cikk: (WinAVR telepítése és használata

A programozás menete :

  • A program megírása
  • A program lefordítása
  • A lefordított program mikrokontrollerbe való töltése (égetés)

Ezekre a lépésekre nem térek ki részletesen, mindenki olvassa el, a program lefordításáról és a programozás szoftveres részéről szóló alábbi cikkeket:

Mielőtt elkezdenénk programozni a mikrovezérlőnket, részletesebben meg kell ismerkednünk a működésével.

 

A mikrokontroller működése/felépítése

A mikrovezérlő a külső világhoz az ún. PORT-okon keresztül kapcsolódik. A PORT-ok regiszterekbe vannak szervezve, és rendszerint külső kivezetésekhez (lábakhoz) vannak kötve. A mikrokontroller lábainak többsége egy PORT-hoz tartozó kivezetés, de egyes lábaknak speciális feladata van (tápellátás, külső oszcillátor, stb.). A portok elnevezése PORTA, PORTB, PORTC, PORTD stb..Egy port egy 8 bites regiszter (8 láb alkot egy PORT-ot), aminek 0 és 255 között bármilyen értéket adhatunk.

A PORT-ok kivezetéseihez tartozó lábak 3 módban működhetnek:

  • digitális kimeneti módban az AVR-en futó programmal kapcsolhatóak a logikai 1-nek megfelelő magas (5V) szint és a logikai 0-nak megfelelő alacsony (0V-föld) feszültségszint között.
  • digitális bemeneti módban a lábakon lévő feszültségszint leolvasható (csak logikai 0 és 1 állapot lehet). Ha a feszültség egy határértéknél nagyobb (ez rendszerint tápfeszültség fele), akkor a leolvasott érték logikai 1, különben logikai 0.
  • egyes speciális lábak lehetnek analóg bemenetek is. Ebben a módban a lábakon lévő analóg feszültségszintet (0 és 5V közötti bármilyen érték lehet) alakítja át a mikrokontroller egy 0 és 1024 közötti értékké.

Az AVR-ezés lényege, hogy a bemeneti lábak állapotát lekérdezzük, és ettől függően a kimeneti lábakra 5V-ot vagy 0V-ot adunk. Így vezérelve mindent a mikrokontrollerrel.

A mikrokontrollerek C programozása közben, a legtöbb esetben a portokhoz kapcsolódó regiszterekkel kell foglalkozni. A legtöbbször előforduló feladat egy regiszteren belüli adott bit értékének megváltoztatása, és annak lekérdezése, hogy egy regiszter adott bitje 0 vagy 1 logikai állapotú-e. Az alábbiakban ezeket mutatom be részletesen.

 

A Regiszterek

A regiszter egyszerűen 8 bit együttese (az ATMega8 8 bites mikrokontrollernél). A regiszter a CPU és a perifériák közötti kapcsolatot látja el. Általában minden egyes regiszteren belüli bitnek külön feladata van (pl. amint azt kesőbb majd látni fogjuk, a PORTD regiszter bitjeihez más más láb tartozik). Olyan eset is van, ahol a regiszter mint egység, tárolhat egy 0-255 közötti értéket (pl. a komparátor output compare regisztere). A regiszterek értékének változtatásával tudja a CPU vezérelni a perifériákat (beállíthatja a működési módokat, vagy utasíthatja a perifériát hogy egy adott műveletet hajtson végre). A regiszter értékének beolvasásával a CPU megállapíthatja egy periféria állapotát vagy a perifériával kapcsolatos adatot. 

 

Amikor egy regiszter (példánkban hívjuk mondjuk REGISZ-nek) bitjeit szeretnénk beállitani egy meghatározott bitmintázatra (pl. bináris 10010111-re), akkor nem írhatjuk azt hogy REGISZ=01100111, mert a fordítónk a 01100111-et tízes számrendszerbeli számnak fogja értelmezni. Ahhoz hogy C-ben bináris számot adjunk meg, a szám elé egy 0b előtagot kell tennünk. Szóval ha azt írjuk hogy:

REGISZ=0b01100111;

Akkor a 01100111 bitmintázatot rendeli a REGISZ regiszter bitjeihez.

A fentiekhez hasonlóan, ha a számot a 0x előtaggal látjuk el, akkor a fordító mint hexadecimális számot fogja értelmezni.

 

Bit értékadás egy regiszteren belül

A célunk az hogy egy adott regiszteren belül egy meghatározott bit (mondjuk az 5. bit) értékét logikai 1-re állítsuk, a többi bit értékét viszont változatlanul hagyjuk. Ehhez a logikai VAGY függvényt (OR, | jel) kell használnunk (most nem ismertetem a logikai függvényeket és igazságtáblájukat, mindenki nézzen utána, aki esetleg nem ismeri).

REGISZ=REGISZ | 0b00100000;

Vagy rövidebben így is írhatjuk:

REGISZ|=0b00100000;

A fenti utasítás az 5. bitet 1-re állítja, a többit változatlanul hagyja, azáltal hogy a REGISZ regiszter minden egyes bitjét VAGY-olja a 0b00100000 byte megfelelő bitjével és a kapott értéket újra eltárolja a REGISZ-be. Ezesetben tehát, ha nem volt 1-es a megfelelő bit, akkor most egyes lesz a többi bit értéke nem változik. Nézzük meg 2 példán keresztül

REGISZ       10011010
VAGY       | 00100000
                 10111010        5. Bit értékének 1 íródott, többi bit értéke nem változott

 

REGISZ        01110101
VAGY        | 00100000
                  01110101        5. Bit értéke továbbra is 1, többi bit értéke nem változott

A gyakorlatban minden egyes bitnek saját neve van ami a feladatára/funkciójára utal. Példánkban a BIT-ünk (az 5. bit) neve legyen mondjuk ENGED, és ha értékének 1-et adunk, akkor engedélyez (bekapcsol) egy adott perifériát, ha pedig töröljük (értékének 0-át adunk), akkor a perifériát letíltja (kikapcsolja).

REGISZ |= _BV(ENGED) ;

A _BV egy fordító makró, amely az angol Bit Value (Bit Érték) rövidítése és egy olyan bájtot állít elő, amelyben csak az adott bitnek megfelelő pozícióban van a bájton belül 1-es, a többi bit értéke pedig 0. Gyakran találkozhatunk az alábbi utasítással is, ami teljesen egyenérétkű az előzővel

REGISZ |= (1<<ENGED);

A << neve left shift (balra tolás ) operátor. A kifejezés bal oldalán lévő változó bit-jeit a kifejezés jobb oldalán álló értékkel balra tolja.

b=1<<5;

ekkor az 1-est, aminek a bináris értéke 00000001 5 hellyel balra tolja és az eredmény 00100000 lesz. Szóval ha az ENGED értéke 5-nek van definiálva az io.h fájlban (ENGED az 5. bit) akkor a

REGISZ | = (1<<ENGED);

Parancs az alábbi utasítással egyenértékű:

REGISZ | = (1<<5);

Ami a fentiek értelmében így is írható:

REGISZ | = (0b00100000);

Megpróbálom egy kicsit áttekinthetőbben ábrázolni az alábbi képen:

Kezdőként megkérdezhetnénk, miért is írhatjuk le ugyanazt az utasítást többféle paranccsal?

  1. Programkód olvashatósága: a REGISZ |= _BV(ENGED) és a REGISZ | = (1<<ENGED) parancsok utalnak arra hogy engedélyezünk egy perifériát, míg a REGISZ|=0b00100000 parancs egyáltalán nem utal arra, hogy mit is csinál valójában. Az adatlapot kell végigbújnunk hogy rájöjjünk hogy a REGISZ bájt 5. bitje minek a beállítására szolgál. Valójában ENGED egy konstans, melynek az io.h fájlban már előre definiálva van a szám megfelelője.
  2. Könnyű program hordozhatóság: Tegyük fel hogy kinőttük az ATmega8 adta lehetőségeinket (a robotunkra már olyan sok érzékelőt raktunk, és olyan bonyolult algoritmusokat szeretnénk leprogramozni, hogy ahhoz már komolyabb tudású AVR kell, pl. egy ATmega32). Ha a fenti utasítást sokat használjuk az ATmega8-ra írt programunkban és azt szeretnénk hogy a fenti programunk fusson egy másik AVR-en. Az új mikrokontroller hasonló az előzőhöz, de már nem 28 lába van hanem 40, ezért szükségszerűen kissé különböző a lábkiosztása és a hozzá tartozó portok és bitek kiosztása. Mondjuk az ENGED a 2. bit az 5. bit helyett. Ebben az esetben a programban minden egyes REGISZ|=(0b00100000) utasítást meg kell keresnünk és ki kell javítanunk a REGISZ|=(0b00000100) utasításra. De ha a REGISZ |= _BV(ENGED) utasítást használjuk, akkor egyszerűen csak meg kell adni a fordítónak hogy egy másik mikrokontrollert használunk és a fordító automatikusan betölti az új AVR-re vonatkozó definíciókat és beállításokat, ajhol az io.h fájlban az új AVR-hez tartozó beállításoknál már az ENGED=2 definíció szerepel.

 

Bit törlés egy regiszteren belül

Egy regiszteren belüli adott bit értékének törléséhez a logikai ÉS függvényt használjuk (AND, & jel).

REGISZ & = ~ _BV(ENGED);

            vagy

REGISZ & = ~ (1<<ENGED);

Ez az utasítás törli (a bit értékét 0-ra állítja) az adott (ENGED által definiált) bit-et a REGISZ regiszterben. annélkül hogy a byte többi bitjeinek az értékét megváltoztatná. Így a REGISZ byte jelenlegi érétékétől függetlenül, csak az általunk törölt bit értéke változik. Nézzük meg lépésekre lebontva hogyan is működik ez az utasítás:

A REGISZ & = ~_BV(ENGED) parancs valójában a REGISZ & = ~ 0b00100000 utasítással egyenértékű. A ~ jel a negálás bitműveletet jelöli, aminek 0b1101111 az eredménye. Így ezzel az utasítással az 5. pozícióban lévő bit (ENGED) mindíg törölve lesz (érétke 0 lesz), a többi bit viszont változatlan marad.

Mikrokontrollerek programozása során sokszor nem érdekel minket egy adott regiszter értéke, hanem csak egyes bitek állapotára vagyunk kiváncsiak.

 

Bit állapotának lekérdezése.

Eddig módosítottuk egy adott regiszteren belül kiválasztott bitet. Most azt vizsgáljuk meg hogyan tudhatjuk meg, hogy egy adott bit értéke vajon 0 vagy 1. Tegyük fel hogy arra vagyunk kíváncsiak, hogy a perifériánk engedélyezve van-e (be van-e kapcsolva) vagy esetleg kikapcsolt állapotban van-e. Ha REGISZ regiszter 5. bitje 1, akkor be van kapcsolva, ha pedig 0, akkor ki van kapcsolva. Ahhoz, hogy a regiszter 5. bitjét lekérdezzük, a regisztert logikai ÉS-elnünk kell egy ÉS MASZK regiszterrel. Ekkor az ÉS MASZK regiszter az alábbi lesz: 0b00100000. Ha ezt az értéket bitenként logikai ÉS-eljük a REGISZ regiszter jelenlegi értékével, akkor ez csak akkor ad nem nulla eredményt ha a REGISZ regiszter 5. bitje 1-es, különben az eredmény 0 lesz.

if (REGISZ & (1<<ENGED))
{
        //ENGED értéke 1 a REGISZ regiszteren belül
        ...
}
else
{
        // ENGED értéke 0 a REGISZ regiszteren belül
}

 

 

Portokhoz kapcsolódó Regiszterek

Az előbbiekben használt REGISZ regiszter és ENGED periféria kitalált, nem valós, csak a példánkban szerepel.

Most nézzük meg az ATMega8 valós portjait és a portokhoz kapcsolódó regisztereit.

Az ATMega8 lábkiosztását megnézve láthatjuk, hogy a lábai 3 portba vannak rendezve: PORTB, PORTC, PORTD. Minden porthoz 3 főbb regiszter tartozik amivel beállíthatjuk/vezérelhetjük. Példaként nézzük a PORTD beállításához kapcsolódó regisztereket:

 

DDRD: ez a D port adat irány regisztere (Data Direction Register for PORTD). Ez a regiszter határozza meg hogy a D port lábai közül melyik legyen digitális bemenet és melyik digitális kimenet. A port lábait bemenetnek kell megadni, ha adatot akarunk róluk beolvasni és kimenetnek, ha adatot akarunk kiküldeni/vezérelni akarunk valamit (pl. LED villogtatás, motorvezérlés). Egy láb kimenetként való definiálásához a hozzá tartozó bit értéket 1-nek, bemenetként való megadásához 0-nak kell megadni. Alapbeállításban a D port minden lába bemenet, a DDRD regiszter minden bitje 0 (0x00 vagy 0b00000000).

PORTD: Ezzel a regiszterrel kontrollálhatjuk az adott porthoz tartozó lábak állapotát. Ha egy láb bemenetnek van megadva és a lábhoz tartozó bit értéke 1, akkor bekapcsolja a lábhoz tartozó belső felhúzó ellenállást. Ha egy láb kimenetnek van megadva akkor pedig 5V-ot vagy 0V-ot kapcsol a lábra attól függően hogy a lábhoz tartozó bit értéke 1 vagy 0. Az I/O lábak alapértelmezettként bemenetnek vannak definiálva.

PIND: Ha egy porthoz tartozó lábat bemenetként adtunk meg, akkor ezzel a regiszterrel tudjuk olvasni a portról (lekérdezni az állapotát).

Mivel a portok alapértelmezettként bemenetek, nem kell előzetesen beállítanunk semmit. Például a PD4 lábra kapcsolt gomb állapotát egyszerűen a PIND regiszter beolvasásával lekérdezhetjük.         

If (PIND & _BV(PD4))
            {
                        .....       // gomb nincs nyomva
            }
            else
            {
                        .....       // gomb nyomva
            }

Most már tudjuk az alap bitműveleteket, amikre legtöbbször szükségünk lehet, és megismerkedtünk a portokhoz kapcsolódó regiszterekkel is, itt az ideje, hogy végre megírjuk az első programunkat, megnézzük hogyan kell a fentieket alkalmazni a valóságban és ideje letesztelnünk, hogy a vezérlőpanelünk hibamentesen működik-e.

Az elektronikát tárgyaló fejezetben lévő ábrának megfelelően csatlakoztassunk egy LED-et a PC0-ás lábra, és egy kapcsolót a PD4-es lábra. A beépített belső felhúzóellenállásokat fogjuk használni, ezért ne kössük a lábat az 5V-ra külső ellenálláson keresztül. 

Miután rákapcsoltuk a 9V-os elem megfelelő pólusait a vezérlőpanel megfelelő érintkezőire, már programozhatjuk is az AVR-ünket.


A cikk még nem ért véget, lapozz!
Következő: »»   6 / 12
Értékeléshez bejelentkezés szükséges!
Bejelentkezés

Belépés

Hirdetés
Lapoda.hu     XDT.hu     HEStore.hu
Az oldalon sütiket használunk a helyes működéshez. Bővebb információt az adatvédelmi szabályzatban olvashatsz. Megértettem