How My 3x3x3 Cube Works

This post is for a complete break down on the brains behind my 3x3x3 Cube. I had looked at a few programs people had shared and all seemed overly complex, hence I decided to just write my own.

Two criteria I set out to meet: must be scale-able and easy to add custom animations. I believe I have achieved both. 

Full code can be found > CubeProgram

http://www.youtube.com/watch?v=cKF0bJtYMck

Now for how it all works.

Variable setup:

 cpp |  copy code |? 
01
const int CUBE = 3;
02
const int COLUMN = CUBE*CUBE;
03
const char POLARITY = 'C';
04
05
int L_ON;
06
int L_OFF;
07
int C_ON;
08
int C_OFF;
09
10
const int LED[] = {2, 3, 4, 5, 6, 7, 8, 9, 10};
11
const int LAYER[] = {11, 12, 13};
12
13
const int ARRAY_SIZE = 17;
14
const int FRAME[][CUBE+1] = {
15
  {146,146,146,300}, //Start Spin
16
  {84,84,84,300},
17
  {56,56,56,300},
18
  {273,273,273,300},
19
  {146,146,146,300},
20
  {84,84,84,300},
21
  {56,56,56,300},
22
  {273,273,273,300},
23
  {146,146,146,300},
24
  {84,84,84,300},
25
  {56,56,56,300},
26
  {273,273,273,300},
27
  {146,146,146,300},
28
  {84,84,84,300},
29
  {56,56,56,300},
30
  {273,273,273,300},
31
  {0,0,0,600}
32
};

I have shrunk the FRAME[] array to keep it short.

  • Line 1 sets the size of the cube. Helps with cube scale-ability
  • Line 2 calculates the number of columns for LED control
  • Line 3 tells the program how the cube is set up. Layers common Anode or Cathode
  • Line 5-8 is variable declaration for Layer and Column, on and off
  • Line 10 is the I/O port on the Arduino the columns are connected to. Important to be in order, LED 1 then 2 then 3 etc
  • Line 11 is the I/O port on the Arduino the layers are connected to. Bottom layer first moving up.
  • Line 13 is the number of frames in the FRAME[] array
  • Line 14 down is the frame setup for the animations

Before I get to the next section of code I will explain how the FRAME[] array for the animations work.

The frame, for a 3x3x3 cube, have four part. (If the cube is 4x4x4 the frame will have five parts)

1
{a, b, c, d},

  • a is the LEDs to light for the bottom layer
  • b is the LEDs to light for the middle layer
  • c is the LEDs to light for the top layer
  • d is the time to display the frame for (1000 equals 1 second)

To determine the value to put in place of a, b or c is very easy. Refer to the grid below.

1 = 1 2 = 2 3 = 4
4 = 8 5 = 16 6 = 32
7 = 64 8 = 128 9 = 256

From the table, if you want to turn on LED 1 on the bottom layer for half a second you would add the following item to the FRAME[] array:

1
{1, 0, 0, 500}

Easy enough. If you want to turn on the corner LEDs on the top and bottom layers for half a second you first calculate the LED value:

1
1 + 4 + 64 + 256 = 325

then you add the following item to the FRAME[] array:

1
{325, 0, 325, 500}

Then to create a complete animation, you calculate the value of the next frame, add it to the array, calculate the next one, add it to the array … etc.

Now all the variable, constants and array is setup we initialise the cube.

 cpp |  copy code |? 
01
void setup(){
02
  if (POLARITY=='A'){
03
    L_ON = 1;
04
    L_OFF = 0;
05
    C_ON = 0;
06
    C_OFF = 1;
07
  }else{
08
    L_ON = 0;
09
    L_OFF = 1;
10
    C_ON = 1;
11
    C_OFF = 0;
12
  }
13
  for (int x = 0; x < COLUMN; x++){
14
    pinMode(LED[x], OUTPUT);
15
    digitalWrite(LED[x], C_OFF);
16
  }
17
  for (int x = 0; x < CUBE; x++){
18
    pinMode(LAYER[x], OUTPUT);
19
    digitalWrite(LAYER[x], L_OFF);
20
  }
21
}

  • Line 2-11 sets the correct value for on and off depending on the cube setup. Common anode or cathode.
  • Line 13-16 loops through all the column pins and sets them to OUTPUT and turns it off
  • Line 17-20 loops through all the layer pins and sets them to OUTPUT and turns it off

