AVR Bit Magic: Rozdiel medzi revíziami
Zo stránky SensorWiki
Bez shrnutí editace  | 
				Bez shrnutí editace  | 
				||
| (12 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
| Riadok 1: | Riadok 1: | ||
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  | tlačidla mohli použiť inštrukciu  | ||
   sbis PIND,3		; sbis = Skip if Bit Is Set,  preskoc ak je PIND.3 nastaveny (=1)  |    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.    | 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:  | Ak chceme testovať stav jedného bitu v bajte, musíme ostatné zakryť, zamaskovať. To sa robí tzv. maskou:  | ||
| Riadok 10: | Riadok 18: | ||
Príslušná konštrukcia v C potom bude  | Príslušná konštrukcia v C potom bude  | ||
  if ( PIND & 0x04) ...  |   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:  | |||
[[Obrázok:MMP_Cvicenie2_Output1.png|center]]  | |||
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:  | |||
[[Obrázok:MMP_Cvicenie2_Output0.png|center]]  | |||
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 ==  | |||
* [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=37871 Bit manipulation (AKA "Programming 101")]  | * [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=37871 Bit manipulation (AKA "Programming 101")]  | ||
| Riadok 24: | Riadok 91: | ||
[[Category:AVR]][[Category:MMP]]  | [[Category:AVR]][[Category:MMP]][[Category:DVPS]]  | ||
Aktuálna revízia z 08:15, 26. október 2012
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:

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:

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:

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);