Salta la barra di navigazione [1] - Vai alla barra di navigazione [3] - Scrivimi una mail [4]

C per sistemi embedded

Tornare a scuola - Sommario - Novità - Tutorial - Progetti - Risorse - Non solo elettronica

In fase di sviluppo Pagina in fase di preparazione In fase di sviluppo

Questo non è un  manuale C, tantomeno una serie di lezioni, ma la raccolta di alcune brevi osservazioni spesso considerate bizzarre quando si studia per la prima volta il C, ma spesso utili quando si lavora a stretto contatto con l'hardware.

Rappresentazione degli interi

Gli interi hanno diverse rappresentazione in memoria, a seconda del numero di bit che vengono utilizzati e del fatto che il bit più significativo sia il segno (complemento a due) oppure no.

Il C non definisce la lunghezza di nessuno di questi interi; per esempio un int, a seconda del compilatore (e della sua configurazione...) potrebbe avere una lunghezza variabile tra 8, 16, 32 o 64 bit. Utile la funzione sizeof().

Alcuni compilatori sono distribuiti con header file che specificano alcuni tipi di interi di lunghezza nota e costante, con e senza segno. Qualche esempio, dall'ovvio significato, forniti con il compilatore gcc:

Esempio d'uso:

int8_t   my_byte; // my_byte potrà contenere valori compresi tra -128 e +127
uint16_t my_word; // my_word potrà contenere valori compresi tra 0 e 65535

La base con cui i numeri sono presenti in memoria è sempre, ovviamente, il binario. Nel scrittura del codice è possibile scegliere la rappresentazione "umana" più comoda per un determinato uso:

I quattro esempi mostrati producono tutti esattamente lo stesso identico effetto. In genere la prima si usa per rappresentare numeri nel senso ordinario del termine, gli altri per rappresentare bit.

Operatori bitwise e bit shifting

Gli operatori bitwise e di scorrimento dei bit operano sui singoli bit che formano i byte, indipendentemente dalla loro posizione e quindi dal peso. In pratica sono le stesse operazioni della logica booleana.

Operano su tutte le variabili riconducibili agli interi (int, char, long int, signed int, unsigned int, int32_t...), per comodità umane spesso rappresentate in esadecimale. Sono quattro operazioni logiche di base e due operatori di shift (scorrimento):

AND &
OR |
XOR ^
NOT ~ (AltGr + ì sulla tastiera italiana)
Scorrimento verso destra >>
Scorrimento verso sinistra <<

Esempi:

unsigned char a=0xFA, b=0xA0, y;  // a = 1111 1010    b = 1010 0000

y = a & b;    // y = 0xA0 = 1010 0000
y = a | b;    // Y = 0xFA = 1111 1010
y = a ^ b;    // y = 0x55 = 0101 1010
y = ~a;       // y = 0x05 = 0000 0101
y = a >> 1;   // y = 0x7D = 0111 1101
y = a << 4;   // y = 0xA0 = 1010 0000

Quanto vale il terzo bit da destra del byte myBYTE? Scriviamo l'if corrispondente:

if (myBYTE & 0b00000100)
    // myBYTE, bit 2 vale 1
else
    // myBYTE, bit 2 vale 0

Voglio che il secondo bit da sinistra (bit 6) del byte myBYTE diventi 1:

myBYTE = myBYTE | 0b01000000; // Non standard... meglio myBYTE = myBYTE | 0x40

Voglio che il secondo bit da sinistra del byte myBYTE diventi 0:

myBYTE = myBYTE & 0b10111111;

Possono anche operare secondo le usuali regole degli operatori. Ad esempio:

a <<= 1; coincide con a = a << 1

Approfondimento ed altri esempi al termine della pagina 44 della guida C. (operazioni logiche tra variabili)

Bit field

Questo tipo particolare di struct permette di creare variabili di lunghezza arbitraria, compresa tra un bit e la lunghezza di un intero. Tipicamente serve per identificare il significato di determinati bit o gruppi di bit all'interno di un registro hardware. Esempio:

typedef struct {
    unsigned a :3;
    unsigned b :1;
    unsigned c :4;
} esempio_t;

esempio_t My_esempio;

La variabile My_esempio è quindi costituita da 3 parti di lunghezza rispettivamente 3, 1 e 4 bit. L'uso è quello solito delle struct:

My_esempio.a = 5;  // Anche My_esempio.a = 0b101;
My_esempio.b = 0;
My_esempio.c = 20; // Errore! 20 non è rappresentabile con soli 4 bit

Questo tipo di struct è ampiamente utilizzato dal compilatore XC8, associando a ciascun SFR fisico due variabili (union):

La modifica di una delle due "variabili" si riflette immediatamente anche sull'altra, oltre che sulla periferica fisica.

Variabile volatile

La parola chiave volatile usata per definire una variabile significa che il suo valore può cambiare indipendentemente dal programma in esecuzione. Nei sistemi embedded due sono i casi significativi:

In pratica il compilatore disattiva per queste variabili una serie di ottimizzazioni.

Esempio, dove PORTC è il contenuto di un registro hardware:.

volatile unsigned char PORTC:

while (PORTC == 10) {
  ... //Codice che non modifica PORTC
  }

Consideriamo PORTC come una variabile normale diversa da 10; questo codice corrisponde ad un loop infinito, il compilatore lo sa e quindi evita che verificare ad ogni giro se PORTC è uguale a 10, risultando più veloce, ma, nel nostro caso, errato. Definire PORTC volatile, obbliga invece il compilatore ad eseguire il confronto ad ogni "giro"

Variabile locale statica

Una variabile locale statica (quindi definita all'interno di una funzione) è una variabile che mantiene il suo valore tra le varie chiamate alla funzione. Quindi si comporta, da questo punto di vista, come una variabile globale pur mantenendo invariata la sua visibilità (scope). Queste variabili vengono inizializzate dal compilatore come le variabili statiche (la variabile My_a dell'esempio, viene inizializzata a zero mentre una normale variabile locale assumerebbe ad ogni chiamata della funzione un valore casuale)

Esempio:

int my_function(void) {
static int My_a;
  ...
}

Utile per esempio per memorizzare lo stato di una ISR senza dove creare una variabile globale.

Diverso il caso di variabili globali statiche: la variabile è infatti globale ma il suo scope è limitato al file in cui è definita.

Approfondimenti: guidaC.pdf

Ultima modifica di questa pagina: 6 ottogre 2016

Tornare a scuola

Licenza "Creative Commons" - Attribuzione-Condividi allo stesso modo 3.0 Unported


EN - Pagina principale - Sommario - Accessibilità - Note legali e privacy policy - Posta elettronica

XHTML 1.0 Strict - CSS 3