【问题标题】:C set a bit (bit manipulation)C 设置位(位操作)
【发布时间】:2017-01-12 13:10:56
【问题描述】:

我正在对工业 plc 进行编程,并且我必须操作位以与 VFD 进行 profi-bus 通信。我得到一个 2 字节的状态并且必须发送 2 字节的命令。对于此操作,我必须设置位以使 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;
}

【问题讨论】:

  • 您是否只是将位 0 ​​和位 2 设置为 1?
  • -val 在 val==1 时将所有位设置为 1,在 val==0 时将所有位设置为 0。
  • 这只是一个例子,我要操作很多不同的命令,所以我想写一个函数,我可以通用。
  • 不清楚“命令”应该是什么。您希望将特定位置的位设置为 1 或 0,或者您希望根据某些模式/命令设置多个位。您的功能似乎同时尝试两者,这对我来说没有意义。
  • 那么看起来你只是想做这样的事情? uint16_t change_bit (uint16_t value, bool set, size_t bit_n) { if(set) return value | (1u &lt;&lt; bit_n); else return value &amp;= ~(1u &lt;&lt; bit_n); }.

标签: c bit-manipulation plc


【解决方案1】:

这似乎可行,但非常令人惊讶且不太清楚。有人可能会“太聪明”。更明确的方法可能是:

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),但聪明的编译器可能会优化它。如果它不是对性能非常关键的代码,那么清晰通常会更好。

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

【讨论】:

  • 由于我在实时系统上,它可能会变得很关键,但就我而言,我还有足够的时间。 uint_16 掩码仅包含 0,并且通过
  • @gerald-zehetner 您可以通过以下方式删除分支:return (command & ~mask) |面具;
  • @GeraldZehetner 是的,除了它是“和”;不是“添加”。
  • @mwk 不,这将始终设置位,这不是目标(仅当valuetrue 时才应设置),否则应将其清除。因此if。 :)
  • 是的,我忘记了。我的意思是:return (command & ~(1u
【解决方案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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-06
    • 1970-01-01
    • 2013-05-03
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多