5x5x5 LED Cube project finished

Well I just finished up on my latest project, a Blue 5x5x5 LED cube. Had a lot of fun building this prototype, both on the hardware and software side. A couple of fresh pictures:

http://img532.imageshack.us/img532/12/img4629y.jpg

http://img39.imageshack.us/img39/6060/img4631l.jpg

And part of the code, I don't think it will all fit in one posting:

/*
 Hardware design based on:
 http://picprojects.org.uk/projects/lc/index.htm#Schematic  This is a PIC based project but the Arduino 
 has all it takes to drive the display. After building the basic driver board using jumpers to an Arduino
 board, I later added a AVR 328 socket to the driver board and moved the processor to it for standalone operation.
 I choose to build the LED cube and driver as seperate assemblies, coupled with a 40 pin IDC ribbon cable. That way 
 I can rebuild the cube someday if I wish, using different support/soldering method.
 
Software:
The MsTimer2 libaray (http://www.arduino.cc/playground/Main/MsTimer2) is used to generate a 2msec timer interrupt, 
so the five row cube is completly refreshed every 10msec. faster then the eye can detect flickering. I have yet to test
using a pattern script from EEPROM memory as of this time. I know how to load a hex file to the AVR chip, but I haven't
figured out how I might create the hex file! A cube editor would be very useful, but well beyond my software skills.

The two column driver chips connected in series are Allegro A6276. I got mine from Newark Electronics. It combines the function
of a 32 bit serial in, parallel output shift register, plus 32 constant current drive output pins.  The data output pin's 
constant current sink value is programmable (set by a fixed or variable resistor), so 125 external current limiting resistors are not
required, and the lamp brightness can be easily adjustable, nice! I used two 650 ohm resistors I had on hand. The two series 
connected 16 bit shift registers allows for a total of 32 bits of column data. For a 5X5X5 cube only 25 bits (0-24) are wired
and used, bits 25-31 could be used for future additions. Maybe drive a 5x7 numeric display at same time as the cube?
The five NPN row driver transistors (I used TIP29C) allow the Arduino to update each 25 light row (or level) for 2msec after
the 25 column data bits have been shifted out to the column driver chips and latched into the current driver outputs.

http://www.allegromicro.com/en/Products/Part_Numbers/6276/6276.pdf


                           April 14, 2010 by retrolefty @ http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl

*/

#include <MsTimer2.h>     // used to set scanning rate
#include <avr/eeprom.h>   // used to read pattern data from on chip eeprom


const byte potInput = 0;    // used to get optional variable cube update interval time via analog input pin 0, not implemented of tested.
const byte clockPin = 11;   // shift reg clock pin
const byte dataPin = 12;    // shift reg data pin
const byte latchPin = 13;   // shift reg latch pin, note that the Arduino built in pin 13 LED will show this latch activity, but very dim.
const byte row0Pin =2;      // 5 cube row driver output pins to drive 5 row driver transistors
const byte row1Pin =3;
const byte row2Pin =4;
const byte row3Pin =5;
const byte row4Pin =6;
const int  timedelay = 200;  // how fast to update cube with a new data pattern (milliseconds)
const byte updatetype = 0;   // 0 = fixed update interval using timedelay constant value 
                             // 1 = variable update interval using analog input pin 0 voltage
                             // 2 = time interval obtained by high 7 hits in active_display[4], from EEPROM script
                             
                          
const unsigned long all_off_display[5] = {0,0,0,0,0};  // all LEDs off pattern
const unsigned long all_on_display[5]  = {0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff}; // all LEDs on pattern
const unsigned long alt_on_display[5]  = {0xaaaaaaaa,0xaaaaaaaa,0xaaaaaaaa,0xaaaaaaaa,0xaaaaaaaa}; // alternating LED pattern
const unsigned long alt_off_display[5] = {0x55555555,0x55555555,0x55555555,0x55555555,0x55555555}; // opposite alt LED pattern

