2009-09-15 14 views
5

Sto entrando nell'hacking del microcontrollore e mentre mi trovo a mio agio con gli operatori bit a bit e sto parlando direttamente dell'hardware, sto trovando il codice risultante molto dettagliato e boilerplate. Il programmatore di livello superiore in me vuole trovare un modo efficace ma efficiente per ripulirlo.Una macro di preprocessore C per raggruppare i bitfield in un byte?

Per esempio, c'è un sacco di bandiere impostazione nei registri:

/* Provided by the compiler */ 
#define SPIE 7 
#define SPE 6 
#define DORD 5 
#define MSTR 5 
#define CPOL 4 
#define CPHA 3 

void init_spi() { 
    SPCR = (1 << SPE) | (1 << SPIE) | (1 << MSTR) | (1 << SPI2X);  
} 

Per fortuna ci sono le macro che nascondono le operazioni di IO porta effettiva (lato sinistro), in modo che appaia come una semplice assegnazione. Ma tutta questa sintassi per me è disordinata.

requisiti sono:

  • deve solo gestire fino a 8 bit,
  • il bit posizioni devono essere in grado di essere passato in qualsiasi ordine, e
  • dovrebbe richiedere impostare solo bit da passato.

La sintassi che vorrei è:

SPCR = bit (SPE, SPIE, MSTR, SPI2X);

Il migliore che è venuta in mente finora è un combo macro/funzione:

#define bits(...) __pack_bits(__VA_ARGS__, -1) 

uint8_t __pack_bits(uint8_t bit, ...) { 
    uint8_t result = 0; 
    va_list args; 
    va_start(args, bit); 

    result |= (uint8_t) (1 << bit); 

    for (;;) { 
     bit = (uint8_t) va_arg(args, int); 
     if (bit > 7) 
      break; 
     result |= (uint8_t) (1 << bit); 
    } 
} 

Questo compila a 32 byte sulla mia particolare architecure e prende 61-345 cicli da eseguire (dipende da quanti bit sono stati passati).

Idealmente questo dovrebbe essere fatto nel preprocessore poiché il risultato è una costante, e le istruzioni macchina uscita shouldbe solo un'assegnazione di un valore a 8 bit in un registro.

Si può fare di meglio?

risposta

6

Sì, ridefinire le macro ABC come 1 << ABC e lo si semplifica. ORingare insieme le bit mask è un linguaggio molto comune che chiunque riconoscerà. Ottenere le posizioni di turno dalla tua faccia aiuterà molto.

Il codice va da

#define SPIE 7 
#define SPE 6 
#define DORD 5 
#define MSTR 5 
#define CPOL 4 
#define CPHA 3 

void init_spi() { 
    SPCR = (1 << SPE) | (1 << SPIE) | (1 << MSTR) | (1 << SPI2X);  
} 

a questo

#define BIT(n) (1 << (n)) 
#define SPIE BIT(7) 
#define SPE BIT(6) 
#define DORD BIT(5) 
#define MSTR BIT(5) 
#define CPOL BIT(4) 
#define CPHA BIT(3) 

void init_spi() { 
    SPCR = SPE | SPIE | MSTR | SPI2X; 
} 

Questo suggerimento si assume che le definizioni bit-field vengono usate molte volte più di quanto non ci sono definizioni di loro.


mi sento come ci potrebbe essere qualche modo per utilizzare variadic macros per questo, ma non riesco a capire su tutto ciò che potrebbe essere facilmente utilizzato come espressione. Si consideri, tuttavia, creando un array letterale all'interno di una funzione di generazione del costante:

#define BITS(name, ...) \ 
    char name() { \ 
     char[] bits = { __VA_ARGS__ }; \ 
     char byte = 0, i; \ 
     for (i = 0; i < sizeof(bits); ++i) byte |= (1 << bits[i]); \ 
     return byte; } 

/* Define the bit-mask function for this purpose */ 
BITS(SPCR_BITS, SPE, SPIE, MSTR, SPI2X) 

void init_spi() { 
    SPCR = SPCR_BITS(); 
} 

Se il compilatore è buono, si vede che l'intera funzione è costante al momento della compilazione, linea e il valore risultante.

+0

che è probabilmente quello che farei se non fosse per le definizioni delle posizioni di bit già in corso di definizione da parte del supporto del compilatore specifico per l'architettura (in, questo caso avr-gcc). Abbastanza stupido, penso. Se c'è ovunque, devono essere usati oltre che come argomento del turno di sinistra ... Non riesco a trovarlo. Ma è quello che è. –

+1

Inoltre, il codice che ho visto finora varia tra l'utilizzo del formato 1 << XX e l'uso di _BV (XX). _BV è una macro fornita che fa (1 << n). Ma ciò significa ancora troppo digitazione. –

+0

buona idea. ma per motivi di sicurezza, aggiungerei alcune parentesi attorno al n. #define BIT (n) (1 << (n)) sai, i macro possono essere cattivi. immagina qualcuno che usa la macro con BIT (5-1) ... – Roland

0

Perché non creare le proprie definizioni in aggiunta a quelli predefiniti ...

#define BIT_TO_MASK(n) (1 << (n)) 

#define SPIE_MASK BIT_TO_MASK(SPIE) 
#define SPE_MASK BIT_TO_MASK(SPE) 
#define DORD_MASK BIT_TO_MASK(DORD) 
#define MSTR_MASK BIT_TO_MASK(MSTR) 
#define CPOL_MASK BIT_TO_MASK(CPOL) 
#define CPHA_MASK BIT_TO_MASK(CPHA) 

void init_spi() { 
    SPCR = SPE_MASK | SPIE_MASK | MSTR_MASK | SPI2X_MASK; 
} 
Problemi correlati