8x8x8 LED Cube programming

Hey guys! I built an 8x8x8 cube that I’m driving with daisy chained 74HC595s. Tested manually by setting all the bytes to 11111111 and changing the layers activates each “plane” correctly.

The problem I’m having is with the code to have it automatically refresh and project the bytes stored in an 8x8 byte array. I have tried many things but nothing seems to work.

The way the thing is wired is like this:
Each shift register controls 8 LEDs (You can think of each register as “x” and the y is the individual bit for example, the byte in the first register would be B00000001 with the “1” being led 0,0,z)
The z coordinate is controlled by the ground layer that is turned on. This is contrled through the arduino digital pins 2-7 and then analog pins 0 and 1.

The following code doesn’t work at all.

The plan is to load 8 bytes into the registers, turn on the z=0 layer, then load the next 8 bytes and turn on the z=1 layer, etc over and over again.

#include <SPI.h>
#include <TimerOne.h>

const int latch = 10;
int refreshRate = 17;
byte cube[8][8];
volatile int z = 0;
void setup(){
   DDRC = B00011111;
   Timer1.initialize(refreshRate);
 Timer1.attachInterrupt(render);
 SPI.begin();
 for(int i=2;i<14;i++){
  pinMode(i,OUTPUT);
  }
  for(int i=0;i<8;i++){
    for(int j=0;j<8;j++){
    cube[i][j]=0;
    }
  }
  cube[0][0]=B11111111;
  cube[1][0]=B10000001;
  cube[2][0]=B10000001;
  cube[3][0]=B10000001;
  cube[4][0]=B10000001;
  cube[5][0]=B10000001;
  cube[6][0]=B10000001;
  cube[7][0]=B11111111;
}

void loop(){

}

void render(){
  
  for(int i=0;i<8;i++){
    digitalWrite(latch, LOW); //Loads the bytes one layer at a time (i hope  D: )
      for(int j=0;j<8;i++){
        SPI.transfer(cube[i][j]);
        if(i==0){
          PORTD = B00000100;
          PORTC = B00000000;}
        else if(i==1){
          PORTD = B00001000;
          PORTC = B00000000;}
        else if(i==2){
          PORTD = B00010000;
          PORTC = B00000000;
        }
        else if(i==3){
          PORTD = B00100000;
          PORTC = B00000000;}
        else if(i==4){
          PORTD = B01000000;
          PORTC = B00000000;}
        else if(i==5){
          PORTD = B10000000;
          PORTC = B00000000;}
        else if(i==6){
          PORTD = B00000000;
          PORTC = B10000001;}
        else if(i==7){
          PORTD = B00000000;
          PORTC = B10000010;}
      }
      digitalWrite(latch, HIGH);
     
  }    
   
  delay(15);
  
  
}

Thats my code with a manually entered pattern (I’m trying to render a box on the side of the cube)

Well, to start....

void loop(){

}

Looks like your program does exactly what you're telling it to do..... Nothing.

1ChicagoDave:
Looks like your program does exactly what you’re telling it to do…
Nothing.

He’s using interrupts, so for the time being loop isn’t needed.

The problem you have is the way you are using interrupts. Look at the code you have in the interrupt:

  for(int i=0;i<8;i++){
    digitalWrite(latch, LOW); //Loads the bytes one layer at a time (i hope  D: )
      for(int j=0;j<8;i++){

Inside every interrupt you count through all LEDs faster than you could ever see. You shouldn’t be counting through each layer using a for loop in the interrupt.

Change this line: (remove it’s corresponding ‘}’)

  for(int i=0;i<8;i++){

to this:

byte i = (nextLayer++)&0x7;

and add an global volatile variable:

volatile byte nextLayer = 0;

What this will do is display the set up the registers to display the next layer during an interrupt.


I would also suggest creating a 2x8 array and use that to store the PORTD and PORTC values:

const byte whichLayer[2][8] = {{0x04,0x08,0x10,0x20,0x40,0x80,0x00,0x00},{0x00,0x00,0x00,0x00,0x00,0x00,0x81,0x82}};

Then replace this lot:

        if(i==0){
          PORTD = B00000100;
          PORTC = B00000000;}
        else if(i==1){
          PORTD = B00001000;
          PORTC = B00000000;}
        else if(i==2){
          PORTD = B00010000;
          PORTC = B00000000;
        }
        else if(i==3){
          PORTD = B00100000;
          PORTC = B00000000;}
        else if(i==4){
          PORTD = B01000000;
          PORTC = B00000000;}
        else if(i==5){
          PORTD = B10000000;
          PORTC = B00000000;}
        else if(i==6){
          PORTD = B00000000;
          PORTC = B10000001;}
        else if(i==7){
          PORTD = B00000000;
          PORTC = B10000010;}

With just:

PORTC = whichLayer[1][i];
PORTD = whichLayer[0][i];

Also, you never set Port D as outputs in the setup, so you will never see any layer which uses that port.

Thanks! I cleaned up my code per your suggestions and it now looks like this (also commented parts on it so it makes more sense):

#include <SPI.h>
#include <TimerOne.h>

const int latch = 10;
int refreshRate = 17;

byte cube[8][8];
const byte whichLayer[2][8] = {{0x04,0x08,0x10,0x20,0x40,0x80,0x00,0x00},{0x00,0x00,0x00,0x00,0x00,0x00,0x81,0x82}};
volatile int z = 0;
volatile byte nextLayer = 0;

void setup(){
   DDRC = B00011111;
   DDRD = B11111100;//please never touch these values!!!
   Timer1.initialize(refreshRate);
 Timer1.attachInterrupt(render);
 SPI.begin();
 
  for(int i=0;i<8;i++){  //inicializes the byte array with 0
    for(int j=0;j<8;j++){
    cube[i][j]=0;
    }
  }
  cube[0][0]=B11111111;
  cube[1][0]=B10000001;
  cube[2][0]=B10000001;
  cube[3][0]=B10000001;
  cube[4][0]=B10000001;
  cube[5][0]=B10000001;
  cube[6][0]=B10000001;
  cube[7][0]=B11111111;
}

void loop(){
              //nothing is here due to interrupt routine. Animation code
              //code will be here later.
}

void render(){
  
  byte i = (nextLayer++)&0x7;
    digitalWrite(latch, LOW); //Loads the bytes one layer at a time (i hope  D: )
      for(int j=0;j<8;i++){
         SPI.transfer(cube[i][j]);
      }
      digitalWrite(latch, HIGH);  
  PORTC = whichLayer[1][i];
  PORTD = whichLayer[0][i];
  
  if (nextLayer = 7){nextLayer = 0;}
  
  delay(15);
  
  
}

I added a check to reset the nextLayer value to 0 again once it got to 7 (I’m not sure if this is necessary), however its still not doing anything. Turning on the transistors that control the layers manually doesn’t show the proper LEDs, which makes me believe that the bytes are not being loaded into the shift registers.

What you said, Tom, about displaying it all too fast makes sense so I will play around with that part of the code. Could you please explain what you do with the bitwise AND? I don’t really understand that at all.

byte i = (nextLayer++)&0x7;

Also, the transistors get no signal to turn on the layers. :frowning:

The bitwise AND basically resets the number to 0 if it is greater than 7 :)

An example: 7 in binary: 0b00000111

Lets say we have done 8 interrupts and nextLayer has the value 8. So what value does the variable i have?

i = 8 AND 7:

0b00001000
0b00000111 &
----------
0b00000000

so 'i' = 0.

It works for any value that nextLayer can take, but is more efficient than the if statement.


I've just noticed something else: Timer1.initialize(refreshRate);

The argument for the initialise function is the period between interrupts in 'microseconds'. You are giving it the value 17, so your cube is changing layer once every 17microseconds. You need to send 256bits to the shift registers every interrupt. The default SPI speed is 4MHz (4000000 bits per second), so it takes 64microseconds just to send the data. This means the timer will have overflowed again before render() has completed which could cause you problems.

You should set the refreshRate variable to a much large value.

Based on the fact

Mh.. I had thought that the values in in Milliseconds... Looking at the library again and you're right.

I’m a idiot. :slight_smile:

in for(int j=0;j<8;j++) I had the j++ as i++, thats why it never loaded the array.

It now loads :smiley:

The only problem now is that the top two layers are switched… no clue why.

Ostrianiel: The only problem now is that the top two layers are switched... no clue why.

Presumably something in the way you have wired them. Assuming by layer you mean the one you select via transistor, the problem can be fixed by simply changing the 'whichLayer' array.

Yeap. Its working perfectly now! Thanks!

could you show your final coding?

About three years later?

Last seen 6 months ago? Could be a bit of an "ask". :astonished:

Hi All , I built an 8 x 8 x 8 LED cube and I also built a schematic to run the cube that I could understand , using the NPN2222 transistors to switch on and off the layers and The 74HC595 shift registers to control each bite that translates into bits for each individual LED. I have tried several codes off of the Internet to drive my LED cube but with no success I have also learned the fundamentals or the Arduino IDE code void loop set up and I do find it to be fascinating I have learned my DigitalWrite , FOR , While and If statements. And Arrays. But I keep coming back to the same question that keeps puzzling me that there must be a simpler and easier way to program an LED cube consisting 512 LEDs I do like the B0000-0000 method. But I have failed to find one nor can I figure one out in programming that will work on a cube of this size . I have had much success on a 4 x 4 x 4 cube using this method I am reaching out to the Arduino family on this problem. I am a Big fan of Kevin Darrah’s RGB cube which is my final goal I have looked over his schematic and it does appear to be doable with the several hundred MOSFETs and transistors that is needed for a cube that size :-) But first things first / I’m not looking for charity but I’m looking for help ,that will work . I learn from codes that work and from there I go on my own . I will take the code and revise it and then try it to see if it works. I would be grateful for any input on this particular electrical electronic eclectic project. Thank All Railroad Jim

Here's a library that works with many cube types: https://github.com/MajicDesigns/MD_Cubo

I also wrote a couple of blog articles in the past https://arduinoplusplus.wordpress.com/2015/08/13/device-independent-control-for-led-cubes/ https://arduinoplusplus.wordpress.com/2018/03/08/revisiting-device-independent-control-for-led-cubes/

Hope this helps.