unsigned long patternDelay;
volatile byte active_row = 0;  // represents the active row of five vertical rows of the cube, 0-4 = bottom to top rows
volatile unsigned long active_display[5] = {0,0,0,0,0}; // holds 5 rows of 25 leds (125 LEDs/bits) to update cube
unsigned int address = 0;      // EEPROM starting address, mega168 holds 128 patterns, mega328 holds 256, mega1280 holds 1,024 patterns
byte Pattern = 2;              // data pattern to send to cube, 0= blink all LEDs on/off, 1 = alternating LEDs on/off,
                               // 2 = random LEDs pattern, 3 = run a script pattern from preloaded EEPROM data (not tested yet)

Lefty

And the rest of the code:

void setup() {
  
  // setup 5 output pins to drive cube row driver transistors
  pinMode(row0Pin, OUTPUT);   // setup and turn off all 5 row driver transistors
  digitalWrite(row0Pin, LOW);
  pinMode(row1Pin, OUTPUT);
  digitalWrite(row1Pin, LOW);
  pinMode(row2Pin, OUTPUT);
  digitalWrite(row2Pin, LOW);
  pinMode(row3Pin, OUTPUT);
  digitalWrite(row3Pin, LOW);
  pinMode(row4Pin, OUTPUT);
  digitalWrite(row4Pin, LOW);
  
  // setup and turn off 3 shift register control pins
  pinMode(clockPin, OUTPUT);  
  digitalWrite(clockPin, LOW);
  pinMode(dataPin, OUTPUT);
  digitalWrite(dataPin,  LOW);
  pinMode(latchPin, OUTPUT);
  digitalWrite(latchPin, LOW);
  
  // four input pins used to select pattern update type by jumper ground clip on driver/processor board
  pinMode(7, INPUT);                  
  digitalWrite(7, HIGH);  //jumper will select pattern 0 on/off
  pinMode(8, INPUT);           
  digitalWrite(8, HIGH);  //jumper will select pattern 1 alternating on/off
  pinMode(9, INPUT);           
  digitalWrite(9, HIGH);  //jumper will select pattern 2 random pattern
  pinMode(10, INPUT);           
  digitalWrite(10, HIGH); //jumper will select pattern 3 EEPROM script pattern


   
    
  for (byte j = 0; j < 32; j++) {          // set shift reg to all columns outputs off, in case shift reg doesn't power-up cleared 
       digitalWrite (dataPin, LOW);        // probably not needed, but why not start in a known all off condition.
       digitalWrite(clockPin, HIGH);       // clock 32 low bits to shift regester
       digitalWrite(clockPin, LOW);
     } 
  digitalWrite(latchPin, HIGH);            //latch all zeros to 25 column output pins
  digitalWrite(latchPin, LOW);
  
  randomSeed(analogRead(1));               // used for random LED pattern mode for cube

  
  Serial.begin(57600);
  Serial.println ("5X5X5 Cube Ready");     // signal initalization done
 

  MsTimer2::set(2, refresh_display);      // 2ms period, generates a 2msec interrupt to the ISR routine
  MsTimer2::start(); 
   
}  // End of setup


