Mar*_*ouf 5 c macros bit-manipulation c-preprocessor
我正在进入微控制器黑客攻击,虽然我对按位运算符和硬件说话非常熟悉,但我发现结果代码非常冗长和样板.我的高级程序员希望找到一种有效但有效的方法来清理它.
例如,寄存器中有很多设置标志:
/* 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);
}
Run Code Online (Sandbox Code Playgroud)
值得庆幸的是,有些宏隐藏了实际的端口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);
}
}
Run Code Online (Sandbox Code Playgroud)
这在我的特定架构上编译为32个字节,需要61-345个周期才能执行(取决于传递了多少位).
理想情况下,这应该在预处理器中完成,因为结果是常量,输出机器指令应该只是将8位值赋给寄存器.
这可以做得更好吗?
是啊,重新定义宏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);
}
Run Code Online (Sandbox Code Playgroud)
对此
#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;
}
Run Code Online (Sandbox Code Playgroud)
这个建议确实假设比特字段定义的使用次数比它们的定义多很多倍.
我觉得可能有某种方法可以使用变量宏,但我无法想象任何可以轻易用作表达式的东西.但是,请考虑在生成常量的函数内创建数组文字:
#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();
}
Run Code Online (Sandbox Code Playgroud)
如果您的编译器是好的,它将看到整个函数在编译时是常量,并内联结果值.