Arduino Uno shift in + out + multiplexing

Hello,
I have created a muliplexer for 8 rgb led's in that way:

  • long leg of led's connected to 5v through a transistor(leds are cmmon anode).
  • the r, g, and b ports of the leds are connected together and all to pwm pins on arduino (9, 10, 11).
  • the transistors' base is connected to 595 ic for shift out operations.

The code just turns 1 transistor on at a time, and sends pwm signals through the "bus", and only the led that is connected to 5v (through the transistor with base = high) is turned on by the signal.

When I am doing it fast enough our eye can't tell that each led is powered on seperately, and we see all the leds on.

The problem comes when I am trying to use the 74hc4021b ic, I can't read the values properly for some reason, and if I would be able to do so, the loop is still too slow and the leds are lighting too slow to trick our eyes.

here is the code:

int outLatchPin = 12;
int outClockPin = 13;
int outDataPin = 8;

int ledRed = 9;
int ledGreen = 10;
int ledBlue = 11;

int inLatchPin = 3;
int inDataPin = 4;
int inClockPin = 2;

int dataColor[3][8] = {
  {255,   0, 255,   0, 255,   0, 255,   0},
  {  0, 255, 255,   0,   0, 255, 255,   0},
  {  0,   0,   0, 255, 255, 255, 255,   0}
};

byte switchVar1 = 72;

void setup() {
  Serial.begin(9600);
  
  pinMode(outLatchPin, OUTPUT);
  pinMode(outClockPin, OUTPUT);
  pinMode(outDataPin, OUTPUT);

  pinMode(ledRed, OUTPUT);
  pinMode(ledGreen, OUTPUT);
  pinMode(ledBlue, OUTPUT);

  pinMode(inLatchPin, OUTPUT);
  pinMode(inClockPin, OUTPUT); 
  pinMode(inDataPin, INPUT);
}

int counter = 0;

void loop() {
  //shift out
  digitalWrite(outLatchPin, LOW);
  shiftOut(outDataPin, outClockPin, MSBFIRST, (int)(1 << counter));
  digitalWrite(outLatchPin, HIGH);

  analogWrite(ledRed, 255 - dataColor[0][counter]);
  analogWrite(ledGreen, 255 - dataColor[1][counter]);
  analogWrite(ledBlue, 255 - dataColor[2][counter]);

  //shift in
  digitalWrite(inLatchPin, HIGH);
  digitalWrite(inLatchPin, LOW);

  switchVar1 = shiftIn(inDataPin, inClockPin);
  Serial.println(switchVar1, BIN);
  Serial.println("-------------------");

  counter++;
  if(counter > 7) counter = 0;
}

byte shiftIn(int myDataPin, int myClockPin) { 
  int i;
  int temp = 0;
  int pinState;
  byte myDataIn = 0;

  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, INPUT);

  for (i=7; i>=0; i--) {
    digitalWrite(myClockPin, 0);
    temp = digitalRead(myDataPin);
    if (temp) {
      pinState = 1;
      myDataIn = myDataIn | (1 << i);
    }
    else {
      pinState = 0;
    }

    digitalWrite(myClockPin, 1);

  }
  return myDataIn;
}

Do you know about a way that I can get this fast motion?

Thank you for ANY help,
Arad

aradarbel10:

  • long leg of led's connected to 5v through a transistor(leds are cmmon anode).
  • the r, g, and b ports of the leds are connected together and all to pwm pins on arduino (9, 10, 11).
  • the transistors' base is connected to 595 ic for shift out operations.

So you try to drive 8 leds by a single pin? Thrice?

Are they bright enough drawing 2.5 mA?

What current limiting resistors are you using?

What happens if you replace the analogWrite by dgitalWrite (or if you only send 0 or 255 via analogWrite)?

Whandall:
So you try to drive 8 leds by a single pin? Thrice?

Are they bright enough drawing 2.5 mA?

What current limiting resistors are you using?

What happens if you replace the analogWrite by dgitalWrite (or if you only send 0 or 255 via analogWrite)?

