Gli operatori di scorrimento. Scomporre un valore a 16 bit in due valori ad 8 bit e viceversa. Esempio in C per picmicro su Display
E’ un problema comune quello di dover memorizzare un valore a 16 bit in 2 celle di memoria da 1 byte (8 bit). Questo è un caso classico che si presenta quando si vuole usufruire di eeprom (ad es. 24LCxx) che hanno le celle di memoria da 8 bit. In tal caso non è possibile memorizzare direttamente il valore a 16 bit ma bisogna scomporlo in due bytes e quindi effettuare ulteriori operazioni per riottenere il valore originale.
Le operazioni da effettuare sono semplicissime e ve le spiego con un banale esempio. Supponiamo di disporre di un numero a 16 bit (ho preso per esempio il numero 21277), convertiamolo in binario e guardiamo la seguente immagine:
Come vedete nella riga “Valore a 16 bit” ho riportato il valore binario del numero preso ad esempio, nelle due righe superiori sono riportati i valori dei singoli bit. Ho quindi scomposto il numero a 16 bit in due pezzi che ho chiamato BH (Byte Alto – che rappresenta gli 8 bit più a sinistra del numero a 16 bit) e BL (Byte Basso – gli 8 bit più a destra del numero a 16 bit).
Prendendo i due byte (BH e BL) separatamente, vediamo che BH in decimale vale 83 e BL vale 29. Come scomporre quindi il nostro numero di partenza in questi due valori, e come ricomporlo?
Indice dei contenuti
Gli operatori di scorrimento o bitshift
Per effettuare queste operazioni utilizzeremo sicuramente delle operazioni logiche e le operazioni di scorrimento dei bit. Gli operatori logici (AND, OR, XOR, NOT) li abbiamo già incontrati in un articolo precedente in cui abbiamo visto come potevano esserci utili nelle operazioni sui registri ad 8 bit, gli operatori di scorrimento (o operatori bitshift o operatori di shift su bit) vediamo ora a cosa servono e come possono esserci utili.
Gli operatori logici e gli operatori di scorrimento vengono inclusi nella macrocategoria operatori bitwise (ovvero operatori orientati ai bit). Tutti i tipi di microcontrollori devono disporre di questi operatori.
Gli operatori di scorrimento sono soltanto due e prendono il nome di scorrimento o shift a destra (indicato con due segni di maggiore >>) e scorrimento o shift a sinistra (indicato con due segni di minore <<). Il loro utilizzo è semplice, prendiamo come esempio lo shift a destra: l’operazione di scorrimento verso destra comporta che i bit del numero sottoposto all’operazione vengano spostati verso destra di un certo numero di posizioni che andremo a specificare; spostandosi i bit verso destra le posizioni lasciate vuote a sinistra verranno rimpiazzate con degli zeri. Facciamo un esempio per capire meglio.
Guardiamo nell’immagine cosa succede se spostiamo i bit del numero 21277 verso destra di una sola posizione:
Come vedete tutti i bit si sono spostati verso destra di una posizione, il primo bit trovandosi in posizione zero esce fuori dal numero e si crea uno spazio vuoto alla sinistra, che viene riempito con uno zero, questo avviene perchè dobbiamo comunque preservare le 16 “posizioni”. Questa operazione viene scritta come:
21277 >> 1 |
E’ facile capire, quindi, che prendendo un numero a 16 bit ed effettuando lo shift a destra di 8 posizioni, si ottiene il valore del byte alto:
21277 >> 8 |
Come vediamo otteniamo proprio il numero 01010011 che in decimale vale 83, che rappresenta il byte alto del nostro numero preso ad esempio, come abbiamo visto all’inizio.
Le operazioni di shift a sinistra sono identiche: i bit si spostano verso sinistra del numero di posizioni specificate e vengono riempite con degli zeri le posizioni che rimangono vuote a destra.
Vediamo ora come possiamo sfruttare gli operatori bitwise per scomporre un numero a 16 bit in due valori ad 8 bit e poi riprendere questi 2 valori ad 8 bit e ricomporli per riottenere il numero a 16 bit.
Scomposizione di un numero a 16 bit in due valori da 8 bit
Abbiamo visto che per ottenere il byte alto di un valore a 16 bit, basta effettuare lo shift a destra di 8 posizioni. In C questo può essere scritto come:
unsigned int numero=21277; // definisco il mio numero su cui effettuare le operazioni di scomposizione unsigned int BH=0; // byte alto BH=numero>>8; //shift a destra di 8 posizioni |
E il byte basso? Semplice, abbiamo visto in un articolo precedente come utilizzare gli operatori logici con delle “maschere” per ottenere ciò che vogliamo, quindi per ottenere il byte basso ci basta impostare a zero il byte alto con un prodotto logico (AND) utilizzando il numero 00000000 11111111, che in esadecimale viene scritto con 0xFF:
unsigned INT BL=0; // byte basso BL = numero & 0xFF; // metto a zero gli 8 bit più a sinistra |
Come vedete, anche se BH e BL sono valori su cui stiamo ragionando ad 8 bit (quindi valori di tipo CHAR o BYTE) li devo dichiarare come INT (16bit, su altri sistemi vengono indicati come WORD) altrimenti non posso eseguire le operazioni: se scorro verso destra un numero a 16 bit, il risultato sarà ancora un numero a 16 bit! Se eseguo l’AND con una maschera qualsiasi su un numero a 16 bit, il risultato sarà ancora a 16 bit.
Bene, fin qui dovrebbe essere tutto chiaro, ma come ricomporre il numero a 16 bit partendo dai valori dei due bytes?
Composizione di due valori ad 8 bit in un valore a 16bit
Abbiamo a disposizione il byte alto (BH), che rappresenta gli 8 bit più a sinistra del nostro numero a 16 bit, e il byte basso (BL) che rappresenta gli 8 bit più a destra. L’operazione è semplice: gli 8 bit del byte basso si trovano già nella giusta posizione, per cui su loro non dovremo effettuare nessuna operazione. Gli 8 bit del byte alto, invece, devono trovarsi 8 posizioni più a sinistra.. Quindi? Effettuiamo uno shift a sinistra di 8 posizioni! Bravi!
Non mi resta che fondere insieme questi due numeri con una somma logica (OR):
unsigned INT numero_ricomposto=0; // variabile in cui ricompongo il mio numero a 16 bit numero_ricomposto = BL | (BH<<8); |
come vedete ho spostato il byte alto (BH) di 8 posizioni a sinistra, in maniera tale che i primi 8 bit diventino zero e siano “sommati logicamente” con il byte basso, in maniera da ricomporre il numero originario.
Un altro modo per ricomporre il numero a 16 bit, senza effettuare operazioni logiche, potrebbe essere questo:
numero_ricomposto = BL + (BH*256); |
Confrontando questa operazione con quella precedente, si può notare una certa analogia tra l’OR con la somma e tra lo shift a sinistra con la moltiplicazione, difatti:
Uno shift a sinistra di n posizioni equivale a moltiplicare il numero per 2n, uno shift a destra di n posizioni equivale a dividere il numero per 2n (e ovviamente prenderne solo la parte intera).
In generale, le operazioni matematiche da effettuare per ricomporre un numero composto da n bytes possono essere riassunte con la formula:
dove Bi è il byte i-esimo, quindi, ad esempio, con un numero composto da 4 bytes (32 bit – indicato con N32) si effettuerà il calcolo:
sapendo che un numero elevato alla zero vale 1:
Esempio scomposizione e ricomposizione di un numero a 16 bit su picmicro
Se avete sottomano lo schema della nostra Lezione 7 sulla programmazione dei PICMicro in C, potete spulciare il codice allegato che effettua queste operazioni e le mostra sul display LCD
Scomposizione di valori a 16 bit con picmicro (296 download)