2009-09-15 3 views
5

Ich bin in Mikrocontroller-Hacking und während ich mich sehr gut mit bitweisen Operatoren und sprechen direkt auf die Hardware bin, finde ich den resultierenden Code sehr ausführlich und Standard. Der Programmierer auf höherer Ebene in mir möchte eine effektive, aber effiziente Methode finden, ihn zu bereinigen.Ein C-Präprozessor-Makro, um Bitfelder in ein Byte zu packen?

Zum Beispiel gibt es eine Menge von Setzen von Flags in den Registern:

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

Zum Glück gibt es Makros, die tatsächlich Port IO-Operationen (die linke Seite) verstecken, so sieht es aus wie eine einfache Aufgabe. Aber diese ganze Syntax ist für mich chaotisch.

Anforderungen sind:

  • es nur auf 8 Bits zu handhaben bis hat,
  • die Bit-Positionen müssen in der Lage sein, in beliebiger Reihenfolge übergeben werden, und
  • nur Bits sein erfordern setzen soll bestanden.

Die Syntax Ich mag würde, ist:

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

Das Beste, was ich mit so weit kommen habe, ist eine Combo-Makro/Funktion:

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

Das auf meinem speziellen architecure zu 32 Bytes kompiliert nimmt und 61-345 Zyklen (wie viele hängt auszuführen Bits wurden übergeben).

Idealerweise sollte dies im Präprozessor erfolgen, da das Ergebnis eine Konstante ist, und die Maschinenausgabeanweisungen sollten nur eine Zuweisung eines 8-Bit-Werts zu einem Register sein.

Kann dies besser gemacht werden?

Antwort

6

Ja, neu definieren Sie die Makros ABC als 1 << ABC, und Sie vereinfachen das. Das Verknüpfen von Bitmasken ist ein sehr gebräuchliches Idiom, das jeder erkennen wird. Die Schaltpositionen aus dem Gesicht zu bekommen wird viel helfen.

Ihr Code geht von

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

dieser

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

Dieser Vorschlag, dass die viele Male Definitionen Bit-Feld übernimmt mehr verwendet, als es Definitionen von ihnen.


Ich fühle mich wie es könnte eine Möglichkeit sein variadic macros für diesen Einsatz, aber ich kann nicht verstehen auf alles, was leicht als Ausdruck verwendet werden könnte. Betrachten wir jedoch eine Arrayliteral innerhalb einer Funktion zu schaffen Erzeugen Ihre Konstante:

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

Wenn Ihr Compiler gut ist, wird es sehen, dass die gesamte Funktion zur Compile-Zeit konstant ist, und Inlines den sich ergebenden Wert.

+0

, das wahrscheinlich ist, was ich schon von der Architektur-spezifische Compiler-Unterstützung definiert werden, wenn nicht für die Bit-Position Definitionen tun würde (in diesem Fall avr-gcc). Ziemlich dumm, denke ich. Wenn es irgendwo gibt, müssen sie anders als als Linkshänder-Argument verwendet werden ... Ich kann es nicht finden. Aber es ist was es ist. –

+1

Auch der Code, den ich bisher gesehen habe, variiert zwischen der Verwendung von 1 << XX Formular und _BV (XX). _BV ist ein bereitgestelltes Makro, das (1 << n) tut. Aber das bedeutet immer noch zu viel tippen. –

+0

gute Idee. aber aus Sicherheitsgründen würde ich einige Klammern hinzufügen um n #define BIT (n) (0 <<< Sie wissen, Makros können böse sein. Stellen Sie sich vor, jemand benutze das Makro mit BIT (5-1) ... – Roland

0

Warum nicht Ihre eigenen Definitionen zusätzlich zu den vordefinierten diejenigen schaffen ...

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