void loop() {
  
  // select pattern to use  
  if(!digitalRead(7)){
    Pattern = 0;  // on/off
    }    
  if(!digitalRead(8)){
    Pattern = 1;  // alternate lamps on/off
    } 
  if(!digitalRead(9)){
    Pattern = 2;  //random lamps
    }
   if(!digitalRead(10)){
    Pattern = 3;  // get script pattern from eprom
    }
 
 // phase one pattern loading    
  noInterrupts();                            // we want new pattern data to transfer 'atomic' so as not to effect display

  for (byte i = 0; i <= 4; i++) {
      if (Pattern == 0) {
        active_display[i] = all_on_display[i];
      }
      if (Pattern == 1) { 
        active_display[i] = alt_on_display[i];
      }
      if (Pattern == 2) {
        active_display[i] = random(0x02000000);
      }
      if (Pattern == 3) {
        active_display[i] = eeprom_read_dword((uint32_t *)address);
        address = address + 4;
      }
  }
  interrupts();
  
  if (updatetype == 0) {
       patternDelay = timedelay;            // fixed cube update time via fixed constant
     }   
  if (updatetype == 1) {
       patternDelay = analogRead(potInput);
       if (patternDelay < 20) {
         patternDelay = 20;                 // variable time interval, clamp variable update time to 50hz minimum  
       }
  }
   if (updatetype == 2) {
     
     patternDelay = (((active_display[4] >> 25) +2)*10);    //high 7 bits of active_display[4] +2 X 10 msec
        // Serial.println (patternDelay);  // for debugging eeprom data as EEPROM scripting not tested yet.
     }   
   if (Pattern == 2) {
        patternDelay = random(100,200);  // added to give a littl random delay to make random pattern more uh random.
      }  
  delay(patternDelay);
   
   noInterrupts();

// phase two, for on/off, alt pattern usage, otherwise only one phase would have been required in main loop
  for (byte i = 0; i < 5; i++) {
      if (Pattern == 0) {
        active_display[i] = all_off_display[i];
      }
      if (Pattern == 1) { 
        active_display[i] = alt_off_display[i];
      }
      if (Pattern == 2) {
        active_display[i] = random();
      } 
      if (Pattern == 3) {
       active_display[i] = eeprom_read_dword((uint32_t *)address);
       address = address + 4;
      } 
  }
  
   interrupts();
   
   if (updatetype == 2) {
      // patternDelay = (active_display[4] >> 25) * 20;    //high 7 bits (0-127) X 20 msec
      patternDelay = (((active_display[4] >> 25) +2)*10);    //high 7 bits of active_display[4] +2 X 10 msec
      // Serial.println (patternDelay);  // for debugging eeprom data
     }
    
   if (Pattern == 2) {
        patternDelay = random(100,200);
      } 
      
   delay(patternDelay);
}

 // Start of Interrupt routine for driving a display cube row, all 5 rows (levels) are driven over a 10msec period (5 x 2msec)
void refresh_display() {
     
     unsigned long shiftdata = active_display[active_row]; // get row data for 25 leds
     unsigned long shiftmask = 1;

     for (byte i = 0; i < 32; i++) {                     // shift 32 bits (only 25 bits are wired up to cube) of data out for the 25 display columns
     
       if (shiftdata & shiftmask) {
         digitalWrite(dataPin, HIGH);
         }
         else {
           digitalWrite(dataPin, LOW);
           }
       
       digitalWrite(clockPin, HIGH);                     //  clock each bit into shift register
       digitalWrite(clockPin, LOW);
       
       shiftmask = shiftmask * 2;       //right shift bit mask one bit
     }
     
     if (active_row == 0) {
       digitalWrite(row4Pin, LOW);            // turn off row 4 driver if row 0 is now active, this is wrap around case
     }
     else { 
       digitalWrite((active_row + 1), LOW);  // turn off driver from prior row update interrupt
     }
    
     digitalWrite(latchPin, HIGH);           // turn on shift register output pins, column drivers
     digitalWrite(latchPin, LOW);
     digitalWrite((active_row + 2), HIGH);   // turn on active row driver transistor
     
   // Set next active row number for next timer interrrupt  
     if (active_row >= 4) {   
         active_row = 0;  // wraparound case
     }
     else { active_row++;  // increament row number for next interrupt cycle
     }
   
}

I haven't yet tested out running patterns from EEPROM memory. I know how to load hex files into eeprom memory, but I have yet figured out how to create the hex files. A "cube editor" sure would be an aid, but that is well beyond my software skills.
Lefty

PS: Today I tested a short script pattern from EEPROM and it works fine. Still manual coding in patterns into eeprom is a time consuming effort without some kind of light editor program to help out.

Nice!

Have you seen this video? Pretty awsome cube editor there!
Link to their webpage about it: eightCubed