Hello Whandall,
To answer your questions:

  1. Not exactly, I am driving RGB LEDs, that means that I am using 3 pins - one for each color - tho I can use 3 of the shift registers to do everything with just 1 pin.

  2. yes, all the LEDs are as bright as hooking a single LED stright to 5v power supply.

  3. I am using 330ohm resistors after doing some measurements and using ohms law.

  4. Also when I am trying to replace the analogWrite with DigitalWrite I am getting the same results. notice that when I am commenting the shift in area of the main loop the code is fast enough and I have no issue.

As far as I understood all eight leds have red, green and connected among each other and to a PWM port,
but at most one of the transistors connects/selects one of them.

That's quite dangerous for the PWM ports of the Arduino, one bad value in the shift register could blow them.

Using a single resistor value for the three types of leds gives you a pretty uneven current distribution,
when I played with those I had to use different values for the three colors to have a chance to mix near to white.

If you only use one resistor per RGB led I'm pretty shure the colors will influence each other

Whandall:
Using a single resistor value for the three types of leds gives you a pretty uneven current distribution,
when I played with those I had to use different values for the three colors to have a chance to mix near to white.

If you only use one resistor per RGB led I'm pretty shure the colors will influence each other

No, I am using 3 different resistors for each RGB. and why is it that dangerous for the PWM ports?

The red led has a forward voltage of about 1.5V, with 330 Ohm that gives a current of around 10 mA.
Switching eight of them on at the same time draws 80 mA into the port.
It's less critical for the leds with the higher forward voltage, but probably also more than the allowed 20 mA.

Strange that you use the same resistors for each color.

Whandall:
The red led has a forward voltage of about 1.5V, with 330 Ohm that gives a current of around 10 mA.
Switching eight of them on at the same time draws 80 mA into the port.
It's less critical for the leds with the higher forward voltage, but probably also more than the allowed 20 mA.

Strange that you use the same resistors for each color.

Don't worry about my crappy electronic skills, but do you have any advice about my problem?

A decoder like an 74LS138 would guarantee that only one pin can be active at the same time.
The 74LS138 uses LOW as active.

Whandall:
A decoder like an 74LS138 would guarantee that only one pin can be active at the same time.
The 74LS138 uses LOW as active.

Ok, but do you know about a way to speed up the process, as I asked before?
I may use one of these 74LS138 in the future, because it does not looks like I have one of these laying around.

aradarbel10:
Ok, but do you know about a way to speed up the process, as I asked before?
I may use one of these 74LS138 in the future, because it does not looks like I have one of these laying around.

It may be that you need to slow down your clock. Assuming you are running your UNO at default clock speed of 16MHz and the UNO and the shift register at 5 volts, depending on the logic family and maker, you may be exceeding the maximum clock frequency of the shift register. This one has a max fCL of 3 MHz, while this one suggests a fCL of 6 MHz. The best way to determine the actual clock speed of your program would be to put an oscilloscope on your clock pin and measure the pulse width and/or frequency.

Perehama:
It may be that you need to slow down your clock. Assuming you are running your UNO at default clock speed of 16MHz and the UNO and the shift register at 5 volts, depending on the logic family and maker, you may be exceeding the maximum clock frequency of the shift register. This one has a max fCL of 3 MHz, while this one suggests a fCL of 6 MHz. The best way to determine the actual clock speed of your program would be to put an oscilloscope on your clock pin and measure the pulse width and/or frequency.

I don't have an oscilloscope, but the datasheet says 5MHz.

I have 2 questions about that:

  1. I am using sn74hc595 for shift out (fCL = 5MHz), and hc4021b for shift in (fCL = 5MHz), all of that with the UNO that runs on 16MHz. How do I determine the optimal speed for the arduino instead of the 16MHz?

  2. When I will have the frequency, how do I change it in the arduino uno? P.S. I am planning to upload the final project onto a standalone atmega328pu, and if I am correct it has a built in crystal. would it matter?

aradarbel10:
I don't have an oscilloscope, but the datasheet says 5MHz.

I have 2 questions about that:

  1. I am using sn74hc595 for shift out (fCL = 5MHz), and hc4021b for shift in (fCL = 5MHz), all of that with the UNO that runs on 16MHz. How do I determine the optimal speed for the arduino instead of the 16MHz?

  2. When I will have the frequency, how do I change it in the arduino uno? P.S. I am planning to upload the final project onto a standalone atmega328pu, and if I am correct it has a built in crystal. would it matter?

  1. without an oscilloscope, it may be a bit of trial and error. instead of using shiftIn(), copy the function from wiring_shift.c and paste it in as your own function, then you can edit it.
