Ghosting effect with 4x4x4 LED-Cube

Hi guys,

I have recently built a 4x4x4 LED-Cube and made a circuit according to the attached schematic. I am using an Arduino Nano, 3x 74HC595 Shift-Registers (2 for 16 columns, 1 for the MOSFETs) and 4x IRL540N MOSFETs (for switching the 4 levels).

My problem:

When turning some LEDs on, I can notice a short pulse in some other random LEDs. This only occurs when a LED is turned on, and does not occur while a LED is permanently on. Also, this does not happen with every LED (as far as I could observe it. Sometimes it is hard to see.)

Especially when multiplexing (I just turn them on and off real fast, is that already multiplexing?) I can notice this ghosting effect. The desired LEDs shine bright, while some others shine dimly.

Solving approaches I tried:

  • Checking for shorts between layers/columns (I can light each LED on its own, so I assume there are no shorts causing my problem)

  • Turning all LEDs and MOSFETs off and waiting for 1ms after lighting a LED (I read on another thread that this might help so that the turn-off time of the MOSFET isn't causing the problem).
    When I tried this, it reduced the amount of ghosting LEDs, but not all of them.

Here is my code. I tried to come up with it on my own, so it might not be very good and efficient (I am just getting into "programming" with Arduino), but I think it should do the job..

#include <SPI.h>




#define latchPin 10

byte hex[]={0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};      // 0000 0000, 0000 0001, 0000 0010, ... , 1000 0000





void shift(byte x_shift, byte y_shift, byte z_shift){

/*  Converts Coordinates into the bytes that have to be "send" to the shift registers
 *  "Sends" them afterwards  
 */


byte eight=0;               //For LEDs 1 to 8
byte sixteen=0;             //For LEDs 9 to 16



//Converting the coordinates

  switch (x_shift) {
  
    case 0: 
            eight=0;
            sixteen=0;
           break;
    case 1:
            eight=hex[y_shift];
            sixteen=0;
           break;
    case 2:
            eight=hex[y_shift+4];
            sixteen=0;
           break;
    case 3:
            eight=0;
            sixteen=hex[y_shift];
           break;
    case 4:
            eight=0;
            sixteen=hex[y_shift+4];
           break;
  }


//"Sends" the bytes to the shift registers. Starting with the register for the MOSFETs since it is last in the daisy chain.

  digitalWrite(latchPin,LOW);
  SPI.transfer(hex[z_shift]);                       
  digitalWrite(latchPin,HIGH);

  digitalWrite(latchPin,LOW);
  SPI.transfer(sixteen);          
  digitalWrite(latchPin,HIGH);
  
  digitalWrite(latchPin,LOW);
  SPI.transfer(eight);          
  digitalWrite(latchPin,HIGH);

}




void setup(){

  pinMode(latchPin, OUTPUT);

  SPI.begin(); 
  SPI.beginTransaction(SPISettings(800000,MSBFIRST,SPI_MODE0)); 

  
}





void loop(){


    //diagonal line
        
    for (byte i=1; i<5; i++){

      shift(i,i,i);
      delay(1);
      shift(0,0,0);
      delay(1);
    }
}

Thank you for taking your time, I am looking forward to your suggestions and solutions! :slight_smile:

4x4x4 LED-Cube_schematic.pdf (560 KB)

A couple of things.

  1. connect a 10K from each FET gate to ground. This will prevent it from briefly turning on when not driven.

  2. when changing the pattern then
    a) turn off all the FETs.
    b) then shift out the pattern and turn the appropriate FET on.

This ensures there is no chance for a ghost when the pattern changes.

4x4x4 LED-Cube_schematic.JPG

The only sensible way to drive a 4 by 4 by 4 monochrome cube is to use a MAX7219. One IC, a resistor and two capacitors does it all. :grinning:

But that is for a 4 by 4 by 4 monochrome cube. Larger ones would need a different approach, so presumably this is what you are intending at a later date. :roll_eyes:

Your schematic shows a bad blunder - you are feeding the Arduino by the "Vin" pin. :astonished:

Just_some_dude:

//"Sends" the bytes to the shift registers. Starting with the register for the MOSFETs since it is last in the daisy chain.

digitalWrite(latchPin,LOW);
 SPI.transfer(hex[z_shift]);                      
 digitalWrite(latchPin,HIGH);

digitalWrite(latchPin,LOW);
 SPI.transfer(sixteen);          
 digitalWrite(latchPin,HIGH);
 
 digitalWrite(latchPin,LOW);
 SPI.transfer(eight);          
 digitalWrite(latchPin,HIGH);

Why are you latching the outputs after every shift?
Does it help to latch only after the last shift?

Thanks for your quick responses!

Grumpy_Mike:
A couple of things.

  1. connect a 10K from each FET gate to ground. This will prevent it from briefly turning on when not driven.

  2. when changing the pattern then
    a) turn off all the FETs.
    b) then shift out the pattern and turn the appropriate FET on.

This ensures there is no chance for a ghost when the pattern changes.

  1. I tried this, but sadly it did not solve my problem. But I will keep the 10k resistors in my circuit, thanks for the advice.

  2. Isn't that what I am already doing?

    for (byte i=1; i<5; i++){

      shift(i,i,i);           //shifting everything out + turning the fets on
      delay(1);
      shift(0,0,0);        //turning everything, including the fets off
      delay(1);
    }

Paul__B:
The only sensible way to drive a 4 by 4 by 4 monochrome cube is to use a MAX7219. One IC, a resistor and two capacitors does it all. :grinning:

But that is for a 4 by 4 by 4 monochrome cube. Larger ones would need a different approach, so presumably this is what you are intending at a later date. :roll_eyes:

Your schematic shows a bad blunder - you are feeding the Arduino by the "Vin" pin. :astonished:

I am actually planning to make a 8x8x8 cube later on, but I wanted to start with a smaller one to know what problems I am gonna encounter. And since many other people used shift registers for that, I thought this would be the way to go. So I did it this way so that I can just scale it up later on.

Actually, I am planning to power the Arduino through a USB-Cable that I cut off, because I read somewhere that this would be better than powering it through Vin...but is it really that bad?

Rintin:
Why are you latching the outputs after every shift?
Does it help to latch only after the last shift?

I tried shifting "unsigned int" to the registers, because that should be exactly 16 bits, as far as my understanding goes. For some reason that did not work tho (first 8 LEDs lit correctly, last 8 had random patterns or did not light up at all). Therefore I assumed that I have to send byte for byte, each time latching once. Do you know a fix to that?

Therefore I assumed that I have to send byte for byte, each time latching once.

Simply don’t do it. That is what I was telling you as well. That is what causes ghosting.

If you tried it before and it didn’t work, post that code, you probably had an error in this.

Please try this:

//"Sends" the bytes to the shift registers. Starting with the register for the MOSFETs since it is last in the daisy chain.

  digitalWrite(latchPin,LOW);
  SPI.transfer(hex[z_shift]);                       

  SPI.transfer(sixteen);          

  SPI.transfer(eight);          
  digitalWrite(latchPin,HIGH);

Using code tags on first post, attaching a proper clear schematic, series resistors calculated to avoid overloading the '595's 70mA max current... One might think that you read the forum guide before posting. We don't often see this! +1 karma

Ok, I tried to shift with latching only once. For convenience I posted only the parts of the code that I changed (except for the setup):

//Sending

  digitalWrite(latchPin,LOW);
  SPI.transfer(hex[z_shift]);                       

  SPI.transfer(sixteen);          

  SPI.transfer(eight);          
  digitalWrite(latchPin,HIGH);


//and this for my diagonal line
        
    for (byte i=1; i<5; i++){

      shift(i,i,i);
      delay(1);
      shift(0,0,0);
      delay(1);
    }

This works a lot better already. But there are still 4 LEDs that are ghosting (very weak). Imagine I would freeze the circuit at a given time. Then I would have 2 LEDs on. First the one that is supposed to be on, and second the one in the same column, but with the level/z-position of the LED that was on before (At least that is what I think).

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

So I assume, I need to individually turn off the FETs before clearing the shift registers (would I need to clear them in this case at all?). I feel like that is what you guys tried to tell me all along..took me a bit to get it, sorry for that.

I just don't know how to do that properly, since I have them daisy chained. I thought about using the masterreset-pin of the third shift register:

//Sending

  
  digitalWrite(masterreset, HIGH);
  
  digitalWrite(latchPin,LOW);
  SPI.transfer(hex[z_shift]);                       
  SPI.transfer(sixteen);          
  SPI.transfer(eight);          
  digitalWrite(latchPin,HIGH);


    //diagonal line
        
   for (byte i=1; i<5; i++){

      shift(i,i,i);
      delay(1);

      digitalWrite(masterreset, LOW);
      shift(0,0,0);
      delay(1);
    }

This did not change anything though. I also tried the same code with the output-enable pin instead of the masterreset-pin, and I switched all the HIGH/LOW commands (only for the oe-pin). In that case the cube remained dark.

Then I tried controlling the FETs via Arduino Pins like this:

byte mosfet[]={0, 2, 3, 4, 5};          //Pins on the Arduino, the zero so my index can start with 1


//Sending

  for (int n=1; n<5; n++){

   digitalWrite(mosfet[n], LOW);
    
  }

  delay(1);             //seems like I do not even need this, hard to see if it makes a difference

  digitalWrite(latchPin,LOW);                       
  SPI.transfer(sixteen);          
  SPI.transfer(eight);          
  digitalWrite(latchPin,HIGH);

  digitalWrite(mosfet[z_shift], HIGH);
}


    //diagonal line
        
    for (byte i=1; i<5; i++){

      shift(i,i,i);
      delay(1);
      shift(0,0,0);
      delay(1);
    }

I am not sure if the ghosting effect is completely gone with this, but it is not noticeable for the human eye anymore. I still see a very, very weak hint of light, but this might as well be a reflection.
Problem is, that this is not the way I want to do it, since I need to control the FETs via a shift-register when building a 8x8x8 cube.


Grumpy_Mike:
If you tried it before and it didn’t work, post that code, you probably had an error in this.

What I tried before is the following:

#define t 1

unsigned int hex[]={0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000};

byte hex_z[]={0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x8};



void shift(byte xy_shift, byte z_shift){

//Sending

  digitalWrite(latchPin,LOW);
  SPI.transfer(hex_z[z_shift]);                       
  SPI.transfer(hex[xy_shift]);         
  digitalWrite(latchPin,HIGH);

    

//diagonal line
       

      shift(1, 1);
      delay(t);
      shift(0, 0);
      delay(t);

      shift(5, 2);
      delay(t);
      shift(0, 0);
      delay(t);

      shift(11, 3);
      delay(t);
      shift(0, 0);
      delay(t);

      shift(16, 4);
      delay(t);
      shift(0, 0);
      delay(t);

When uploading this code, some LEDs light up very dimly, but I can not see a pattern.

PaulRB:
Using code tags on first post, attaching a proper clear schematic, series resistors calculated to avoid overloading the '595's 70mA max current... One might think that you read the forum guide before posting. We don't often see this! +1 karma

Thank you! Yes, I actually read the forum guide and I came here quite often for other problems. But this time I could not find a solution for my problem on here, so I had to make my own post.

Actually I often overload the shift registers, because the Arduino does sometimes just light up the whole cube when starting it. They haven't given up on me yet fortunately. Is there anything I can do about that besides a manual switch for the shift register power line?

Yes, 74hc595 has an output enable pin. Connect that to an Arduino pin with a 10K pull-down (up?) and set the Arduino pin to OUTPUT/HIGH (LOW?) after data has been shifted out & latched.

Alternatively, use Arduino pins to control the layers instead of a '595. Put pull-downs on each pin. But not so convenient when you build your 8x8x8.

I tried that, but it did not change a thing, unfortunately :frowning:

Post your updated code & schematic and we will check it you did it correctly.

This is how I did it. Also changed my code from turning one LED at a time to 16 at a time, to increase brightness.

#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, basically the same, but eliminates the need for an 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 shift 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 shift 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 shift registers to be sure everything is off
    
  buffer_clear();                                             
  buffer_shift();                                             //Clearing shift 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();

   

}

I added a 10k Pullup-Resistor to the outputenable-pin, because the shift registers seem to turn every pin on when starting up, which in turn turns my whole cube on.

I have a question concerning the current consumption. I measured 40mA with 16 LEDs on. This is good, because it means my shift registers do not get overloaded, but it does not make any sense to me.

Assuming a forward voltage of 3V (blue LEDs), I calculate: ((5V-3V)/220Ohm)*16=145mA.

I then got curious and measured with 64 LEDs on and got 50mA. Across one LED I measured a voltage drop of 2,5V.

None of this makes sense to me..any explanation? I measured with an analog multimeter, if that matters.

Edit: The measurements were taken when the LEDs were permanently on and not multiplexed.

Across one LED I measured a voltage drop of 2,5V.

Is that drop the same across all LEDs or just one?

Check the supply voltage with one layer on. Is it 5V?

Check the voltage drop across the '595 between 5V and the outputs.

Check the voltage drop across the series resistors.

Check the voltage drop across the source & drain of the MOSFETs.

Added together, these voltages should match the supply voltage.

I noticed that I had some resistors with a higher resistance in my circuit, but I exchanged them now. I got the following measurements (again with an analog multimeter):

one Layer, 16 LEDs:

current (just the LEDs): 62,5mA

voltage drops:
-supply voltage (Arduino 5V to GND): 4,7V
-across 595's (5V to outputs): 0,7V
-across resistors (220 Ohm): 1V
-across mosfets (source to drain): 0V
-across LED (same for all LEDs): 2,9V

4,7V-(0,7+1+0+2,9)V=0,1V ->adds up, close enough

four layers, 64 LEDs:

current: 67,5mA

voltage drops:
-supply voltage: 4,7V
-across 595's: 0,7V
-across resistors: 0,9V
-across mosfets: 0V
-across LED: 2,6V

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

My issue in my last post was that I thought with 4 layers the current should be 4 times as high. But actually it should be pretty much the same, because the resistance in both cases is almost the same (a little lower with all 4 layers, therefore a higher current). Is that correct?

Also that means that the resistance of my circuit (with one layer) is:

(4,7V-2,9V)/(62,5mA/16)= 461 Ohm
"(supply voltage - LED forward voltage) / (current / number of LEDs) = resistance"

Subtracting the 220 Ohm from the resistor leaves my circuit with a resistance of 240 Ohm. That seems pretty bad to me. Is my math correct or am I missing something here?

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

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

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.

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.