C宏设置多个位

时间:2019-01-25 16:18:11

标签: c macros c-preprocessor

我正在C语言中的微控制器上工作。这部分涉及更改寄存器中的位值。我提出了一些宏来简化操作:

#define BIT_SET(addr, shift) (addr | (1 << shift))
#define BIT_RESET(addr, shift) (addr & ~(1 << shift))
#define BIT_GET(addr, shift) (addr & (1 << shift))
...
reg1 = BIT_SET(reg1, 3); //set bit 3
...

但是,现在我想创建一个宏,该宏将一次更改几个位。例如,可能想将10101010的前3位更改为110,从而得到数字11001010

我可以用C语言吗?还是我走错路了?

3 个答案:

答案 0 :(得分:2)

我的经验是拥有多个位的位掩码要容易得多。因此,使用特定的定义来标识各个位,其定义表示该位的功能,如

#define ENABLE_T1  0x0001
#define ENABLE_T2  0x0002

然后将它们与按位运算符一起使用以打开和关闭位。

unsigned short usReg1 = GetRegister(REG1);

// disable T1 and enable T2

usReg1 &= ~ ENABLE_T1;
usReg1 |= ENABLE_T2;

// enable both T1 and T2
usReg1 |= (ENABLE_T1 | ENABLE_T2);

// disable both T1 and T2
usReg1 &= ~(ENABLE_T1 | ENABLE_T2);

或测试位

if (usReg1 & ENABLE_T1) {  // is T1 turned on

if (usReg1 & (ENABLE_T1 | ENABLE_T2)) {  // is either T1 or T2 turned on

if ((usReg1 & (ENABLE_T1 | ENABLE_T2)) == ENABLE_T1) {  // is T1 turned on and T2 turned off

if ((usReg1 & (ENABLE_T1 | ENABLE_T2)) && ((usReg1 & (ENABLE_T1 | ENABLE_T2)) != (ENABLE_T1 | ENABLE_T2))) {  // is either T1 or T2 turned on but not both

宏说明

通过这种方式,您需要确保在宏定义中使用括号来尽可能地隔离参数,以确保安全。所以

#define BIT_SET(addr, shift) (addr | (1 << shift))

真的应该写成

#define BIT_SET(addr, shift) ((addr) | (1 << (shift)))

答案 1 :(得分:2)

您可以引入一个全角掩码,该掩码定义要设置的位。 例如。要设置前3位,请使用0xe0(二进制11100000)。

连同该掩码一起,提供还要以全角写入的值。
例如。前三个位中所需的二进制110为0xc0。

然后只需要写一点所需的位,就需要一点点操作魔术。

#define MULTIBIT_SET(addr, mask, value) (((addr)&((0xff)^(mask)))|((value)&(mask)))

此处((0xff)^(mask))会反转掩码,因此与此进行“与”运算会从值中删除现有位。
((value)&(mask))是该值,但仅在所需位置设置了位,即,这防止了在其他位置进行不必要的位设置。

总的来说,将这两个部分相加,即可获得所需的结果。

这里有一个设计选择,我要提到:
称之为偏执狂。如果有人告诉我“我只想将前三位设置为该值,该值在其他位置有位”,那么我宁愿以这种方式确保安全,即删除“其他位”。
是的,我假设无意设置位比没有设置位的风险更大,这是一个问题。

答案 2 :(得分:1)

您始终可以在C语言中完成它:-)

如果您使用的是C99兼容的编译器,并且假设它是2019年,我想您可以使用,则可以使用可变参数宏使它工作,更具体地说,使用P99

我有一个示例,其中我提取了P99的相关组件,但是它变得一团糟,因为这是对C预处理器作为图灵完备语言的一种严重使用。

这也滥用括号;特别是EXTRACT_VALUE x扩展为EXTRACT_VALUE (a, b),因为我们传递了一个元组列表。

#include "p99.h"

#define EXTRACT_SHIFT(shift, value) (shift)
#define EXTRACT_VALUE(shift, value) (value)
#define MERGE(NAME, I, A, B) (A), (B)

#define SET_OR_CLEAR(target, x, i) \
    ((EXTRACT_VALUE x) ? \
        ((target) |= (1 << EXTRACT_SHIFT x)) : \
        ((target) &= ~(1 << EXTRACT_SHIFT x)))
#define SET_AND_CLEAR(target, pairs...) \
    P99_FOR(target, P99_NARG(pairs), MERGE, SET_OR_CLEAR, pairs)

// ...
reg1 = BIT_SET(reg3, 3); //set bit 3                                         
// ...
SET_AND_CLEAR(reg3, (7, 1), (6, 1), (5, 0)); // set bits 6 and 7, clear bit 5

您可能需要使用-std=c99进行编译。请注意,我使用了变体词pairs而不是__VA_ARGS__,以明确表明我们希望使用2元组作为参数。

请注意,这不会掩盖任何未设置或清除的输入寄存器。