AC 预处理器宏将位域打包到字节中?

发布于 2024-08-05 00:59:12 字数 1145 浏览 3 评论 0原文

我正在研究微控制器黑客技术,虽然我非常熟悉按位运算符并与硬件直接对话,但我发现生成的代码非常冗长和样板。我的高级程序员希望找到一种有效但高效的方法来清理它。

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

/* 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 位值分配给寄存器。

这件事还能做得更好吗?

I'm getting into micro-controller hacking and while I'm very comfortable with bitwise operators and talking right to the hardware, I'm finding the resulting code very verbose and boilerplate. The higher level programmer in me wants to find an effective but efficient way to clean it up.

For instance, there's a lot of setting flags in registers:

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

Thankfully there are macros that hide actual port IO operations (the left hand side), so it looks like a simple assignment. But all that syntax to me is messy.

Requirements are:

  • it only has to handle up to 8 bits,
  • the bit positions must be able to be passed in any order, and
  • should only require set bits to be passed.

The syntax I'd like is:

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

The best I've come up with so far is a combo macro/function:

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

This compiles to 32 bytes on my particular architecure and takes 61-345 cycles to execute (depends on how many bits were passed).

Ideally this should be done in preprocessor since the result is a constant, and the output machine instructions shouldbe just an assignment of an 8 bit value to a register.

Can this be done any better?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

你丑哭了我 2024-08-12 00:59:12

是的,将宏 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;
}

建议确实假设位域定义的使用次数比它们的定义多很多次。


我觉得可能有某种方法可以为此使用 可变参数宏 ,但我想不出任何可以轻松用作表达式的东西。但是,请考虑在生成常量的函数内创建数组文字:

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

如果编译器良好,它会发现整个函数在编译时是常量,并内联结果值。

Yea, redefine the macros ABC as 1 << ABC, and you simplify that. ORing together bit masks is a very common idiom that anyone will recognize. Getting the shift positions out of your face will help a lot.

Your code goes from

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

to this

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

This suggestion does assume that the bit-field definitions are used many times more than there are definitions of them.


I feel like there might be some way to use variadic macros for this, but I can't figure on anything that could easily be used as an expression. Consider, however, creating an array literal inside a function generating your constant:

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

If your compiler is good, it will see that the entire function is constant at compile-time, and inline the resultant value.

渡你暖光 2024-08-12 00:59:12

除了预定义的定义之外,为什么不创建自己的定义......

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

Why not create your own definitions in addition to the pre-defined ones...

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文