uint8_t slowerShiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
  uint8_t value = 0;
  uint8_t i;
  for (i = 0; i < 8; ++i) {
    digitalWrite(clockPin, HIGH);
    if (bitOrder == LSBFIRST) value |= digitalRead(dataPin) << i;
    else value |= digitalRead(dataPin) << (7 - i);
    delayMicroseconds(1);
    digitalWrite(clockPin, LOW);
    delayMicroseconds(1);
  }
  return value;
}
  1. since timing is very important, transferring to your ATmega328P-PU will mean you can drive the CPU at a more appropriate frequency. I would NOT use the internal crystal. Even when calibrated and under optimal conditions, which is unlikely, you lose the power to drive it at the perfect frequency for what you want it to do. For example, let's say on an oscilloscope you measure your current clock at 8MHz. Knowing that's half your CPU clock means you can drive your chip at 10MHz as programmed for optimal interaction with the shift register.
    The internal crystal is best suited for tasks that are not time critical, for tasks that are almost entirely internal to the microcontroller etc. It is not accurate enough for critical timekeeping such as pulse width modulation, UART, frequency counting etc.

You don't have to worry about shiftIn or shiftOut being too fast.

ShiftIn Clock 72 kHz

ShiftOut Clock 62 kHz

const byte siData = 38;
const byte siClock = 39;
const byte soData = 40;
const byte soClock = 41;

void setup() {
  pinMode(siData, INPUT_PULLUP);
  pinMode(siClock, OUTPUT);
  pinMode(soData, OUTPUT);
  pinMode(soClock, OUTPUT);
}
void loop() {
  byte test = shiftIn(siData, siClock, MSBFIRST);
  shiftOut(soData, soClock, MSBFIRST, test);
}

Measured on a Mega with an absurdly cheap 8 bit logic analyzer.

Whandall:
You don't have to worry about shiftIn or shiftOut being too fast.

I just ran this test myself on an ATmega328P-PU clocked at 16MHz. My timing is slightly different than pictured, but admittedly slower than I might have thought.

Maybe that is caused by the additional address byte that has to be used on calls.

I had no Uno on my desk so I made the measurement with a Mega.

If you use digitalWrite it's hard to get high frequencies.

I just realized what's going on here. The problem isn't the speed of the shift register. It's doing so many things while the display is off or on, I can't really tell what the latch pin is doing. A schematic connecting the pins of the Arduino to the shift registers would be helpful. From experience with LED displays, you have to turn on the LEDs for several microseconds. using a 4094 might be better since you can shift data in and out of the register, then commit it to the latch and work behind the scene to refresh the display. That is, shift out, OE high, strobe high, low, OE low, while counting microseconds, shift out again, then at the top of the count OE high, strobe high low, OE low and repeat....

aradarbel10:
Ok, but do you know about a way to speed up the process, as I asked before?
I may use one of these 74LS138 in the future, because it does not looks like I have one of these laying around.

Ok, to answer the OP, use direct port manipulation instead of the Arduino macros. (At your own risk.)

Perehama:
instead of the Arduino macros.

With macros you mean the functions digitalWrite and digitalRead ?

Why do you call them macros?

Whandall:
With macros you mean the functions digitalWrite and digitalRead ?

Why do you call them macros?

Yes, those... Not macros, functions... it was late in my workday.
although direct port manipulation is not as portable, I was able to write a simple test program to see if the speed increased. The clock is around 1.6 microseconds.

void setup() {
  DDRD |= (1 << DDD4);
  DDRD |= (1 << DDD5);
}
void loop() {
  fastShiftOut(255);
}
void fastShiftOut(byte val) {
  for (byte i = 0; i < 8; i++)  {
    if (val & (1 << i)) PORTD |= (1 << PD5);
    else PORTD &= ~(1 << PD5);
    PORTD |= (1 << PD4);
    PORTD &= ~(1 << PD4);
  }
}

Perehama:
YThe clock is around 1.6 microseconds.

The 625 kHz are still way below any critical speed.
Maybe on a Due the timing may become critical, but I doubt it.