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);        
    }
}
  • !!(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? – Edgar Bonet 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
9

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
  • 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! – Edgar Bonet 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(). – Edgar Bonet 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). – Edgar Bonet 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.