我试图编写的宏的目的是更新一个传递由值、位置和长度定义的新值的字符。让我举个例子来更好地解释一下:
原始值:0b11000011 要更新的值:0b11 放置位置:4 新值的长度:2
基本上,我想在字节(0b11000011)中间放置两位(0b11)以获得新字节(0b11011011)
我现在拥有的宏类似于
#define UPDATE_REG(REG, MASK, POS, LEN, VAL) ((REG) = (((REG) & (MASK)) | (VAL<<(POS +1 -LEN))))
而且效果很好。例如,在我的标题中,我有这样的定义
#define CP_BYP_MODE_SAD 0x38
#define CP_BYP_MODE_ADR 0xD4
#define CP_BYP_MODE_POS 4
#define CP_BYP_MODE_LEN 3
#define CP_BYP_MODE_DEF 0b00
#define CP_BYP_MODE_MSK 0b11100111
为了使用宏,我编写了以下代码
uint8_t Reg = 0b11000011;
UPDATE_REG(Reg, CP_BYP_MODE_MSK, CP_BYP_MODE_POS, CP_BYP_MODE_LEN, 0b11);
这个解决方案效果很好,但我的观点是,我想通过一些技巧来减少参数(如果可能的话),考虑到我将在每次调用时使用由以下组成的三个定义 姓名_MSK 名称_POS name_LEN
需要明确的是,我的目标是拥有一个类似的宏
#define UPDATE_REG(REG, NAME, VAL) ((REG) = (((REG) & (**NAME_MSK**)) | (VAL<<(**NAME_POS** +1 - **NAME_LEN**))))
例如,仅通过 CP_BYP_MODE 即可获取 CP_BYP_MODE_POS,并且能够自动添加 _POS 后缀。
最佳答案
首先,确实没有必要为此编写晦涩的宏,这只会让代码更难阅读。不幸的是,人们试图将简单的按位算术隐藏在抽象层后面是很常见的,只是因为他们发现按位算术很棘手。
您应该做的是保持简单:
uint8_t reg = 0xC3;
uint8_t mask = 0x18;
uint8_t new_val = 0x18;
reg &= ~mask; // clear values with mask
reg |= new_val; // write new value to the cleared position
<小时/>
值得注意的是,如果您知道掩码,则意味着您已经知道大小和位位置。您可以根据大小和位置计算掩模,如下所示:
mask = ((1 << size)-1) << pos;
大小 = 2,位置 = 3 给出:
1 << 2
= 0000 0100b = 0x04
0x04-1
= 0000 0011b = 0x03
0x03 << 3
= 0001 1000b = 0x18
关于动态使用不同定义的 C 宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47470362/