2009-09-15 40 views
5

我正在進入微控制器黑客行爲,雖然我很熟悉按位運算符和正確的硬件說話,但我發現代碼非常冗長,並且沒有樣板。我的高級程序員想要找到一種有效但有效的方法來清理它。C預處理器宏將位域打包爲一個字節?

例如,有一個在寄存器中很多設置的標誌:

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

值得慶幸的是還有一些隱藏實際的端口IO操作(左側)宏,所以它看起來像一個簡單的任務。但是,對我來說,所有的語法都是混亂的。

的要求是:

  • 它只有處理多達8位,
  • 位位置必須能夠以任意順序進行傳遞,並
  • 應該只需要設置位被通過。

我想的語法是:

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

我想出迄今最好的是一個組合的宏/功能:

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

這將編譯在我的特定構建築32個字節,並採取61-345週期執行(取決於有多少位通過)。

理想情況下本應在預處理器來完成,因爲結果是恆定的,並且輸出機器指令shouldBe這樣只是一個8位的值與寄存器的分配。

這可以做得更好嗎?

回答

6

肯定,重新定義宏ABC1 << ABC,你簡化。把口罩放在一起是任何人都會認識的非常常見的習慣用法。從你的臉上獲得輪換職位將有很大幫助。

您的代碼變爲從

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

這個

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

這一建議並假設位字段定義使用的許多倍以上,也有他們的定義。


我覺得可能有一些方式使用variadic macros這一點,但我不能對任何可能很容易被用作表達式的身影。考慮,但是,創建數組的函數產生的常數內的文字:

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

如果你的編譯器是好的,它會看到,整個功能是在編譯時間常數,內聯得到的值。

+0

這可能是我會如果沒有做已經由特定架構的編譯器支持定義位的位置定義(在這種情況下avr-gcc)。 我覺得很蠢。如果有任何地方他們必須使用,而不是作爲左移參數...我找不到它。但是它就是這樣啊。 –

+1

此外,我目前看到的代碼在使用1 << XX表單和使用_BV(XX)之間有所不同。 _BV是提供的宏(1 << n)。但這仍然意味着打字太多。 –

+0

好主意。但是出於安全原因,我會加上一些大括號,如果你知道,宏可以是討厭的。想象有人使用BIT(5-1)的宏... – Roland

0

爲什麼除了預定義的人不能創造自己的定義...

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