2009-09-15 9 views
5

Zajmuję się hakowaniem mikro-kontrolerów i podczas gdy jestem bardzo zadowolony z operatorów bitowych i mówię wprost do sprzętu, znajduję wynikowy kod bardzo obszerny i szablonowy. Wyższy poziom programisty we mnie chce znaleźć skuteczny, ale skuteczny sposób, aby to wyczyścić.Makr preprocesora C do spakowania bitfieldów do bajtu?

Na przykład, istnieje wiele ustawień flagi w rejestrach:

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

szczęście istnieją makra, które ukrywają rzeczywiste operacje portów IO (po lewej stronie), więc wygląda na to proste zadanie. Ale cała ta składnia jest dla mnie brudna.

Wymagania są następujące:

  • ma tylko obsłużyć do 8 bitów,
  • bit pozycji musi być przekazywana w dowolnej kolejności, a
  • powinny wymagać ustawione tylko bity być minęło.

Składnia ja jak to:

SPCR = bitów (SPE Spie MSTR, SPI2X);

Najlepszym Mam wymyślić tak daleko to combo makro/funkcja:

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

To kompiluje do 32 bajtów na moim szczególnym architecure i trwa 61-345 cykli do wykonania (w zależności od tego, ile bitów zostały przekazane).

Idealnie należy to zrobić w preprocesorze, ponieważ wynik jest stałą, a instrukcje maszyny wyjściowej powinny być po prostu przypisaniem 8-bitowej wartości do rejestru.

Czy można to zrobić lepiej?

Odpowiedz

6

Tak, ponownie zdefiniuj makra ABC jako 1 << ABC i uprość to. ORing razem masek bitowych jest bardzo popularnym idiomem, który każdy rozpozna. Pozbycie się pozycji na zmianę z twojej twarzy bardzo pomoże.

Kod idzie z

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

do tego

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

Ta sugestia nie zakładamy, że definicje nieco-polowe są wykorzystywane wielokrotnie więcej niż istnieją definicje nich.


Czuję, że może być jakiś sposób na wykorzystanie variadic macros na to, ale nie mogę dowiedzieć się na niczym, które mogą być łatwo wykorzystane jako wyraz. Zastanów się jednak, tworząc tablicę dosłowne wewnątrz funkcji generującej stałej:

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

Jeśli kompilator jest dobry, to widać, że cała funkcja jest stała w czasie kompilacji, a inline wartość wynikową.

+0

To chyba to, co bym zrobił, gdyby nie definicji pozycji bitowej już zdefiniowane przez architekturę ukierunkowane wsparcie kompilatora (w tym przypadku avr-gcc). Całkiem głupi myślę. Jeśli jest gdziekolwiek, muszą być użyte inaczej niż jako argument z przesunięciem w lewo ... Nie mogę tego znaleźć. Ale tak właśnie jest. –

+1

Również kod, który widziałem do tej pory, zmienia się przy użyciu formularza 1 << XX i przy użyciu _BV (XX). _BV jest dostarczonym makro, które ma (1 << n). Ale to nadal oznacza zbyt dużo pisania. –

+0

dobry pomysł. ale ze względów bezpieczeństwa dodałbym kilka nawiasów wokół n #define BIT (n) (1 << (n)) wiesz, makra mogą być nieprzyjemne. wyobraź sobie kogoś używającego makra z BIT (5-1) ... – Roland

0

Dlaczego nie tworzyć własne definicje oprócz predefiniowanych z nich ...

#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; 
} 
Powiązane problemy