How shiftOut function works internally? (explanation on source code)

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? 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. 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

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 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. Jun 1 '15 at 10:52
• 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
• @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