代码之家  ›  专栏  ›  技术社区  ›  itsadok

一个C预处理器宏把位域压缩成一个字节?

  •  5
  • itsadok  · 技术社区  · 17 年前

    我进入了微控制器黑客,虽然我非常熟悉按位运算符和正确的硬件交谈,但我发现产生的代码非常冗长和样板。我的高级程序员希望找到一种有效但高效的方法来清理它。

    例如,寄存器中有许多设置标志:

    /* 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个周期来执行(取决于传递了多少位)。

    理想情况下,这应该在预处理器中完成,因为结果是一个常量,并且输出机器指令应该只是一个8位值到寄存器的赋值。

    这能做得更好吗?

    2 回复  |  直到 10 年前
        1
  •  6
  •   Phil Miller    17 年前

    是的,重新定义宏 ABC 作为 1 << 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();
    }
    

        2
  •  0
  •   Troy Moon    17 年前

    为什么不在预定义的定义之外创建自己的定义。。。

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