Az előző cikkben megnéztük, hogy néz ki egy egyszerű AVR-re írt program. Most egy kicsit tovább megyünk, és megnézzük hogy kell használni a kontrollerünk “lábait”.
Egy AVR- kontrollernek sokféle regisztere van. A legtöbb közülük írható/olvasható regiszter. (Read/Write) Ez azt jelenti, hogy a program a regiszterek tartalmát írni, és olvasni is képes. 
Az AVR- kontrollerekben az I/O-Regisztereknek fontos szerepük van: Ezek nyújtanak hozzáférést a kontroller portjaihoz, és perifériáihoz. Az AVR-eknél 8 és 16 bit-es regisztereket különböztetünk meg . Egyelőre csak a 8 bit-es regisztereket vesszük szemügyre.
Néhány regiszter minden AVR-ben rendelkezésre áll, néhány viszont csak némelyikben fordul elő. Ilyenek például azok a regiszterek, amik az UART hozzáféréséhez szükségesek azokban a kontrollerekben amikben persze van ilyen.
A regiszterek nevei az adott AVR típusának megfelelő Header-file-jában vannak definiálva. Ha a Makefile- ban a MCU típusa definiálva van, a fordító automatikusan betölti a helyes header-t, de az alkalmazott kontroller típusát akár a C – forrásfájlban is definiálhatod, ha éppen örömöd leled benne.
A regiszterek tartalmát ugyanúgy írjuk, mintha egyszerű változók lennének. Azok a forráskódok, amik az avr-libc korábbi verzióihoz készültek, és például az outp() függvényt használják a regiszterek írására, a jelenlegi verziókban már nem támogatottak, így nem lehetséges az outp() függvény általi elérésük. Őszintén bevallom, én nem is találkoztam még ezekkel a függvényekkel, de jó ha megjegyezzük hogy ilyen is van.
Itt egy példa a regiszterekbe való íráshoz:
#include <avr/io.h> int main() { /* A PORTA (A jelű port) adairányregiszterének 0xFF értéket ad (az A jelű porthoz tartozó minden kapcsot kimenetként deklarálunk) */ DDRA = 0xFF; /* A PORTA értékét 0x03 állítja, Bit 0, és Bit 1 logikai 1 értéket kap (HIGH) a többi logikai 0 szinten marad. */ PORTA = 0x03; // a 0,1,2,3 és 4 –ik bitek értékadása // bináris formátumban: 0b00011111 Hexadecimális: 1F DDRB = 0x1F; /* közvetlen hozzáférés – átláthatatlan */ /* Részletes írásmód: Azonosítható működés, bár többet kell gépelni, de átláthatóbb, és magától értetődő: */ DDRB = (1 << DDB0) | (1 << DDB1) | (1 << DDB2) | (1 << DDB3) | (1 << DDB4); }
Előnyösebb az átlátható írásmód, mivel a hozzárendelések magától értetődőek, és a kód ezáltal könnyebben nyomokövethető. Ezt a formátumot alkalmazza az ATMEL is az adatlapjaiban, és a példa kódjaiban.
A gcc C-Compiler a 4.3.0 verziótól a konstansokat bináris formátumban is támogatja, például DDRB = 0b00011111, de nem került bele a szabványba, tehát nem célszerű használni, ha a hordozhatóság is szempont, mivel más fordítók nem biztos hogy támogatják ezt a formátumot.
Egy regiszter bitjeihez a C szabvány logikai műveletei útján rendelhető hozzá érték, vagy törölhető az.
// Értékadás egy meghatározott bitnek x |= (1 << Bit_helyierteke); // Egy meghatározott bit értékéne törlése x &= ~(1 << Bit_helyierteke);
A fenti parancsok alkalmazásával minden esetben csak a megadott bit értéke változik, a többi bit állapota érintetlen marad.
Gyakorlati példa:
#include <avr/io.h> #define Bit2 2 // A kettes helyiértékű bit értéke (3. bit) 1 lesz PORTA |= (1 << Bit2); // A kettes helyiértékű bit értékét törli (3. bit), 0 lesz PORTA &= ~(1 << Bit2);
Ezzel a módszerrel akár egy regiszter egyszerre több bitjének is adhatunk értéket, vagy törölhetjük azok értékeit:
#include <avr/io.h> DDRA &= ~(1 << PA0) | (1 << PA3); // PA0 és PA3 bemenet lesz PORTA &= ~(1 << PA0) | (1 << PA3); // Felhúzó ellenállások bekapcsolása
A bitek egyenkénti értékadása, és törlése azokban a korábbi forráskódokban, amik az avr-libc régebbi verzióihoz készültek, az sbi, és cbi függvényekkel valósultak meg, ám ezek a legfrissebb verziókban már nem elérhetőek.
Regiszterek olvasása
Egy regiszter olvasásakor ahhoz ugyanúgy férünk hozzá , mintha azok változók lennének. A regiszterek tartalmát a korábbi forráskódokban, amik az avr-libc régebbi verziójára íródtak, az inp() függvénnyel olvasták ki. A frissebb verziókban ennek a függvénynek a használata nem lehetséges.
Lássunk egy példát:
#include <avr/io.h> #include <stdint.h> uint8_t x; int main() { // a fent deklarált változóba másolja // a PORTB bemeneteinek állapotát x = PINB; }
A bitek állapotának lekérdezése a teljes regiszter tartalmának beolvasását vonja maga után, és azoknak a biteknek a tartalmát elrejti ami számunkra nem érdekesek. Íme néhány példa, annak a vizsgálatához, hogy egy adott bitnek van – e értéke, vagy a bit törölve van – e:
#define Bit0 0 #define Bit2 2 uint8_t i; extern test1(); extern test2(); // test1 hívása ha a PINA regiszter 0.-dik bitjének van értéke (1) i = PINA; // A regiszter tartalmát a változóba írjuk i = i & 0x01; // A nulladik biten kívül mindent figyelmen kívül hagy // ha a 0.-dik bitnek volt értéke, i értéke 1 lesz if (i != 0) test1(); // Rövidebben a fenti: if ((PINA & 0x01) != 0) test1(); // még egyszerűbben: if (PINA & 0x01) test1(); // előre definiált bitszámmal: if (PINA & ((1 << Bit0))) test1(); // Függvény hívása, ha Bit0-nak, vagy Bit-2nek értéke van // Emlékezetül: Bit0 értéke 1, Bit1 értéke 2, Bit2 értéke 4 //tehát 1+4=5 = 0x05 = 0b00000101 if (PINA & 0x05) test1(); // ha legalább az egyik bitnek van értéke, a feltétel igaz // definiált bitszámokkal: if (PINA & (( 1 << Bit0) | (1 << Bit2))) test1(); // a test2() függvény hívása ha Bit0 vagy Bit2 törölve van (0) i = PINA; // aktuális állapot átmeneti változóba írása i = i & 0x01; // A Bit0 maszkolása if (i == 0) test2(); // a feltétel igaz, ha Bit0 törölve van. // tagadás ! operátorral (nem) if (i!) test2(); // lerövidítve: if (!(PINA & 0x01)) test2();
Az avr-libc-ben vannak olyan függvények (igazából makrók) is, amik egy regiszter megadott bitjét kérdezik le. Más fordítókban ezek nem elérhetők, de makrók használatával könnyen után lehet őket “gyártani“.
bit_is_set( Regiszter , Bitszam );
A bit_is_set függvény azt vizsgálja, hogy egy adott regiszter, adott bitjének van e érték adva. Ha a bithez van érték hozzárendelve, akkor az általa visszaadott érték 0-val egyenlőtlen lesz. Pontosabban a visszaadott érték a lekérdezett bit helyiértéke lesz
bit_is_clear( Regiszter , Bitszam );
A bit_is_clear függvény megvizsgálja, hogy egy megadott regiszter, adott bitje törölve van e, tehát ha az értéke 0, akkor egy 0-val egyenlőtlen érték lesz a visszatérési érték.
Várakozás egy meghatározott állapotra
Az avr-libc tartalmaz olyan függvényeket, amik addig várnak, amíg egy bit nem ér el egy meghatározott állapotot. A program futásának ilyen módon való blokkolása nem egy szép programozási módszer. A vezérlés ezen a ponton megáll, és addig itt is marad, amíg a maszkolt esemény be nem következik. Ha bevetjük a Watchdog-ot, figyelni kell arra, hogy ez is triggerelve legyen. (Watchdogtimer visszaállítása)
A loop_until_bit_is_set addig vár egy ciklusban, amíg a definiált bitnek van értéke. Ha a függvény hívásakor a bitnek már van értéke, a függvény azonnal visszatér. A legalacsonyabb bit helyiértéke a 0.
#include <avr/io.h> /* Vár amíg a PINA regiszterben a Bit2-nek (a harmadik bit) van értéke) */ #define WaitPIN PINA #define WaitBIT PA2 // avr-libc függvénnyel: loop_util_bit_is_set(WaitPIN, WaitBIT); // ugyanez Standard C-ben: //az üres ciklus addig fut, míg a WaitBIT 0-val nem egyenlőtlen while (!(WaitPIN & (1 << WaitBIT)))
loop_until_bit_is_clear addig vár egy ciklusban, amíg a definiált bit törölve van, nincs értéke. Ha a függvény hívásakor éppen nincs értéke, a függvény azonnal visszatér. A legalacsonyabb bit helyiértéke a 0.
#include <avr/io.h> /* Vár amíg a PINB regiszterben a Bit4-nek (az ötödik bit) nincs értéke */ #define WaitPIN PINB #define WaitBIT PB4 // avr-libc függvénnyel: loop_util_bit_is_clear(WaitPIN, WaitBIT); // ugyanez Standard C-ben: //az üres ciklus addig fut, míg a WaitBIT értéke 1 nem lesz while (WaitPIN & (1 << WaitBIT))
Elérkeztünk ennek a résznek a végére is. Most már tudtok akár kísérletezni is a megismert példák segítségével. A következő részben egy kicsit tovább megyünk, de még mindig a regisztereknél maradunk.