Bitwise AND and Bitshifting

This code I found on this forum is for a multiplexer.

for (int i=0; i<16; i++)
  {   
    //The following 4 commands set the correct logic for the control pins to select the desired input
    //See the Arduino Bitwise AND Reference: http://www.arduino.cc/en/Reference/BitwiseAnd
    //See the Aruino Bitshift Reference: http://www.arduino.cc/en/Reference/Bitshift
    digitalWrite(CONTROL0, (i&15)>>3); //S3
    digitalWrite(CONTROL1, (i&7)>>2);  //S2
    digitalWrite(CONTROL2, (i&3)>>1);  //S1
    digitalWrite(CONTROL3, (i&1));     //S0
.........

I looked up the references for bitwise and, and bitshift. I seem to understand how they function. Bitwise AND only returns the bits where values that are compared shared a 1, and bitshifting just shifts the bits left or right.

I am having a hard time understanding what this code is doing though. I get that 15,7,3, and 1 all represent a binary number consisting of all 1's (00001111, 00000111, 00000011, and 00000001). So then what is this code actually doing? Why do you need to compare 0&15 and bitshift it to the right 3 times?

What does one iteration of this loop do?

Thanks

edit: I started working out what values would be returned in a given iteration. So for instance given i=5

5&15>>3

00000101
00001111
00000101 >> 3 = 00000000

5&7>>2
00000101
00000111
00000101 >> 2 = 00000001

5&3>>1
00000101
00000011
00000001 >> 1 = 00000000

5&1
00000101
00000001
00000001 >> 0 = 00000001

while i=6 gives 0, 1, 1, 0 respectively. So I guess the ultimate purpose here is to iterate all values of 0's and 1's (meaning HIGH/LOW in this case?). Quite a confusing way to read it in the code but I suppose it makes a lot of sense once you understand its purpose.

It is indeed not pretty code.

That 'for'-loop is used to select all 16 channels one by one.
The lowest bit, bit 0 of 'i' goes to pin CONTROL3.
Bit 1 of 'i' goes to pin CONTROL2.
Bit 2 of 'i' goes to pin CONTROL1.
And bit 3 of 'i' goes to pin CONTROL0.

I think the source is from 2010 from this site: Mux Shield | Mayhew Labs.

The multiplexer that is used is the 74HC4067 which is a normal 16-to-1 analog multiplexer/demultiplexer.

If you want to see normal code for that, have a look at Nick Gammon's tutorial: https://www.gammon.com.au/forum/?id=11976.
He uses this:

  // select correct MUX channel
  digitalWrite (addressA, (which & 1) ? HIGH : LOW);  // low-order bit
  digitalWrite (addressB, (which & 2) ? HIGH : LOW);
  digitalWrite (addressC, (which & 4) ? HIGH : LOW);  // high-order bit

That's makes perfect sense, doesn't it ?
Instead of the number 1, 2, 4, he could have used the binairy notation for that: 0b00000001, 0b00000010 and 0b00000100. That single bit is tested and used to set a pin HIGH or LOW.

The Arduino has also functions to do things with bits.
For example bitRead(): bitRead() - Arduino Reference.

Example:

for (int i=0; i<16; i++)
{  
    digitalWrite(CONTROL0, bitRead( i, 3) == 1 ? HIGH : LOW);
    digitalWrite(CONTROL1, bitRead( i, 2) == 1 ? HIGH : LOW);
    digitalWrite(CONTROL2, bitRead( i, 1) == 1 ? HIGH : LOW);
    digitalWrite(CONTROL3, bitRead( i, 0) == 1 ? HIGH : LOW);
}

The part " == 1 ? HIGH : LOW" is a if-statement. If something is 1, then use HIGH else use LOW.

Since HIGH is 1 and LOW is 0, some don't bother to do that conversion. So this is the same:

for (int i=0; i<16; i++)
{  
    digitalWrite(CONTROL0, bitRead( i, 3));
    digitalWrite(CONTROL1, bitRead( i, 2));
    digitalWrite(CONTROL2, bitRead( i, 1));
    digitalWrite(CONTROL3, bitRead( i, 0));
}

However, the way the HIGH is defined as 1 and the way that LOW is defined as 0 is different on different Arduino board. So I prefer to do it right and change the 1 and 0 to HIGH and LOW.

Do me a favor, don't try to understand questionable code, try to learn from good code.

Koepel:
If you want to see normal code for that, have a look at Nick Gammon's tutorial: Gammon Forum : Electronics : Microprocessors : 74HC4051 multiplexer / demultiplexer.
He uses this:

  // select correct MUX channel

digitalWrite (addressA, (which & 1) ? HIGH : LOW);  // low-order bit
  digitalWrite (addressB, (which & 2) ? HIGH : LOW);
  digitalWrite (addressC, (which & 4) ? HIGH : LOW);  // high-order bit



That's makes perfect sense, doesn't it ?
Instead of the number 1, 2, 4, he could have used the binairy notation for that: 0b00000001, 0b00000010 and 0b00000100. That single bit is tested and used to set a pin HIGH or LOW.

The Arduino has also functions to do things with bits.
For example bitRead(): https://www.arduino.cc/reference/en/language/functions/bits-and-bytes/bitread/.

Example:


for (int i=0; i<16; i++)

    digitalWrite(CONTROL0, bitRead( i, 3) == 1 ? HIGH : LOW);
    digitalWrite(CONTROL1, bitRead( i, 2) == 1 ? HIGH : LOW);
    digitalWrite(CONTROL2, bitRead( i, 1) == 1 ? HIGH : LOW);
    digitalWrite(CONTROL3, bitRead( i, 0) == 1 ? HIGH : LOW);
}




