C设置一点(位操作)

时间:2016-09-05 07:46:14

标签: c bit-manipulation plc

我正在编程工业plc,我必须操纵位以进行与VFD的profi-bus通信。我得到2byte状态,必须发送2byte命令。对于此操作,我必须设置位以使VFD运行。例如:

                   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

所以我必须将位0设置为在操作模式下设置VFD。然后我需要设置第2位来启动驱动器。

现在我发现了一个question,其中描述了位操作,我发现这个解决方案应该可行,但我并不是真的理解它。

有人可以解释一下为什么这样做或不起作用?

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;
}

2 个答案:

答案 0 :(得分:4)

这似乎有效,但这非常令人惊讶,并不那么清楚。有人可能“太聪明”了。更明确的方法可能是:

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;
}

它有一个跳跃(if),但聪明的编译器可能会优化它。如果它不是性能非常关键的代码,那么通常更清晰。

请注意,在进行位级操作时,使用无符号类型通常是一个好主意。

答案 1 :(得分:2)

这确实是一个聪明的技巧,可以修改一个没有分支的位。这是一个解释,假设您了解按位运算符的工作原理。

让我们首先重新安排表达式

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

首先,让我们交换-valcommand

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

然后,应用分配法

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

现在,实现(假设二进制补码)如果val为0,-val为0(没有设置位),如果val为1,则-val为-1(设置所有位)

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

因此可以将赋值重写为if语句

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

如果val为1,则位置bit_nr的位与其否定值进行异或运算,该值始终设置该位。如果val为0,则该位与自身进行异或运算,并始终清除该位。所有其他位与0进行异或,保持不变。

这是一个更易读的无分支版本,用于换班的按位操作:

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;
}