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
1 Answer 1
OK. First, how the multiplexer works. I have a page about multiplexers in which there is this example of the 74HC4051:
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 betterCurtis Bracher– Curtis Bracher2018年05月03日 15:56:30 +00:00Commented May 3, 2018 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.2018年05月03日 21:38:14 +00:00Commented May 3, 2018 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.2018年05月03日 22:12:27 +00:00Commented May 3, 2018 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 wellCurtis Bracher– Curtis Bracher2018年05月04日 02:27:40 +00:00Commented May 4, 2018 at 2:27
digitalWrite(M_S2, i & 0b1000000)
not 0b110000. You do an anologRead, but never do anything with the read value (IRstate).0
on the left of all mask.IRstate = recv_state[x];
. Maybe you meantrecv_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 thefor( x ...)
loop and addrecv_state[i] = IRstate;
anddsply_state[x] = recv_state[x];