I looked over that video and web site, very impressive. However it is based on a different concept I think. Their LED cube is full time tethered to the PC, and the PC sends real time updates for displaying. My design, as many are, is standalone, where the pattern is preloaded and executed without the need to be hooked up to a PC while displaying the display script.

Lefty

I think I will attempt this some time in the future!

I think I will attempt this some time in the future!

Please do, I think it's a good project, not overwelming in costs or complexity, but will add to your building and/or software skills.

Software was my main learning task with this project. I had 90% of the software done while waiting for the LEDs to come from Asia. Once they arrived I started in with soldering the cube up. That does take awhile and when I tired of soldering at times, I could just switch over to working of the software. E-bay has some great LED prices in bulk quanities.

Lefty

Hi guys,
I'm doing the same project with 6276 and a 8051 microproccessor
and i'm having trouble with the code specifically at sending data and
using it in the 6276.
So if anyone has any ideas or relevent codes for this please send them to me. (m5_e65@nana.co.il)
Thanx. :slight_smile: :slight_smile:

Please forgive me, i'm so confused. I know how to use a 595 8bit shift register to control 8 pins from 1 arduino pin. How are you controlling that many from so few registers?

Why are there no ShiftOut() commands being used?

And how can this code do anything if the var j isn't even in it?

for (byte j = 0; j < 32; j++) {          // set shift reg to all columns outputs off, in case shift reg doesn't power-up cleared
       digitalWrite (dataPin, LOW);        // probably not needed, but why not start in a known all off condition.
       digitalWrite(clockPin, HIGH);       // clock 32 low bits to shift regester
       digitalWrite(clockPin, LOW);
     }

And how can this code do anything if the var j isn't even in it?

But it is in 'it'. j is being used as a loop counter in the for statement. The j++ part is incremented each time after the three statements are performed for a total of 32 times.

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

Why are there no ShiftOut() commands being used?

Just because I wanted to do it this way. :wink: Mostly because it seems to me that ShiftOut() shifts out a single byte at a time and I wanted to do a 32 bit shift out. This seemed simpler in my mind, and hey it worked. :wink:

How are you controlling that many from so few registers?

Because the chip I'm using is a 16 bit serial register chip and two of them are wired in series and therefore in my code acts like a single 32 bit shift register.

Lefty

I have this blurry vision of a cube of 1m x 1m x 1m absolutely clear with such LED´s built in Acting as a 3D TV.

Or maybe a cube of gas with lasers activating a pinpoint region in any colour. A practical hologram

Or maybe a cube of gas with lasers activating a pinpoint region in any colour. A practical hologram

I got that same dream. Just do away with the gas-filled cube, just use regular air electrostatically biased and then a weak-ish laser focused at some point mid-air to exite the molecules there ::slight_smile: Better yet different wavelength lasers to excite different type molecules to get different colors, kind of like the aurora borealis. I mean, oxygen is.. what, green, nitrogen is red? Or is it the other way around, or something else? (too lazy to google atm). Just need to find blue somehow.. (preferably from normal air)..

Then a biig ultrasonic transducer array to make it tactile..

Sort of like this: Japanese Device Uses Laser Plasma to Display 3D Images in the Air Btw I suspect one should NOT look into that laser... so probably a bit impractical in the living room..

Forcefields and photons... :sunglasses:

I got that same dream.

Probably deserving of it's own posting, rather then tacked on the end of this one. ;D

Lefty

Probably deserving of it's own posting,

Uhm yes..sorry about that :smiley:

Sorry lefty ........just got to finish the green thought

hello retrolefty,

I was looking for a fun and educating project for programming a led cube and i studying this arduino for the first time and know how to go on and of with led light on arduino.

