2016-09-05 8 views
7

私は工業用PLCをプログラミングしており、VFDとのprofi-bus通信のビットを操作する必要があります。私は2byteのステータスを取得し、2byteのコマンドを送信する必要があります。この操作では、VFDを動作させるビットを設定する必要があります。例:Cビット設定(ビット操作)

    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 

したがって、動作モードでVFDを設定するにはビット0を設定する必要があります。次に、ドライブを起動するためにビット2を設定する必要があります。

ここで私はを見つけました。このビット解読が記述されていますが、この解決策はうまくいくはずですが、実際には理解できません。

誰かがこれがうまくいかない理由を説明できますか?

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

あなただけの1にビット0ビット2を設定しようとしていますか? – dudeman

+0

- val val == 1のときはすべてのビットを1に設定し、val == 0のときはすべてのビットを0に設定します。 –

+0

-val ^コマンドは、valが1のときにコマンドを無効にします。 –

答えて

2

これは確かに分岐せずにビットを変更する巧妙なトリックです。ここでは、ビットごとの演算子の仕組みを理解していることを前提に説明します。

のは

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

まず式を整理することから始めましょう、ましょうスワップ-valとその後

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

commandは、(それを実現すると仮定すると、今すぐ分配法則

(command & (1 << bit_nr))^(-val & (1 << bit_nr)) 
を適用します2の補数)の場合 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のビットは、そのビットを常に設定するネゲートされた値とXORされます。 valが0の場合、ビットはそれ自身と排他的論理和が取られ、常にビットがクリアされます。それ以外のビットはすべて0でXORされ、それらは変更されません。ここで

がシフトをビット単位の操作を取引するより読み無店舗バージョンです:

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; 
} 
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)を持っていますが、賢いコンパイラはそれを行う最適化するかもしれません。パフォーマンスに重大な影響を与えるコードではない場合は、明瞭さを保ちながら改善することがよくあります。

ビットレベルの操作を行うときは、通常、符号なしの型を使用することをお勧めします。

+0

As I ' uint_16マスクには0が含まれています。<<操作では1を希望の位置に移動します。右に設定するといいでしょうか?私は0に設定したい場合、反転マスクを追加していますか?その結果、すべてのビットに1が加算されます選択されたビットに0が追加されます。これは、他のすべてのビットがそれらの値を保持し、希望のビットが0に設定されていることを意味します。私はそれを得ますか? –

+0

@ gerald-zehetner次のようにしてブランチを削除することができます。 return(command&〜mask)|マスク; – mwk

+0

@GeraldZehetnerはい、それ以外は "and"です。 "追加"しないでください。 – unwind