The reason behind the ON/OFF set up is, for the LED to light up then Anode leg has to be taken HIGH and the Cathode leg has to be LOW. The variable allows for easy transition of the code from common Anode to Cathode with out having to go through all the code and change 1 to 0 and 0 to 1.

 
Now for where all the joy happens …

 cpp |  copy code |? 
01
void loop(){  
02
  for (int F = 0; F < ARRAY_SIZE; F++){  
03
    long START = millis();
04
    int DISP = FRAME[F][CUBE];    
05
    
06
    while (millis() < START + DISP){    
07
      for (int L = 0; L < CUBE; L++){        
08
        if (L == 0){
09
          digitalWrite(LAYER[CUBE-1], L_OFF);
10
        }else{
11
          digitalWrite(LAYER[L-1], L_OFF);
12
        }        
13
        digitalWrite(LAYER[L], L_ON);        
14
        for (int C = 0; C < COLUMN; C++){
15
          if (1 & (FRAME[F][L] >> C)){
16
            digitalWrite(LED[C], C_ON);
17
            digitalWrite(LED[C], C_OFF);
18
          }          
19
        }
20
      }
21
    }
22
  }
23
}

  • Line 2 begins the For loop which iterates through the frames. Current frame is the value of F
  • Line 3, 4 and 6 is the key for the Persistence of Vision (POV). Will explain in a second
  • Line 7 begins the For loop which iterates through the layers. Current layer being L
  • Line 8-12 ensures that only one layer is on at a time
  • Line 13 turns on current layer
  • Line 14  begins the For loop which iterates through the columns. Current column being C
  • Line 15 takes the value from the current frame, F, and layer, L in the FRAME[] array and using bitwise operation compares it to 1. I will explain this soon
  • Line 16, 17 turns on the current LED if it should be on

First major thing to explain, Line 3, 4 and 6. This is the key to keeping the POV effect because, it constantly repeats the same operation over all data until the time it started plus the delay time has been reached. The Arduino clock speed is 16MHz so in theory each LED that should be on gets turned on and off 592,592 times per second (probably a bit less then that, but you get the picture).

 
Next is how the number entered in the frame data is converted to LED on or off on Line 15. As I said above, it takes the value and compares it to 1 using bitwise operation. Bitwise basically treats all the data it deals with as binary.
The binary value for all LED in a layer on and off is:

1
111111111 = 511 ;//All on
2
000000000 = 0 ;//All off

The ‘>>’ operation shifts the bits C amount to the right. ie.

1
511 >> 3 = 000111111

By shifting the bits to the right and comparing to 1 is how to determine whether the current LED should be on or off.

The following is how the ‘&’ operation coupled with the ‘>>’ work. I will list how example above with the corner LEDs lit would look for bottom layer.

 cpp |  copy code |? 
01
325 >> 0 = 101000101
02
101000101 & 000000001 = 1
03
325 >> 1 = 010100010
04
010100010 & 000000001 = 0
05
325 >> 2 = 001010001
06
001010001 & 000000001 = 1
07
325 >> 3 = 000101000
08
000101000 & 000000001 = 0
09
325 >> 4 = 000010100
10
000010100 & 000000001 = 0
11
325 >> 5 = 000001010
12
000001010 & 000000001 = 0
13
325 >> 6 = 000000101
14
000000101 & 000000001 = 1
15
325 >> 7 = 000000010
16
000000010 & 000000001 = 0
17
325 >> 8 = 000000001
18
000000001 & 000000001 = 1

Hopefully the explanation was decent enough. Ask any questions below. I will upload the video of the cube in action using the who code linked at the beginning so you can compare the operation to the frame code.

A few handy links:

http://arduino.cc/en/Reference/BitwiseAnd

http://arduino.cc/en/Reference/Bitshift

Post navigation

Leave a Reply

Your email address will not be published. Required fields are marked *