简体   繁体   English

C设置一点(位操作)

[英]C set a bit (bit manipulation)

I'm programming a industrial plc and I have to manipulate bits for a profi-bus communication with a VFD. 我正在编程工业plc,我必须操纵位以进行与VFD的profi-bus通信。 I get a 2byte status and have to send 2byte commands. 我得到2byte状态,必须发送2byte命令。 For this operations I have to set bits to get the VFD operating. 对于此操作,我必须设置位以使VFD运行。 For example: 例如:

                   Byte n+1           Byte n
PLC    -->  --------------------- ---------------   --> VFD
            15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
            ---------+--------- | | | | -+- | | +- 0: Reglersperre / Freigabe
                     |          | | | |  |  | +--- 1: Freigabe / Schnellstopp
                     |          | | | |  |  +----- 2: Freigabe / Halt
                     |          | | | |  +-------- 3..4: reserviert = 0
                     |          | | | +------------5: Parametersatz-Umschaltung
                     |          | | +------------- 6: Reset
                     |          | +--------------- 7: reserviert = 0
                     |          |
                     |          +----------------- 8: Lüften der Bremse ohne Antreibsfreigabe
                     +---------------------------- 9..15: reserviert = 0

So I have to set bit 0 to set the VFD in operation mode. 所以我必须将位0设置为在操作模式下设置VFD。 Then I need to set bit 2 to start the drive. 然后我需要设置第2位来启动驱动器。

Now I found a question where bit-maipulation is described and I figured out that this solution should work, but I don't really understand it. 现在我发现了一个问题 ,其中描述了位操作,我发现这个解决方案应该可行,但我并不理解它。

Can someone please explain why this works or doesn't work? 有人可以解释为什么这有效或不起作用?

uint16_t change_bit(uint16_t command, unsigned int bit_nr, unsigned int val) {
  /* command = 2byte command; bit_nr = bit to manipulate;
     val = value bit should get (1;0) */
  command ^= (-val ^ command) & (1U  << bit_nr);
  return command;
}

That seems to work, but it's very surprising and not so clear. 这似乎有效,但它非常令人惊讶,并不那么清楚。 Some one could it "too clever". 有人可能“太聪明”了。 A more clear way could be: 更明确的方法可能是:

uint16_t change_bit(uint16_t command, unsigned int bit, bool value)
{
  const uint16_t mask = 1u << bit;
  if(value)
    return command | mask;
  else
    return command & ~mask;
}

This has a jump in it (the if ), but a clever compiler might optimize that out. 这有一个跳跃( if ),但聪明的编译器可能会优化它。 If it's not very performance-critical code, it's often better with clarity. 如果它不是非常性能关键的代码,那么通常更清晰。

Note that using unsigned types is generally a good idea when doing bit-level manipulation. 请注意,在进行位级操作时,使用无符号类型通常是个好主意。

This is indeed a clever trick that modifies a bit without branching. 这确实是一个聪明的技巧,可以在没有分支的情况下修改一点。 Here's an explanation that assumes you understand how bitwise operators work. 这是一个解释,假设您了解按位运算符的工作原理。

Let's start by rearranging the expression 让我们从重新排列表达开始

(-val ^ command) & (1 << bit_nr)

First, let's swap -val and command 首先,让我们交换-valcommand

(command ^ -val) & (1 << bit_nr)

Then, apply the distributive law 然后,应用分配法

(command & (1 << bit_nr)) ^ (-val & (1 << bit_nr))

Now, realize that (assuming two's complement) if val is 0, -val is 0 (no bits set), and if val is 1, -val is -1 (all bits set) 现在,实现(假设二进制补码)如果val为0, -val为0(没有设置位),如果val为1,则-val为-1(所有位设置)

(command & (1 << bit_nr)) ^ (val ? (1 << bit_nr) : 0)

So the assignment can be rewritten as an if-statement 因此,可以将赋值重写为if语句

if (val)
    command ^= (command & (1 << bit_nr)) ^ (1 << bit_nr);
else
    command ^= command & (1 << bit_nr);

If val is 1, the bit at position bit_nr is XORed with its negated value which always sets the bit. 如果val为1,则bit_nr位的位与其否定值进行异或运算,该值始终设置该位。 If val is 0, the bit is XORed with itself which always clears the bit. 如果val为0,则该位与其自身进行异或运算,该位始终清除该位。 All other bits a XORed with 0 which leaves them unchanged. 所有其他位与0进行异或,使其保持不变。

Here's a more readable branchless version that trades a bitwise operation for a shift: 这是一个更具可读性的无分支版本,用于换班的按位操作:

uint16_t change_bit(uint16_t command, unsigned int bit_nr, unsigned int val) {
  // Always clear the bit.
  command &= ~(1u << bit_nr);
  // Set the bit if val == 1.
  command |= val << bit_nr;
  return command;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM