Go Down

Topic: Ghosting effect with 4x4x4 LED-Cube (Read 2364 times) previous topic - next topic

Just_some_dude

Aug 04, 2019, 02:53 am Last Edit: Aug 04, 2019, 03:57 am by Just_some_dude
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..




Code: [Select]
#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! :)

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.

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.  :smiley-lol:

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.  :smiley-roll:

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

Rintin

Code: [Select]

//"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?

Just_some_dude

Thanks for your quick responses!

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?

Code: [Select]

    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);
    }





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.  :smiley-lol:

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.  :smiley-roll:

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

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?




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?

Grumpy_Mike

Quote
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.

Rintin

Please try this:
Code: [Select]


//"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);


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

Just_some_dude

#8
Aug 04, 2019, 05:19 pm Last Edit: Aug 04, 2019, 05:25 pm by Just_some_dude
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):

Code: [Select]


//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:

Code: [Select]
//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:

Code: [Select]
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.


--------------------------------

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:

Code: [Select]
#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.
--------------------------------------

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?

PaulRB

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.

Just_some_dude

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

PaulRB

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

Just_some_dude

#12
Aug 06, 2019, 04:28 pm Last Edit: Aug 06, 2019, 06:20 pm by Just_some_dude
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.

Code: [Select]



#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.

PaulRB


PaulRB

Quote
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.

Go Up