我正在进入微控制器黑客攻击,虽然我对按位操作符和硬件说话非常熟悉,但我发现结果代码非常冗长和样板。我的高级程序员希望找到一种有效但有效的方法来清理它。
例如,寄存器中有很多设置标志:
/* 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操作(左侧),所以它看起来像一个简单的赋值。但对我来说,所有这些语法都很混乱。
要求是:
我想要的语法是:
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位值赋值给寄存器。
这可以做得更好吗?
答案 0 :(得分:6)
是的,将ABC
重新定义为1 << ABC
,然后简化它。将位掩码OR化在一起是一种非常常见的习惯,任何人都会认识到这一点。让轮换位置脱离你的脸会有很大的帮助。
您的代码来自
#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();
}
如果您的编译器是好的,它将看到整个函数在编译时是常量,并内联结果值。
答案 1 :(得分: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;
}