2016-09-05 6 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를 설정하여 드라이브를 시작해야합니다.

지금은 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

방금 ​​비트 0 비트 2를 1로 설정하려고 : 여기

는 변화에 대한 비트 연산을 거래보다 읽기 쉬운 무점포 버전입니까? – 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이면 비트는 항상 비트를 지우는 자체와 XOR됩니다. 다른 모든 비트는 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을 이동합니다. 맞습니까? 1로 당신은 마스크를 or'ing 의미합니다, 어떤 의미에서 이전에 상관 없이이 위치에 하나를 의미합니다. 만약 내가 0으로 설정하려면 반전 된 마스크를 추가하고 있습니까? 결과는 모든 비트가 추가됩니다 1 선택된 비트에 0이 추가됩니다. 즉, 다른 모든 비트는 보유한 값을 유지하고 원하는 비트는 0으로 설정됩니다. 나는 그 권리를 얻는다? –

+0

@ gerald-zehetner 다음을 수행하여 분기를 제거 할 수 있습니다. return (command & ~ mask) | 마스크; – mwk

+0

@GeraldZehetner 예, "and"를 제외하고는; "추가"하지 마라. – unwind