2009-09-15 7 views
5

Je me lance dans le piratage de microcontrôleurs et, bien que je sois très à l'aise avec les opérateurs bit à bit et que je parle directement au matériel, je trouve que le code qui en résulte est très verbeux et passe-partout. Le programmeur de niveau supérieur en moi veut trouver un moyen efficace mais efficace pour le nettoyer.Une macro préprocesseur C pour empaqueter des champs de bits dans un octet?

Par exemple, il y a beaucoup de drapeaux de réglage dans les registres:

/* 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);  
} 

Heureusement, il y a des macros qui cachent port réel des opérations d'entrées-sorties (le côté gauche), il ressemble à une simple affectation. Mais toute cette syntaxe est pour moi désordonnée.

Les exigences sont:

  • il n'a qu'à gérer jusqu'à 8 bits,
  • les positions bit doivent pouvoir être transmis dans un ordre quelconque et
  • ne devraient exiger que définis bits à passé.

La syntaxe je voudrais est:

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

Le meilleur que je suis venu avec à ce jour est une macro/fonction combo:

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

Cette compile 32 octets sur mon architecure particulier et prend 61-345 cycles pour exécuter (en fonction du nombre de les bits ont été passés).

Idéalement, cela devrait être fait en préprocesseur puisque le résultat est une constante, et les instructions de la machine de sortie devraient être juste une affectation d'une valeur de 8 bits à un registre.

Cela peut-il être amélioré?

Répondre

6

Oui, redéfinissez les macros ABC comme 1 << ABC, et vous simplifiez cela. ORing ensemble des masques de bits est un idiome très commun que tout le monde reconnaîtra. Obtenir les positions de décalage hors de votre visage aidera beaucoup.

Votre code va de

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

à cette

#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; 
} 

Cette suggestion ne suppose que les définitions de champ de bits sont utilisés plusieurs fois plus qu'il y a des définitions d'entre eux.


je me sens comme il pourrait y avoir un moyen d'utiliser variadic macros pour cela, mais je ne peux pas figurer sur tout ce qui pourrait facilement être utilisé comme une expression. Considérez, cependant, la création d'un tableau littéral dans une fonction de génération de votre constante:

#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(); 
} 

Si votre compilateur est bon, il verra que la fonction entière est constante à la compilation, et en ligne la valeur résultante.

+0

C'est probablement ce que je ferais si pas pour les définitions de position de bit déjà définies par le support du compilateur spécifique à l'architecture (dans ce cas avr-gcc). Assez bête je pense. S'il y a n'importe où, ils doivent être utilisés autrement que comme un argument de gauche ... Je ne peux pas le trouver. Mais c'est ce que c'est. –

+1

En outre, le code que j'ai vu jusqu'ici varie entre l'utilisation de 1 << formulaire XX, et l'utilisation de _BV (XX). _BV est une macro fournie qui fait (1 << n). Mais cela signifie encore trop de dactylographie. –

+0

bonne idée. mais pour des raisons de sécurité, je voudrais ajouter quelques accolades autour de n #define BIT (n) (1 << (n)) vous savez, les macros peuvent être méchant. Imaginez quelqu'un utilisant la macro avec BIT (5-1) ... – Roland

0

Pourquoi ne pas créer vos propres définitions en plus de celles prédéfinies ...

#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; 
} 
Questions connexes