Risorse per il Lab. Elettronica
Home Page


CAD/CAE

LTspice

PSpice


Sistemi

Arduino

AVR

Raspberry Pi

National Instruments


Risorse

Datasheet

Link

Hardware e Software

Misc






Licenza Creative Commons
*** IL DOWNLOAD DEI SEGUENTI FILE COMPORTA L'IMPLICITA ACCETTAZIONE DELLE SEGUENTI CONDIZIONI ****
I seguenti Documenti, ove non diversamente specificato, sono distribuiti con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported. E' dunque escluso l'utilizzo per scopi di lucro . Il materiale viene fornito AS-IS senza alcuna garanzia di assenza di errori e/o imprecisioni e senza alcuna forma di supporto. L'Autore è sollevato da ogni responsabilità per qualsiasi utilizzo dei seguenti file. Autore: Francesco Parisi fparisi gmail com


« Indice

AVR ATmega8 con libC - Gli operatori bit a bi



u.r. 17/04/16

OPERATORI BIT A BIT

Prima di iniziare la programmazione in C dei mcu AVR, è bene soffermarsi sugli operatori bit a bit del linguaggio C essendo questi molto usati nella scrittura/lettura dei registri

Si ricorda che le variabili associate ai registri dei mcu AVR a 8 bit sono ovviamente del tipo char senza segno (0...255)
Gli esempi sottoriportati possono essere eseguiti con qualsiasi compilatore C (es. gcc) su qualsiasi sistema operativo (Windows, Linux ecc.). La notazione 0b non è standardizzata nel linguaggio (il compilatore lcc-win32 sembra essere l'unico ad accettarla); qui è utilizzata solo come convenzione di rappresentazione del numero in formato binario.

Il linguaggio C, per la manipolazione dei bit, mette a disposizione del programmatore sei operatori bit a bit, che possono essere applicati soltanto ad operandi interi ovvero di tipo char, short, int e long, con o senza segno.

& AND bit a bit
| OR inclusivo bit a bit
^ OR esclusivo (EX-OR), bit a bit
<< bit shift a sinistra
>> bit shift a destra
~ complemento a uno (NOT)

E’ necessario distinguere gli operatori bit a bit & e | dagli operatori logici && e ||, che implicano la valutazione da sinistra a destra di un valore di verità. Per esempio, se x vale 1 e y vale 2, allora x&y vale 0, mentre x&&y vale 1”, p.59, B.W. KERNIGHAN e D.M. RITCHIE, Linguaggio C, II edizione, Jackson Libri, Milano 1989.
Gli operatori che vengono correntemente applicati sono:

Operatore NOT ~

E’ un operatore unario che produce il complemento ad uno dell’operando; converte dunque un bit 1 in bit 0 e, viceversa, un bit 0 in bit 1. Esempio:
unsigned char portb = 0x00 /* portb vale 0b00000000 */
portb = ~portb /* portb ora vale 0b11111111 */
vengono negati tutti i bit della variabile registro portb, indistintamente.

Operatori di SHIFT << e >>

Gli operatori di shift << e >> spostano, rispettivamente, verso sinistra e verso destra il loro operando sinistra di un numero di bit pari al valore del loro operando destro, che deve essere positivo. Per esempio x << 3 sposta a sinistra di tre posizioni il valore di x, riempiendo con degli zeri le posizioni così liberatesi. Esempio:
unsigned char portc = 0x01; /* portc = 0b00000001 */
portc = portc << 3; /* portc ora vale 0b00001000 */
Creiamo una tabella, in cui shiftiamo a sinistra il bit 0x01 (ovvero 1 in decimale) di k posizioni, per k che va da 0 a 7. Otteniamo così tanti byte che hanno attivo solo il bit in corrispondenza della posizione k, mentre i restanti risultano azzerati:

Da questa tabella, guardando la colonna dei risultati in formato decimale o esadecimale, è facile verificare che lo shift a sinistra corrisponde ad una moltiplicazione per due; analogamente lo shift a destra corrisponde a una divisione per due. Gli 1 "shiftati" di k posizioni ci torneranno utili più avanti.

Operatore OR bit a bit |

Se a un bit aggiungiamo 1, qualunque sia il suo valore, il risultato sarà sempre 1: se è 0 abbiamo che 0 | 1 = 1, se è 1 abbiamo comunque che 1 | 1 = 1. Dunque, quando vogliamo attivare (set) un determinato bit di un byte, non dobbiamo fare altro che sottoporre questo a un’operazione OR bit a bit con un altro byte, che abbia tutti i bit azzerati tranne quello della posizione (ovvero delle posizioni) che vogliamo attivare. Questo secondo byte prende il nome di maschera ("mask") Quindi l’operatore OR bit a bit | viene usato per attivare uno o più bit, lasciando inalterati gli altri.
Esempio:
Attivare i soli bit 0 3 della variabile ddrc, lasciando inalterati i restanti bit:
unsigned char ddrc = 0x98; /* 0b10011000 */
unsigned char mask = 0x09; /* 0b00001001 */
ddrc = ddrc | mask; /* ora ddrc vale 0b10011001 */
Un byte che abbia attivo un solo bit (e tutti gli altri azzerati) altro non è che il risultato di uno shift a sinistra del numero 1, di un numero di posizioni corrispondente alla posizione del bit attivo.
Nell’esempio appena fatto, la variabile maschera altro non era che l’OR bit a bit di due “sottomaschere”: 0b00000001 (per attivare il bit 0) e 0b00001000 (per attivare il bit 3). Queste due sottomaschere altro non sono che, rispettivamente, il numero 1 shiftato di 0 posizioni e il numero 1 shiftato di 3 posizioni (vedi tabella precedente).
Riscriviamo dunque l’esempio, in una forma più leggibile:
unsigned char ddrc = 0x98; /* 0b10011000 */
unsigned char mask = (1 << 0) | (1 << 3); /* 0b00000001 | 0b00001000 */
ddrc |= mask;
Questa seconda notazione, del tutto equivalente alla precedente, è sicuramente preferibile, in quanto ci consente subito di sapere quali bit della variabile verranno "attivati" (ovvero "settati").
Infine, l’istruzione usata:
ddrc |= mask
adopera l’operatore di assegnamento messo a disposizione dal C ed è del tutto equivalente, ma sintatticamente più compatta, a quella precedemente usata:
ddrc = ddrc | mask

Operatore AND bit a bit &

Facciamo subito un esempio. Fare l’AND bit a bit di due operandi ddrb e mask e riporre il risultato nella variabile ddrb:
unsigned char ddrb = 0xFF; /* ddrb = 0b11111111 */
unsigned char mask = 0xFA; /* mask = 0b11111010 */
ddrb = ddrb & mask; /* ddrb ora vale 0b11111010 */
Nell’esempio l’operatore AND ha quindi azzerato solo alcuni bit della variabile registro ddrb secondo una maschera; nell’esempio, la variabile mask ha azzerati i soli bit di posizione 0 e 2 e dunque anche i soli bit di posizione 0 e 2 della variabile ddrb verranno azzerati, mentre i restanti rimarranno invariati, avendo subito un’operazione AND bit a bit con un bit 1.
Quindi l’operatore AND bit a bit & viene usato per azzerare uno o più bit, lasciando inalterati i restanti.
Per dare una maggiore leggibilità e immediatezza al codice, la maschera viene creata attraverso una operazione di AND bit a bit con il complemento del solo bit a 1 che vogliamo azzerare:
unsigned char ddrb = 0b11111111;
unsigned char mask = (1 << 0) | (1 << 2); /* 0b00000001 | 0b00000100 */
ddrb &= ~mask; /* ddrb ora vale 0b11111010 */
L’inizializzazione della variabile mask è del tutto equivalente a quella dell’esempio precedente, ma in questo caso si legge immediatamente quale bit verrà azzerato; inoltre, volendo, è possibile aggiungere, semplicemente con un’operazione di OR bit a bit, altri bit da resettare; definendo la posizione del bit con una variabile è infine possibile creare delle strutture iterative.
Infine l’istruzione usata
ddrb &= ~mask
è la forma compatta e del tutto equivalente alla seguente:
ddrb = ddrb & ~mask
dove si è fatto uso di un operatore di assegnamento

Operatore X OR bit a bit ^

L’ex-or serve a fare il toogle di un determinato bit ovvero la sua commutazione, lasciando inalterati gli altri bit: se il bit è 0, lo attiva; viceversa, se è 1, lo azzera. L’operatore X-OR serve, dunque, a commutare uno o più bit, restando inalterati gli altri

Dalla tabella di verità si vede che se il bit è a 0, facendone l’X-OR con una maschera di valore 1, diventa 1; viceversa se è a 1, facendone l’X-OR sempre con una maschera di valore 1 diventa 0. Se, invece, la maschera è 0 per quel bit, lo stesso non viene commutato cioè resta inalterato. Quindi se vogliamo far commutare un bit alla posizione k, dobbiamo sottoporlo a una operazione di EX-OR con una maschera che abbia un bit 1 alla posizione k e tutti bit 0 nelle restanti posizioni.

Esempio: Commutare il bit 1 e il bit 0 della variabile portb
unsigned char portb=0x01; /* portb vale 0b00000001 */
unsigned char mask = (1 << 1) | (1 << 0);  /* commuta i soli bit di posizione 0 e 1 */
portb ^= mask /* ora portb vale 0x00000010; */
Alcune applicazioni degli operatori bit a bit:

Lettura del singolo bit di una variabile registro

Per leggere il singolo bit di una variabile registro, bisogna fare prima un AND bit a bit con una maschera che ha il bit 1 solo nella posizione relativa al bit di cui vogliamo conoscere lo stato; questa maschera, quindi, altro non è che il solito 1 shiftato a sinistra di k posti, ove k è proprio la posizione del singolo bit che vogliamo leggere.
Successivamente si fa un AND logico

Esempio: leggere il bit 7 della variabile pinb e stamparne a video lo stato: alto (1) o basso (0)

#include <stdio.h>

int main(void)
{
   unsigned char pinb =0x81; /* assegnamo pinb il valore 0b10000001 */
   
   if ( pinb && (1 << 7) ) {      /* 0b10000001 && 0b1000000 = 1 */
      printf("pb7 livello H\n");
   } else {
      printf("pb7 livello L\n");
     }
}
Il risultato dell’operazione di AND logico può essere o 1 (livello H) oppure 0 (livello L); per quanto citato all'inizio, in questo caso, vale 1 perché il bit 7 di pinb vale 1. Ripetere l'esercizio assegnando un valore di pinb che comporti il bit di posizione 7 a 0 (es. 0x62)

Esercizi

  1. Settare i soli bit di posto 2, 3 e 4 della variabile portc
  2. Leggere il bit di posizione 3 della variabile pinc e stamparne il valore a video: H o L
  3. Fare il toogle dei bit 1 e 7 della variabile portb
  4. Mediante un for, attivare un solo bit per volta della variabile portc; per ciascuna attivazione stampare il messaggio: "attivo bit numero..."
  5. Usare la seguente macro, per gli esercizi precedenti:
    #define _BV(B) (1 << (B))
    
  6. Utilizzare le seguenti macro, per gli esercizi precedenti:
    #define _SETBIT(P,B)  P |= _BV(B) 
    #define _CLEARBIT(P,B)  P &= ~_BV(B) 
    #define _TOGGLEBIT(P,B)  P ^= _BV(B) 
    

Informativa estesa sui Cookie | Web Statistics

Clicky

| Realizzazione a cura di Francesco Parisi (2002 ÷ 2024) | Contatti