Categories
AVR-ek programozása

Az AVR portjainak írása és olvasása. (Kimenetek, bemenetek)

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.

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöljük.