Ghosting effect with 4x4x4 LED-Cube

4,7V-(0,7+0,9+0+2,6)V=0,5V ->does not really add up

I should have said it must add up. Voltage cannot disappear to nowhere.

With all 4 MOSFETS switched on, there will be 4 current paths for the current to get from the series resistor to ground. You have to measure the voltage drops along a single current path for it to add up.

My guess at this point is that a single layer is close to the 70mA limit for the shift register. When you switch on all 4 layers at once, the shift register is throttling the current down. But I would expect it to do that by dropping more voltage, witch does not seem to be happening.

Paul__B:
What code? The last code that I have seen you post generates only one static pattern, if that.

And it is awfully difficult to read. Double spaced makes it almost illegible. You need to use single bank lines only between functional units, double between functions to demarcate them. And your comments are too long and wordy.

I'm sorry for that, I'll try to avoid the double spacing in the future. My comments are long and wordy because I want to remember why I did something if I look back at my code in the future.

I was talking about the last code I posted. Dynamic patterns make it pretty hard to make out the ghosting effect, that's why I chose a static pattern as an example. It is not a big deal, but the fact it is still there bothers me.
Should I post my code again in a more readable way?

PaulRB:
I should have said it must add up. Voltage cannot disappear to nowhere.

With all 4 MOSFETS switched on, there will be 4 current paths for the current to get from the series resistor to ground. You have to measure the voltage drops along a single current path for it to add up.

My guess at this point is that a single layer is close to the 70mA limit for the shift register. When you switch on all 4 layers at once, the shift register is throttling the current down. But I would expect it to do that by dropping more voltage, witch does not seem to be happening.

That was probably my mistake. I averaged my measurements and then calculated the voltage drop in my last post. When measuring the voltage drops for each column independently, the difference between the voltage drop and the supply voltage is about 0,2V. I know it needs to add up, but this might just be a measuring error.

I measured the current by hooking up the multimeter to the common ground, meaning I measured the combined current of the whole circuit. Since I have 2 shift registers for driving the LEDs, one should not get more than about 30-35mA. So I don't think they are throttling.

Also, did someone find out why my code is not eliminating the ghosting effect despite using the outputenable on the shift registers?

Can't see a problem in your code causing that. But did expect you to connect OE of all 3 shift registers together, so maybe try that.

I tried it, but the effect remains.

Just_some_dude:
I was talking about the last code I posted. Dynamic patterns make it pretty hard to make out the ghosting effect, that's why I chose a static pattern as an example. It is not a big deal, but the fact it is still there bothers me.
Should I post my code again in a more readable way?

OK, so edited to a plausibly readable version and slightly adjusted, this was the last code you posted:

#include <SPI.h>
#define t 1                   //on-time of the LEDs
#define latchPin 10
#define outputenable 3

//Array with hexadecimal values to turn on the LED according to the index, "hex[x][y]"
byte hex[5][5] = {
  {0, 0, 0, 0, 0},                     // So that the indexes can start with 1
  {0x00, 0x01, 0x02, 0x04, 0x08},      // Row 1
  {0x00, 0x10, 0x20, 0x40, 0x80},      // Row 2
  {0x00, 0x01, 0x02, 0x04, 0x08},      // Row 3 = Row 1, avoiding if-statement in the "buffer_write" function
  {0x00, 0x10, 0x20, 0x40, 0x80},      // Row 4 = Row 2
};

//Array to buffer the current pattern, again one bigger than needed for easier use of indexes
byte buffer_pattern[5][5][5];

//Function to store the LED to be turned on to the buffer
void buffer_write(byte x, byte y, byte z) {
  buffer_pattern[x][y][z] = hex[x][y];
}

//Function to shift out the buffer. Each latch-cycle displays one level (8 LEDs for each register),
//meaning that the registers need to latch 4 times to display the whole cube one time
void buffer_shift() {
  for (byte level = 1; level < 5; level++) {              //z, iterating through all levels
    byte cache_8 = 0;                                   //needs to be reset with every iteration, storing final pattern for columns 1-8
    byte cache_16 = 0;                                  //columns 9-16
    //Column 1-8: "creating" byte for the first register by adding the bytes of each LED to get the final pattern
    for (byte row = 1; row < 3; row++) {                  //x, iterating through first 2 rows
      for (byte column = 1; column < 5; column++) {   //y, iterating through the 4 columns in each row
        cache_8 = cache_8 | buffer_pattern[row][column][level];
      }
    }
    //Column 8-16: Same procedure as before, now for the 2nd register
    for (byte row = 3; row < 5; row++) {                  //x, iterating through last 2 rows
      for (byte column = 1; column < 5; column++) {   //y, iterating through the 4 columns in each row
        cache_16 = cache_16 | buffer_pattern[row][column][level];
      }
    }
    shift(cache_8, cache_16 , level);             //shifting the pattern for the whole level, 4 times for the whole cube
    delay(t);                                     //delay, so that the LEDs are on for longer, makes them shine brighter
    //delayMicroseconds(t);
  }
}
//Clearing the buffer. Necessary because only 1s get filled into the buffer, but no 0s
//Meaning that existing 1s only get overwritten by another 1 if buffer does not get cleared
void buffer_clear() {
  for (byte x = 1; x < 5; x++) {
    for (byte y = 1; y < 5; y++) {
      for (byte z = 1; z < 5; z++) {
        buffer_pattern[x][y][z] = 0;
      }
    }
  }
}

