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); 
 }
}
asked Jun 1, 2015 at 8:38
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? Commented Jun 1, 2015 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. Commented Jun 1, 2015 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. Commented Jun 1, 2015 at 10:29

1 Answer 1

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
answered Jun 1, 2015 at 9:28
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 Commented Jun 1, 2015 at 10:43
  • This is correct! Commented Jun 1, 2015 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. Commented Jun 1, 2015 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(). Commented Jun 1, 2015 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). Commented Oct 1, 2017 at 12:34

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.