0

I’m trying to wrap my head around using binary addresses in a mux. Failing miserably. For the circuit I have six 16 channel analog multiplexers CD74HC4067 as slaves to an eight channel multiplexor CD54HC4051 master. They are bringing in 96 sensor readings (IR receiver diodes through Op Amps) into an array that, in turn, is used to constantly update lotsa LEDs through a series of shift registers (TLC5940) using the Arduino TLC5940 Library.

I’ve included the possible wiring scheme that the code is trying to control. It probably needs work too.enter image description here

Question: I’ve seen, and SEMI-understand matrixes that address 32 inputs (4 x 8 bits) but not quite understanding how I can address the six mux chips with 96 inputs. Or if I can even do it this way. Example from this forum: How to code for cascading multiplexers? Is it possible with this matrix of 96 inputs, and how? Am I barking up the wrong tree?

Code so far:

    #include <Tlc5940.h> //Arduino TLC5940 Library controls the individual LEDs - "Pixels"

int ledCount = 96; //change  LED "PIXEL" count in matrix...also number of IR sensor inputs

int thresh = 10; //value for usable distance reading from IR

// main structures to hold LED Pixel array on/off state, IR reciever readings
byte dsply_state[ledCount];    //pixel on-off grid, need for tlc5940    
uint_16  recv_state[ledCount];           // IR Readings - FROM mux set-up  


// Master IR -mux sensor pins for Arduino Pro MINI
#define  M_S0 2
#define  M_S1 4
#define  M_S2 5
// Slave IR mux sensor pins
#define  S_S0 6
#define  S_S1 7
#define  S_S2 8
#define  S_S3 12


//  ANALOG PIN :
#define  InputFromMux A0

void setup() {
    //Serial.begin(9600);

    //tlc5940 Arduino library
    Tlc.init(0);

    // CONFIGURE ADDRESS PINS
    pinMode(M_S0, OUTPUT);
    pinMode(M_S1, OUTPUT);
    pinMode(M_S2, OUTPUT);

    pinMode(S_S0, OUTPUT);
    pinMode(S_S1, OUTPUT);
    pinMode(S_S2, OUTPUT);
    pinMode(S_S3, OUTPUT);
    pinMode(InputFromMux, INPUT); 

}


void loop() {
    // LOOP THROUGH ALL THE ADDRESSES OF THE MASTER and SLAVES.  

  for (int i = 0; i < ledCount; i++) {

    digitalWrite(M_S2, i & 0b1000000);
    digitalWrite(M_S1, i & 0b0100000);    
    digitalWrite(M_S0, i & 0b0010000);
    digitalWrite(S_S3, i & 0b0001000);       
    digitalWrite(S_S2, i & 0b0000100);
    digitalWrite(S_S1, i & 0b0000010);
    digitalWrite(S_S0, i & 0b0000001);

    delay(2);

    //edited as per @Gerben. thanks so much

    //get an individual sensor reading from IR receiver/opAmp mux, store it in a variable 
     int IRstate = analogRead(InputFromMux);

    //populate receiving array with a reading
     recv_state[i] = IRstate;

    //populate display array for tlc5940 to push out
     dsply_state[i] = recv_state[i];

} //end for(i...) loop

      //run through display state array to check if the stored reading is greater than a threshold number
      //if yes, then turn on the "pixel". If not, set it to zero
      //

       for (int x = 0; x < ledCount; x++)
          if (dsply_state[x] >= thresh ) {  

               Tlc.set(dsply_state[x], 4000);

             //or if reading from mux is zero  
          } else if (dsply_state[x] < thresh) {

              Tlc.set(dsply_state[x], 0);
              }

            //tlc5940 Arduino library - update() clocks the chips
            // move update(); outside the for(x...) loop for speed?
        Tlc.update();
        delay(10);

      } //end For(x..) Loop

}//endMainLoop
  • The 48 in the for loop should be 96. It should be digitalWrite(M_S2, i & 0b1000000) not 0b110000. You do an anologRead, but never do anything with the read value (IRstate). – Gerben May 3 '18 at 15:07
  • Thank you @Gerben. This is exactly the part that confuses me. How to correctly write the masks(?) that make the chips cycle through each input. If I digitalWrite M-S2 as i & 0b1000000, won't that be the same as M_S1? Or does it change enough as you've added six zero's instead of five - M_S1, i & 0b100000? As per IRstate, I thought I was pushing each reading into recv_state array in the for loop. Or should the variable be inside the loop? Or is there a better way to get each reading into an array? – Curtis Bracher May 3 '18 at 15:45
  • You correctly spotted the extra 0. Your mask needs to be 7 bits long. 3 bits to represent a 0-7 for the first mux, 4 bits to represent 0-15 for the second stage muxes. Maybe for readability, you could add a 0 on the left of all mask. – Gerben May 3 '18 at 15:53
  • The line of your code is IRstate = recv_state[x];. Maybe you meant recv_state[x] = IRstate; instead. The value of right part is what you assign to the left part. I think what you need is to remove the for( x ...) loop and add recv_state[i] = IRstate; and dsply_state[x] = recv_state[x]; – Gerben May 3 '18 at 15:56
  • Thanks @gerben, I'll edit the masks. Re:IRstate.....So that works without a for loop? int IRstate = analogRead(InputFromMux); recv_state[i] = IRstate; disply_state[x] = recv_state[i]; That three lines takes the individual readings, pushes all 96 readings into recv-state array and then maps recv-state[] onto a new display_state[] array? Is it the main loop then, doing the pushing? – Curtis Bracher May 3 '18 at 16:24