//Shifting the pattern from function "buffer_shift"
void shift(byte cache_8, byte cache_16, byte level) {
  digitalWrite(outputenable, HIGH);
  digitalWrite(latchPin, LOW);
  SPI.transfer(hex[1][level]);
  SPI.transfer(cache_16);
  SPI.transfer(cache_8);
  digitalWrite(latchPin, HIGH);
  digitalWrite(outputenable, LOW);
}


void setup() {
  pinMode(outputenable, OUTPUT);
  digitalWrite(outputenable, HIGH);                           //Disabling output, preventing whole cube from lighting up on startup
  SPI.begin();
  SPI.beginTransaction(SPISettings(800000, MSBFIRST, SPI_MODE0));
  pinMode(latchPin, OUTPUT);
  randomSeed(analogRead(0));
  //Clearing registers to be sure everything is off
  buffer_clear();
  buffer_shift();                                             //Clearing registers
  digitalWrite(outputenable, LOW);                            //Enabling output, startup complete
}

void loop() {
  //cube_outline();

  //moving_plane();

  //bouncing_plane();

  //raindrops();

  //diagonal line
  for (byte i = 1; i < 5; i++) {
    buffer_write(i, i, i);
  }
  buffer_shift();
  buffer_clear();
}

(Right, so I actually checked it in the IDE!)

So I haven't quite figured it all out, but explain to me, this is a static pattern, what exactly is this "ghosting" effect? Have a picture?

Maybe try lighting one led in the cube (while multiplexing), flashing on for one for 1s, off for 1s. Do you see ghosting then? If so, which led(s)? Always in the same led ghosting? Then try other single leds. Do you always see ghosting? If so, see if you can figure out the relationship between the led being flashed and the led that is ghosting. There is probably some pattern to be found.

Paul__B:
OK, so edited to a plausibly readable version, this was the last code you posted:

Thank you! Since you asked: I started the indexes with 1 because it made more sense to me at the time. I just didn't bother to change it since I don't think it is a huge issue.

Here you can see one column. The top LED is supposed to be on, the two below shouldn't. It is not that bright actually, the camera enhanced it. The third LED from the top is only visible in a dark room.

Here you can see the diagonal line. There are a lot of reflections, because I did not think of painting the LEDs from below. But you can also see the ghosting LEDs below the diagonal LEDs.

In words: I try to light (1,1,1), (2,2,2), (3,3,3),(4,4,4), but I also see a weak light for (1,1,4), (2,2,1), (3,3,2), (4,4,3) and a very weak light in (1,1,3), (3,3,1), (3,3,4), (4,4,2).

Projected into 2D it would look like this (Edit: The picture describes a side-on view):

2D representation.JPG

2D representation.JPG

PaulRB:
Maybe try lighting one led in the cube (while multiplexing), flashing on for one for 1s, off for 1s. Do you see ghosting then? If so, which led(s)? Always in the same led ghosting? Then try other single leds. Do you always see ghosting? If so, see if you can figure out the relationship between the led being flashed and the led that is ghosting. There is probably some pattern to be found.

Ok I tried it with all 64 LEDs and got the same result for each one: When flashing one LED for one second, I see ghosting in all LEDs in that column. I made sure it's no reflection.
I then turned up the delay time after shifting out the bytes so that the desired LED actually stayed on for half a second. I noticed that the other LEDs were flashing very shortly when turning the desired LED on. After the inital flash they stayed off until the next time the desired LED was turned on again.

Projected into 2D it would look like this:

2D protection from what point of view? Top-down, side-on? Are the ghosts always in the same column as a led which is on?

If so, it sounds like the MOSFETs are not switching off quickly. But the '595 should be capable of doing this quickly. How long are the wires between the '595 and the MOSFETs?

PaulRB:
2D protection from what point of view? Top-down, side-on? Are the ghosts always in the same column as a led which is on?

If so, it sounds like the MOSFETs are not switching off quickly. But the '595 should be capable of doing this quickly. How long are the wires between the '595 and the MOSFETs?

It's from a side-on view. Yes, the ghosts are always in the same column. The cables are 10cm jumper wires. At the moment everything is still on a breadboard, if that matters.

PaulRB:
If so, it sounds like the MOSFETs are not switching off quickly. But the '595 should be capable of doing this quickly.

And for that very reason, you do not want to use ~OE on that shift register.

PaulRB:
How long are the wires between the '595 and the MOSFETs?

Hmmm, wouldn't have thought that would be significant. :astonished:

Still cannot see the problem either. Still looking!

The 100 µF capacitor on each register is unnecessary, the 0.1 µF is plenty. :astonished:

And for that very reason, you do not want to use ~OE on that shift register.

