Howdy Y'all...
I have a two (2) questions about the code that makes up the Arduino predefined shiftOut() function. First, please allow me to explain where I think the shiftOut() code is and what it does, then I will ask my questions. Please go easy on me, I'm no expert on this C/C++ stuff:
In my stand-alone copy of Arduino v1.8.5 running on a Windows 10 laptop I found the code for shiftOut() inside this file:
C:...\arduino-1.8.5\hardware\arduino\avr\cores\arduino\wiring_shift.c
Here is an exact copy of the shiftOut() code extracted from the file "wiring_shift.c":
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);
}
}
I think I understand what is going on with this code. In the LSBFIRST case, (1 << i) steps a 1 from LSB to MSB one bit position per step. At each step the moving bit is bitwise ANDed with the byte to be shifted out. This allows the digitalWrite() statement to toggle the result to the output pin once per bit as the bit position steps from LSB to MSB. In the MSBFIRST case (1 << (7 - i)) steps the 1 in the opposite direction (from MSB to LSB) and just like the LSBFIRST case each bit is sent to the ouput pin, just in the reverse order starting with the MSB.
Now - for my two questions:
Question-1: What are the two bitwise NOT operators !! doing in: !!(val & (1 << i)) and !!(val & (1 << (7 - i)))? I duplicated the shiftOut() function on my Arduino Uno while probing the bytes during each step and as far as I can tell the !! operators do nothing except slow down and complicate the code. So why are the back-to-back NOTs there?
Question-2: Where are the { } brackets in the if(){}-else{} statement? I know the code works without the brackets, I tested it stand-alone. If the brackets are not functionally needed then why do they exist? If the brackets exist just to add clarity to the code (a very good reason IMO), then why are they missing from Arduino's shiftOut() built-in code?
By the way, just for fun (and a bit of clarity) here is the same shiftOut() function written with the { } brackets included and uint8_t replaced by Arduino's byte. I tested this code stand-alone as well and it works exactly like Arduino's shiftOut() built-in function (even without the !!'s). Note how I renamed shiftOut() to shOut() here to avoid a collision when testing:
// Functional duplicate of the Arduino built-in shiftOut():
void shOut(byte dataPin, byte clockPin, byte bitOrder, byte val) {
byte 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);
}
}
In case anyone is wondering why I'm digging into the Arduino code; I was thinking about how to reorder the endianness of an unsigned byte array and I remembered shiftOut(). I figured shiftOut() with its MSBFIRST/LSBFIRST options must do this (it does). And yes, bit-reordering algorithms are almost as common as hen's-teeth. For example have a look here:
http://graphics.stanford.edu/~seander/bithacks.html
Thanks for any replies... David in Sunny Florida