1

OK. First, how the multiplexer works. I have a page about multiplexers in which there is this example of the 74HC4051:

CD4051

Depending on the "binary" input to A/B/C (shown as S0/S1/S2 on your schematic) there is a low resistance path between one of the 8 inputs and the single output. The CD74HC4067 works in a similar way, except it switches 16 inputs.

So, if you set up the binary number 2 (010) in A/B/C then the third input is selected (number 02 because it is zero-relative).

In your schematic you have 6 x CD74HC4067 which therefore can have 16 x 6 inputs (96 inputs in total). The outputs of those 6 are fed into the 54HC4051 (2 aren't used) so therefore the 54HC4051 can select which of the 6 16-input chips it is interested in right now, by setting up a number between 0 and 5 on the 8-input multiplexer.

The four binary inputs to the 6 x CD74HC4067 are all tied together as you can see on the schematic.

Thus your method would be:

  • Loop from 0 to 5 on the master multiplexer (the 54HC4051). This selects which of the 6 16-input multiplexers you are interested in right now.

  • For each of those, loop through 0 to 15 on S0/S1/S2/S3 so that the currently-selected CD74HC4067 routes its input through to its output.

  • Read the resulting value

Those two loops will give you 96 inputs, which is what you are trying to read.

This technique takes 7 output pins to output the binary numbers (3 bits plus 4 bits). It also needs one input pin to read the result.


Judging by the schematic it uses SPI (serial data) to control the 6 x TLC5940 shift registers which lets it turn LEDs on and off with only two control pins (SCK and MOSI named SCLK and SOUT on your schematic).

The chips are daisy-chained together so all of those TLC5940s are controlled by those two pins.

This fairly minimalist design has therefore left some spare pins on the Arduino Pro.


Mostly having a hard time visualizing the code, especially how to write correctly the 7 binary digital write commands. Is it possible for you to correct what I've written above, so I might be able to see the pattern?

I know you changed the question by the time I saw this, but the code below illustrates what I mean by the two loops:

const int MUX_CHIPS = 6;
const int LEDS_PER_CHIP = 16;
const int TOTAL_IR_INPUTS = MUX_CHIPS * LEDS_PER_CHIP;
// Master IR -mux sensor pins for Arduino Pro MINI
const byte  M_S0 = 2;
const byte  M_S1 = 4;
const byte  M_S2 = 5;
// Slave IR mux sensor pins
const byte  S_S0 = 6;
const byte  S_S1 = 7;
const byte  S_S2 = 8;
const byte  S_S3 = 12;
//  ANALOG PIN :
const byte INPUT_FROM_MUX = A0;

int  recv_state[TOTAL_IR_INPUTS];           // IR Readings - FROM mux set-up  

void setup() {
    Serial.begin(115200);

    // CONFIGURE ADDRESS PINS
    pinMode(M_S0, OUTPUT);
    pinMode(M_S1, OUTPUT);
    pinMode(M_S2, OUTPUT);

    pinMode(S_S0, OUTPUT);
    pinMode(S_S1, OUTPUT);
    pinMode(S_S2, OUTPUT);
    pinMode(S_S3, OUTPUT);
    pinMode(INPUT_FROM_MUX, INPUT); 

    // set ADC prescaler of 32
    ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2)); // clear prescaler bits
    ADCSRA |= bit (ADPS0) | bit (ADPS2);                 //  32 
} // end of setup


