C set bit (manipulation bit)

I program industrial plc and I have to manipulate bits to communicate Profi-bus with VFD. I get 2byte status and should send 2 byte commands. For these operations, I have to set the bits for VFD to work. 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 

Therefore, I need to set bit 0 to set VFD to operating mode. Then I need to set bit 2 to start the disk.

Now I have found a question where bit mockups are described, and I realized that this solution should work, but I don't understand this.

Can someone explain why this is working or not working?

 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; } 
+7
c bit-manipulation plc
source share
2 answers

This is a really smart trick that changes a bit without branching. Here's an explanation assuming that you understand how bitwise operators work.

Let's start by rearranging the expression

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

First, let swap -val and command

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

Then we apply the distribution law

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

Now understand that (under the condition of two additions), if val is 0, -val is 0 (no bits), and if val is 1, -val is -1 (all bits are set)

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

Thus, the assignment can be rewritten as if-statement

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

If val is 1, the bit at bit_nr has XORed with its negative value, which always sets the bit. If val is 0, the bit is XORed with itself, which always clears the bit. All other XORed bits are 0, which leaves them unchanged.

Here's a more readable, unbranched version that handles the bitwise action for the 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; } 
+2
source share

It seems to work, but it is very surprising and not very clear. Someone might have been "too smart." A more understandable 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; } 

It has a jump in it ( if ), but a smart compiler can optimize it. If this is not very performance-critical code, it is often better with clarity.

Note that using unsigned types is usually a good idea when doing bit-level manipulations.

+4
source share

All Articles