C中的位设置/清除?

时间:2013-09-01 18:44:15

标签: c embedded

如何写一个位?我有一个1或0的变量,我想将其值写入8位reg变量中的一个位。

我知道这会设置一点:

reg |= mask; // mask is (1 << pin)

这将清楚一点:

reg &= ~mask; // mask is (1 << pin)

我有办法在一行代码中执行此操作,而无需确定值是高还是低作为输入?

5 个答案:

答案 0 :(得分:10)

假设value01

REG = (REG & ~(1 << pin)) | (value << pin);

我使用REG代替register,因为在OP评论中指出@KerrekSBregister是C关键字。

这里的想法是我们计算REG的值,并清除指定的位,然后根据value设置位。

答案 1 :(得分:2)

因为您使用 embedded 标记了这一点,我认为最好的答案是:

if (set)
    reg |= mask; // mask is (1 << pin)
else
    reg &= ~mask; // mask is (1 << pin)

(您可以将其包装在宏或内联函数中)。原因是像AVR这样的嵌入式架构具有位设置和位清除指令,并且与其他指令相比,分支成本并不高(因为它是在具有推测执行的现代CPU上)。 GCC可以在if语句中识别成语并生成正确的指令。更复杂的版本(即使它在现代x86上测试时无分支)可能无法汇编到嵌入式系统上的最佳指令。

确定的最佳方法是反汇编结果。您不必是专家(特别是在嵌入式环境中)来评估结果。

答案 2 :(得分:2)

C的一个被忽视的特征是位打包,这对于嵌入式工作非常有用。您可以定义struct来单独访问每个位。

typedef struct
{
    unsigned char bit0 : 1;
    unsigned char bit1 : 1;
    unsigned char bit2 : 1;
    unsigned char bit3 : 1;
    unsigned char bit4 : 1;
    unsigned char bit5 : 1;
    unsigned char bit6 : 1;
    unsigned char bit7 : 1;
} T_BitArray;

: 1告诉编译器您只希望每个变量都是1位长。然后只需访问变量reg所在的地址,将其转换为位数组,然后单独访问这些位。

((T_BitArray *)&reg)->bit1 = value;

&reg是变量的地址。 ((T_BitArray *)&reg)是相同的地址,但现在编译器将其视为T_BitArray地址,((T_BitArray *)&reg)->bit1提供对第二位的访问权限。当然,最好使用比bit1

更具描述性的名称

答案 3 :(得分:0)

我认为你所要问的是你是否可以在没有先读取它所在的字节的情况下对单个位执行写入指令。如果是,那么不,你不能这样做。与C语言无关,只是微处理器没有解决单个位的指令。即使在原始机器代码中,如果要设置一个位,也必须读取它所在的字节,更改位,然后将其写回。没有其他办法可以做到。

答案 4 :(得分:0)

how do you set, clear, and toggle a single bit的重复,我也将重新发布我的答案,因为没有人提到过SET和CLEAR寄存器:

  

由于这被标记为“嵌入式”,我假设你正在使用微控制器。以上所有建议均有效且有效。工作(读 - 修改 - 写,工会,结构等)。

     

然而,在基于示波器的调试回合中,我惊讶地发现,与直接将值写入微型PORTnSET / PORTnCLEAR寄存器相比,这些方法在CPU周期中具有相当大的开销,这使得存在紧张循环/高频ISR的切换引脚。

     

对于那些不熟悉的人:在我的例子中,micro有一个通用的引脚状态寄存器PORTn,它反映了输出引脚,所以做PORTn | = BIT_TO_SET会导致对该寄存器的读 - 修改 - 写。

     

但是,PORTnSET / PORTnCLEAR寄存器的“1”表示“请将此位置1”(SET)或“请将此位置为零”(CLEAR),将“0”表示“将引脚单独保留” ”。因此,您最终会得到两个端口地址,具体取决于您是设置还是清除该位(并非总是方便),而是更快的反应和更小的汇编代码。