void loop() {

  unsigned long start = micros ();

  // read from IR diodes

  int whichLED = 0;
  for (byte master = 0; master < MUX_CHIPS; master++)
    {
    // select appropriate slave on the master MUX
    digitalWrite(M_S2, (master & 0b100) ? HIGH : LOW);
    digitalWrite(M_S1, (master & 0b010) ? HIGH : LOW);    
    digitalWrite(M_S0, (master & 0b001) ? HIGH : LOW);

    // take each slave reading
    for (byte slave = 0; slave < LEDS_PER_CHIP; slave++)
      {
      digitalWrite(S_S3, (slave & 0b1000) ? HIGH : LOW);       
      digitalWrite(S_S2, (slave & 0b0100) ? HIGH : LOW);
      digitalWrite(S_S1, (slave & 0b0010) ? HIGH : LOW);
      digitalWrite(S_S0, (slave & 0b0001) ? HIGH : LOW);

      // store this reading
      recv_state [whichLED++] = analogRead (INPUT_FROM_MUX);

      } // end of for each slave
    } // end of for each master

  // display results here ...

} // of loop

The exact code above took 5148 µs (around 5 ms) to execute the loop once on my Uno. If we remove the speedup of the ADC (analogRead) it takes 13252 µs (13 ms).

To allow for your exact wiring you need to ignore the low-order bit on the master MUX because you are not using input A0_13 in which case you need to add 2 to the master value in the outer loop:

    // select appropriate slave on the master MUX
    byte master_plus_2 = master + 2;  // allow for not using input A0
    digitalWrite(M_S2, (master_plus_2 & 0b100) ? HIGH : LOW);
    digitalWrite(M_S1, (master_plus_2 & 0b010) ? HIGH : LOW);    
    digitalWrite(M_S0, (master_plus_2 & 0b001) ? HIGH : LOW);
  • Thank you @nickGammon. Appreciate the mux explanation. Very helpful. Mostly having a hard time visualizing the code, especially how to write correctly the 7 binary digital write commands. Is it possible for you to correct what I've written above, so I might be able to see the pattern? And yes, keeping this art installation very simple for now, at least until I can understand better – Curtis Bracher May 3 '18 at 15:56
  • Look like you already have worked it out. You may want to change the ADC clock rate (see here). A prescaler of 32 will make the analogReads take only 26 µs rather than 104 µs which will improve responsiveness when you are reading 96 of them. – Nick Gammon May 3 '18 at 21:38
  • I updated my answer anyway to include the code I suggested (an inner loop and an outer loop). That will make it easier to change if you leave A0 disconnected on the master MUX. – Nick Gammon May 3 '18 at 22:12
  • This is sooo far out of my league :-) Regardless, thank you very much for the lessons @nickGammon. The explanations + code are so helpful. I will try both codes, the simpler and this double loop/pre-scaler after I solder up the circuit. I can still change the 4051 to use A0 - A5 as suggested and try to get that pre-scaler in there for speed. Especially considering that the 96 values, in turn, need to be pushed out through the TLC5940's as well – Curtis Bracher May 4 '18 at 2:27

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.