The part "<something> == 1 ? HIGH : LOW" is a if-statement. If something is 1, then use HIGH else use LOW.

Since HIGH is 1 and LOW is 0, some don't bother to do that conversion. So this is the same:


for (int i=0; i<16; i++)

    digitalWrite(CONTROL0, bitRead( i, 3));
    digitalWrite(CONTROL1, bitRead( i, 2));
    digitalWrite(CONTROL2, bitRead( i, 1));
    digitalWrite(CONTROL3, bitRead( i, 0));
}




However, the way the HIGH is defined as 1 and the way that LOW is defined as 0 is different on different Arduino board. So I prefer to do it right and change the 1 and 0 to HIGH and LOW.

Do me a favor, don't try to understand questionable code, try to learn from good code.

Thanks.

So this effectively does the same thing? Basically iterates between all combinations of 0000 to 1111 to select each multiplexer pin?

Yes, it is a simple as that, there is no more to it.

The channel selector of the multiplexer are the same bits of a variable that count from 0 to 15.
A variable for a 'for'-loop from 0 to 15 has exactly the same 0000 to 1111 in the 4 lowest bits.
So all we have to do, is grab the 4 bits from the variable and smash them onto the 4 pins of the multiplexer.

The only extra code is for using 4 digital pins, so four digitalWrite() calls are needed.

Koepel:
Yes, it is a simple as that, there is no more to it.

The channel selector of the multiplexer are the same bits of a variable that count from 0 to 15.
A variable for a 'for'-loop from 0 to 15 has exactly the same 0000 to 1111 in the 4 lowest bits.
So all we have to do, is grab the 4 bits from the variable and smash them onto the 4 pins of the multiplexer.

The only extra code is for using 4 digital pins, so four digitalWrite() calls are needed.

Oh hang on I get it now. I didnt realize what bitRead was doing. So it's basically converting to binary 0-15 and then the ", 3" ", 2" etc is saying this bit position of whatever value "i" is. So right, ultimately it should get all iterations.

And now the first example makes more sense as to why they bitshift... It's just a complicated way of doing the bitRead function. Instead of reading bit 3 specifically it's just shifting it into the first position and reading it there.

Thanks for the info!

Everything is open source and online.

Arduino reference for bitRead(): bitRead() - Arduino Reference.

The bitRead() is macro, defined in Arduino.h here: ArduinoCore-avr/Arduino.h at master · arduino/ArduinoCore-avr · GitHub.

#define bitRead(value, bit) (((value) >> (bit)) & 0x01)

It uses shifting of course :wink:
Suppose you need bit number 3, then it shifts three times to the right. Then a mask with 0x01 to be sure to select only that bit. And that is returned.