so my question is, what kind components do you use for that printboard. so to get led cube go in action with different kinds of patern lights. sorry for the english, cause i`m here in netherland so help me please..

or can you give me good start with this project

a lot of thanks!!

I based my project off a PIC based project from the web. They explain the design pretty well. My project board is nothing more then a simple 'stand-alone' Arduino with two 16 bit constant current shift registers and 5 transistors to drive the 5 levels.

Lefty

Would you perhaps post the code?, I'm trying to wrap my head around how a person would set up the data arrays for a certain pattern to stay on for a certain amount of time since it would have to refresh each layer several times with the same data it you wanted to hold a pattern for say 1 second, meaning 5 data sets (one for each layer) shifted out across 4-8 bit shift registers several times to appear on, if it is held for 10mS for each layer it would need to loop through the same set of 5 patterns 20 times, do you have to write the same 5 data sets over and over 20 times or is there a way to tell it to shift out layer 1 , then 2, then 3, then 4, then 5 and repeat fot 1 second.

Another question I some how got it in my head only one LED can be on at a time or they will not be uniform in their brightness, but upon rethinking this, if they are all hooked to a voltage regulated power source, and each column and each layer having current limiting resistors it shoudn't if I'm thinking straight affect this, I thougt for some reason only one led could be one a time, but that would mean 125 data sets for each full refresh and would eat up they memory very quickly

on the verge of understanding, I think

Carl Ross

P.S. you mention you used a pic, I hae no expedrience with pic, how hard is it to convert that code to Arduino compaticle code?

Would you perhaps post the code?, I'm trying to wrap my head around how a person would set up the data arrays for a certain pattern to stay on for a certain amount of time since it would have to refresh each layer several times with the same data it you wanted to hold a pattern for say 1 second, meaning 5 data sets (one for each layer) shifted out across 4-8 bit shift registers several times to appear on, if it is held for 10mS for each layer it would need to loop through the same set of 5 patterns 20 times, do you have to write the same 5 data sets over and over 20 times or is there a way to tell it to shift out layer 1 , then 2, then 3, then 4, then 5 and repeat fot 1 second.

The code is posted in the code section of the first two posting of thread. I talk a little about the timing in the remarks section of the code I think. And yes you have to keep sending data out to each layer continously even if the data is the same.

Another question I some how got it in my head only one LED can be on at a time or they will not be uniform in their brightness, but upon rethinking this, if they are all hooked to a voltage regulated power source, and each column and each layer having current limiting resistors it shoudn't if I'm thinking straight affect this, I thougt for some reason only one led could be one a time, but that would mean 125 data sets for each full refresh and would eat up they memory very quickly

Up to 25 leds max can be on at any instance, that is if the data for a given level (row) are all on, then when the driver for the row is enabled it will allow current for all 25 leds to flow at the same time. The reason this design is nice about even brightness between having 1 or 25 leds turned on in a row is that the shift registers used have special constant current outputs that forces the current to the correct value (set by a single resistor).

on the verge of understanding, I think

It's not too complex. The main loops job is to keep any new data patten loaded into 5 long (32 bit) words (thats enough to hold 125 bits which each represents a specific LED). Each word holds data for one layer or row. The interrupt routine (ISR) job is to take the data from those 5 words, one word at a time and shift them out for each row. The timer interrupt sets that basic row update rate of 2 millsecs, so each layer gets refreshed every 10 millisecs, 2 millisec X 5 layers.
Carl Ross

P.S. you mention you used a pic, I hae no expedrience with pic, how hard is it to convert that code to Arduino compaticle code?

No, I didn't mention using a PIC, I used a mega328 chip. The project site that I linked to is what I based my cube on, but I didn't use a PIC or the code they provided.

Let me know if any of this helped with your questions.

Lefty

I'm still taking in everything you said, but I think it has answered some nagging questions and pointed me toward understanding it better, I just need to read everything a dozen times or so to fully absorb it. :o

Thanks again,

Carl

yet another noob question, is it possible to use the PCB kit from pic projects and make a daughter board that holds an arduino, I have another kit coming from that eight cubed place and I'd like to do the same with that since I'd rather not have it attached to my laptop all the time. and I'd like to customize my own patterns. a bit off topic, but I'm new to arduino and throwing pic in the mix at thos point would be a recipe for disaster and shelving everything I've spent money on.

-carl