Yes, of course! The MOSFET gates need to discharge through the 595 outputs. @Just_some_dude forget I suggested that, connect the OE of the third 595 back to ground.

But the ghosting was present before that change. The change should have made matters worse, but it didn't.

Maybe try 10K between the MOSFET gates and ground?

That appears to already be in the schematic, FWIW.

While it obviously is not as I would have done it myself and the code seems rather convoluted, I cannot see the basis for the ghosting yet it must be in there somewhere! :astonished:

By the way, try the code exactly as I last posted it. :grinning:

Paul__B:
The 100 µF capacitor on each register is unnecessary, the 0.1 µF is plenty. :astonished:

Is there a rule of thumb for that? I read somewhere that you should put a smaller capacitor with 0.01µF-0.1µF as close as possible to the chip, and one bigger with 10µF-100µF in the proximity. At least for every other chip, but better for every chip. Actually the capacitors I am using are only 0.068µF, but I forgot to update my schematic. Sorry for that.

PaulRB:
@Just_some_dude forget I suggested that, connect the OE of the third 595 back to ground.

But the ghosting was present before that change. The change should have made matters worse, but it didn't.

Maybe try 10K between the MOSFET gates and ground?

Yes, it didn't make it worse. I connected it back to ground.

The MOSFETs are already in place as @Paul__B mentioned.

Edit: meant to say: The pull-downs on the MOSFET gates [...].

Paul__B:
While it obviously is not as I would have done it myself and the code seems rather convoluted, I cannot see the basis for the ghosting yet it must be in there somewhere! :astonished:

Ahhh it hurts, I tried my best :smiley:
But it's my first real project, so I think it can't be perfect. Do you have any suggestions for improvement? I think you are especially not impressed by my code, while I'm just glad that I got it running :smiley:

Paul__B:
By the way, try the code exactly as I last posted it. :grinning:

I did, unfortunately same result.

Just_some_dude:
The MOSFETs are already in place as @Paul__B mentioned.

I don't understand. I was trying to suggest some 10K pull-downs on the MOSFET gates.

Could you post a picture of the breadboard to show the actual wiring?

PaulRB:
I don't understand. I was trying to suggest some 10K pull-downs on the MOSFET gates.

Sorry, I meant to say the pull-downs on the MOSFET gates are already in place.

david_2018:
Could you post a picture of the breadboard to show the actual wiring?

Well...it is a huge mess, and I would rather not show it :smiley:
It's really hard to see on a picture. But I attached some pictures.
The little breadboard is not in use for the code that is discussed right now.

In your buffer_shift() function, try turning off all the LEDs and the MOSFETs before writing out the new values. It could be that the shift registers directly driving the LEDs are switching faster than the MOSFETs, causing the wrong LEDs to very briefly turn ON before the MOSFET has had time to switch to the next level.

void buffer_shift() {
  for (byte level = 1; level < 5; level++) {              //z, iterating through all levels
    byte cache_8 = 0;                                   //needs to be reset with every iteration, storing final pattern for columns 1-8
    byte cache_16 = 0;                                  //columns 9-16
    //Column 1-8: "creating" byte for the first register by adding the bytes of each LED to get the final pattern
    for (byte row = 1; row < 3; row++) {                  //x, iterating through first 2 rows
      for (byte column = 1; column < 5; column++) {   //y, iterating through the 4 columns in each row
        cache_8 = cache_8 | buffer_pattern[row][column][level];
      }
    }
    //Column 8-16: Same procedure as before, now for the 2nd register
    for (byte row = 3; row < 5; row++) {                  //x, iterating through last 2 rows
      for (byte column = 1; column < 5; column++) {   //y, iterating through the 4 columns in each row
        cache_16 = cache_16 | buffer_pattern[row][column][level];
      }
    }
    shift(0,0,0);  //<<<< add this line to turn off all LEDs before writing out new pattern >>>>
    shift(cache_8, cache_16 , level);             //shifting the pattern for the whole level, 4 times for the whole cube
    delay(t);                                     //delay, so that the LEDs are on for longer, makes them shine brighter
    //delayMicroseconds(t);
  }
}

20190810_192641.jpg

20190810_192723.jpg

Regarding the capacitors, the point is that the smaller ceramics are suppressing impulses caused by the switching of the CMOS chips themselves which are of significance because even short lengths of PCB track conveying power to the chips will have significant inductance. The larger capacitors are more to absorb surges from bulk switching events of the whole assembly and compensating for resistance in the wiring rather than inductance, thus only one per whole section is really required.

Now we are reminded that you do not have a PCB, but a breadboard. In fact, you have not placed the 0.068µF capacitors across the chips. Instead, you have placed them across the power busses which is useful in itself, but not as effective and the electrolytics are effectively all in the one place anyway.

The ceramics - need long wires for the breadboard - really should be bridged directly across the chips themselves, pins 8 and 16. On a PCB this is generally done by running the Vcc track under the chip to the ground end and placing the capacitor immediately at the end of the chip. Whether it will make a difference in your breadboarded setup I don't know; I still suspect there is something in your code we haven't quite twigged to and it will be interesting to see what David's suggestion achieves.