7

I were examining shiftOut() function code in wiring_shift.c and I didn't quite understand what is going in digitalWrite function. I see !!(val & (1 << i)) is taking the bit value from val but how exactly it works?

The whole function is below.

void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
    uint8_t i;

    for (i = 0; i < 8; i++)  {
        if (bitOrder == LSBFIRST)
            digitalWrite(dataPin, !!(val & (1 << i)));
        else    
            digitalWrite(dataPin, !!(val & (1 << (7 - i))));

        digitalWrite(clockPin, HIGH);
        digitalWrite(clockPin, LOW);        
    }
}
3
  • !!(val & (1 << i)) is the most complex part of this code. If you do understand this, then what is the part you do not understand? Jun 1 '15 at 8:56
  • @edgar-bonet Actually this was the question. I can see it somehow calculates the bit value, but I didn't understand how it do this.
    – wizofwor
    Jun 1 '15 at 9:01
  • You do understand the behaviour of the shiftOut function? I mean, you do understand that it'll shift out a value (in binary form). And will give a clock pulse along with it.
    – Paul
    Jun 1 '15 at 10:29
8

I'll assume bitOrder == LSBFIRST.

  • i is the bit number, i.e. the “index” of the next bit to write
  • 1 is 00000001 in binary
  • << is the shift left operator. It returns its first argument shifted left by as many positions as indicated by the second argument
  • 1<<i is binary 00000001 shifted left by i positions, i.e. something like 0...010...0, where the single 1 is in the i-th position counting from the right (rightmost being position 0)
  • & is the “bitwise and operator”, where any_bit & 0 is zero and any_bit & 1 is any_bit
  • val & (1 << i) is 0...0(i-th bit of val)0...0 in binary, where the i-th bit of val is in the i-th position of the result
  • !! is a double negation: it converts zero to zero and any non-zero value to one
  • !!(val & (1 << i)) is either 0 or 1, and is exactly the i-th bit of val
6
  • let me summarize what I understand. Let assume val = '10010111'; for i=2 !!(val & (1 << i)) = !!('10010111' & '00000100') = !!('00000100') = 1 If i is = 3 !!(val & (1 << i)) = !!('10010111' & '00001000') = !!('00000000') = 0
    – wizofwor
    Jun 1 '15 at 10:43
  • This is correct! Jun 1 '15 at 10:48
  • And this means if I give 16bit or longer data to shiftOut, it will send least significant 8 bits and ignore the rest.
    – wizofwor
    Jun 1 '15 at 10:52
  • 1
    shiftOut() takes uint8_t data. If you call it with a 16-bit argument, the compiler will implicitly remove the 8 most significant bits before the actual call to shiftOut(). Jun 1 '15 at 11:01
  • 1
    @SteveMcDonald: Yes, the output would be the same without the double negation, because digitalWrite() interprets any non-zero value (not just 1) as meaning HIGH. Apparently, the author of shiftOut() did not want to rely on this behavior, and instead wanted to always call digitalWrite() with either 0 (i.e. LOW) or 1 (HIGH). Oct 1 '17 at 12:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.