[英]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
首先,让我们交换
-val
和command
(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.