AVR Bit Magic

Z SensorWiki
Prejsť na: navigácia, hľadanie

V assembleri existuje pre manipuláciu s bitmi dostatok inštrukcií. To v C-čku štandardne nie je, preto sa musíme naučiť s bitmi manipulovať.


Pozn.: Keď budete skúšať jednotlivé spôsoby, pozrite sa aj na to, akým spôsobom kompilátor danú konštrukciu preloží.


Vstupy

V prvom cvičení sme napríklad na test stlačenia tlačidla mohli použiť inštrukciu

 sbis PIND,3		; sbis = Skip if Bit Is Set,  preskoc ak je PIND.3 nastaveny (=1)

V jazyku C ale typ bit neexistuje (hoci niektoré kompilátory takýto typ zaviedli), preto môžeme pracovať len s bajtmi, a musíme si nejako vypomôcť. Pri rozumnom zápise však kompilátor rozpozná, že chceme pracovať s bitom a preloží našu konštrukciu správne.


Ak chceme testovať stav jedného bitu v bajte, musíme ostatné zakryť, zamaskovať. To sa robí tzv. maskou:

MMP Cvicenie2 Input.png

Príslušná konštrukcia v C potom bude

if ( PIND & 0x08)          // PIND & 0000 1000

To, že chceme pracovať s tretím bitom sa dá okrem priameho zápisu 0x08 zapísať aj takto (0x01<<3). Príslušná konštrukcia v C potom bude

if ( PIND & (0x01<<3))          // PIND & 0000 1000

Pričom je užitočné zadefinovať si na tento účel makro (v skutočnosti už také makro s názvom _BV(x) existuje)

#define BIT(x) (0x01<<(x))     // zátvoriek nikdy nie je dosť!

Príslušná konštrukcia v C potom bude

if ( PIND & BIT(3))          // PIND & 0000 1000

Podobne sú zadefinované aj tieto dve užitočné makrá (pozri <avr/sfr_defs.h>)

#define bit_is_set(sfr, bit)     (_SFR_BYTE(sfr) & _BV(bit))
#define bit_is_clear(sfr, bit) (!(_SFR_BYTE(sfr) & _BV(bit)))

Takže napokon konštrukcia môže vyzerať takto:

#define Switch 3
if ( bit_is_set(PIND, Switch) )          // test, ci je tlacitko stlacene...

Výstupy

Obdobný problém nastáva, ak chceme nastaviť jeden jediný bit v celom osembitovom registri, či porte. Ani v tomto prípade v assembleri problém neexistuje:

sbi PORTC,2        ; Set   Bit 2 na porte PC
cbi PORTC,2        ; Clear Bit 2 na porte PC

Keďže v C pracujeme s celým bajtom, musíme dávať pozor, aby sme nezmenili stav tých ostatných bitov, ktoré meniť nechceme. Preto najprv prečítame obsah príslušného registra a pomocou masky v ňom zmeníme práve ten jeden požadovaný bit. Budeme rozlišovať, či chceme nastaviť bit do jednotky, alebo vynulovať ho.

Pre nastavenie do jendotky je maska tvorená log. 0 na miestach ktoré nechceme meniť a log. 1 na tom mieste, kde má byť vo výsledku 1:

MMP Cvicenie2 Output1.png

Príslušná konštruckia v C

PORTC = PORTC | 0x04;    // PORTC = PORTC + 0000 0100 
PORTC |= 0x04;
PORTC |= (0x01<<2);

Aby sme nemuseli zložito prepočítavať hexadecimálne hodnoty (C-čko nepozná dvojkovú sústavu, hoci niektoré verzie áno), použijeme vyššie definované makro

PORTC |= _BV(2);

Pre nastavenie do nuly (resp. zhodenie, vynulovanie) je maska tvorená log. 1 na miestach ktoré nechceme meniť a log. 0 na tom mieste, kde má byť vo výsledku 0:

MMP Cvicenie2 Output0.png

Príslušná konštruckia v C

PORTC = PORTC & 0xFB;    // PORTC = PORTC & 1111 1011 
PORTC &= 0xFB;

Aby sme nemuseli zložito prepočítavať hexadecimálne hodnoty (C-čko nepozná dvojkovú sústavu, hoci niektoré verzie áno), použijeme vyššie definované makro

PORTC &= ~(_BV(2));

Aj v tomto prípade si môžeme zadefinovať ďalšie užitočné makrá:

// from AVR035: Efficient C Coding for AVR
#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#define FLIPBIT(ADDRESS,BIT) (ADDRESS ^= (1<<BIT))
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT))

Na záver len zmienime, že v hlavičkových súboroch sú zadefinované aj názvy všetkých špeciálnych registrov SFR vrátane názvov jednotlivých významových bitov, takže všetky uvedené konštrukcie sa používajú aj pri prístupe k nim

MCUCR |= (0x01<<PUD);    // Disable all pull-up resistors
MCUCR |= (_BV(PUD));
SETBIT(MCUCR,PUD);

Pričom všetko uvedené možno kombinovať aj pre viac ako jeden bit

 // Enable receiver and transmitter; enable RX interrupt
 UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);

Further reading