Go Down

Topic: 4x4x4 LED Cube with ATtiny85 and MAX7219 (Read 3 times) previous topic - next topic

PaulRB

Mar 16, 2014, 04:21 pm Last Edit: Mar 16, 2014, 04:33 pm by PaulRB Reason: 1
Hi everyone, here's my take on that old classic, the 4x4x4 LED cube.

I believe my design must be one of the simplest around. There are very few components:

  • 64 x 5mm LEDs (preferably high brightness), any colour you like

  • 1 x ATtiny85, plus PCB socket. ATtiny45 would probably be OK too. Or you can leave off the ATtiny and drive the cube directly with almost any Arduino.

  • 1 x MAX7219 LED driver IC, plus PCB socket

  • 3 x 10K resistors

  • 2 x 0.1uF capacitors

  • 1 x 10uF capacitor

  • 1 x light-dependant resistor (LDR)

  • 1 x PCB. Stripboard or Tripad board is a possibility, but very fiddly. I will give single-sided Eagle PCB design files below. Making or getting a PCB made makes the cube far easier and far faster to construct.

  • 1 x battery box for 3 or 4 x AA cells, plus connector if needed. If using 4 cells, they must be rechargeables. 4 non-rechargeables would be too high a voltage and could damage the IC. Alternatively power with 5V from Arduino or other regulated source.



http://www.youtube.com/watch?v=PJ6e-DbKvzI

PaulRB

#1
Mar 16, 2014, 04:48 pm Last Edit: Mar 16, 2014, 04:54 pm by PaulRB Reason: 1
Here are images of the PCB - schematic and board layout, and the Eagle files:

PaulRB

#2
Mar 16, 2014, 05:14 pm Last Edit: Mar 16, 2014, 05:18 pm by PaulRB Reason: 1
This involves first making a "jig" by drilling 8 x 5mm holes in an off-cut of wood. I recommend using a piece of stripboard or perfboard as a template. The hole centres should be 0.8" apart, in a 2 x 4 pattern. Use a fine marker pen or propelling pencil to mark the wood through the stripboard. Then remove the stripboard and use a bradawl to mark the wood in the same spots. Then drill holes through with a 5mm bit. Finally, mark the surface of the wood with pencil to remind you where to bend the LED legs and which which direction to orient the flat on the side of each LED. See attached picture.

PaulRB

#3
Mar 16, 2014, 05:32 pm Last Edit: Mar 17, 2014, 11:07 am by PaulRB Reason: 1
First test 8 LEDs using breadboard and a series resistor (e.g. 200R).

Place 8 LEDs in the holes, taking care to orient the flat spot on the LED correctly. These flat spots indicate which of the 2 legs is the cathode. In this design, the anode legs form the vertical pillars and the cathodes form the horizontal "floors" in the structure. The flat spot should be in a diagonal, North-West orientation.

Then bend the cathode legs down flat in a circuit as in the 3rd picture. You will need to use small electronics pliers to bend the legs at the corners as shown in the 4th picture. Before soldering, double-check to ensure the LEDs are oriented correctly and there is no chance of a short-circuit between anodes and cathodes.

PaulRB

#4
Mar 16, 2014, 05:51 pm Last Edit: Mar 16, 2014, 05:58 pm by PaulRB Reason: 1
Solder the cathodes. Then fold the anode legs flat in a south-east direction as in the second picture. Use the pliers to bend the anode legs 90 degrees upwards again. There are bulges in the legs of many LEDs and they will naturally bend at this point, giving a uniform shape to all 8 legs.

Finally, carefully push the LEDs out of the wood using a small screwdriver from the other side of the wood. Ease all 8 LEDs out a little way first, then ease all 8 out a little more and so on until they are all free. This avoids bending the whole structure by accident.

PaulRB

#5
Mar 16, 2014, 06:11 pm Last Edit: Mar 16, 2014, 06:15 pm by PaulRB Reason: 1
Now make 7 more of those structures!

Then solder up the two halves of the cube. You will need some object to place between the layers to get the shape consistent. I found this kitchen clip used to seal food bags. A small piece of wood, planed to the right depth and width, could also be used.

Place one group of LEDs back in the holes, place the separator between the vertical legs and then place a second group of LEDs on top of that. Take care with the orientation! Then solder the two groups together. I find it easiest to solder two opposite corners first.

Solder the remaining legs together, then slide out the separator and repeat the process until you have completed both towers.

PaulRB

#6
Mar 16, 2014, 06:45 pm Last Edit: Mar 17, 2014, 11:13 am by PaulRB Reason: 1
You could skip this step, but I tested the towers by setting up the circuit on breadboard. Wire the anodes and cathodes up in the easiest way. Its not necessary wire them up exactly as per the PCB version for the purposes of testing.  I also uploaded the sketch to the ATtiny at this point (although that could be done on the final PCB).

To upload the sketch to the ATtiny, I first loaded ArduinoISP sketch to a Nano 3, then added the "anti-master-reset" capacitor and wired up the Slave-Reset, MISO, MOSI and SCLK pins as described in the ArduinoISP sketch. Procedure should be exactly the same for Uno and most other Arduinos. During programming of the ATtiny, the led cube will blink randomly, because those same pins are connected to the MAX7219 IC.

PaulRB

#7
Mar 16, 2014, 07:10 pm Last Edit: Mar 17, 2014, 11:24 am by PaulRB Reason: 1
Solder the IC sockets, resistors, capacitors and header sockets to the PCB, then insert the two ICs. Note that, unlike the other resistors and capacitors, the 10uF cap must be oriented correctly, with its -ve terminal away from the IC sockets.

Then line up and insert the two towers of LEDs and solder in place.

Finally, strip lengths of solid wire and solder the 8 remaining pads to the common anode "floors" on each of the 4 levels on each of the 2 halves of the cube. The pads on the ATtiny side of the PCB should be soldered to the "first floor" anodes and the pads on the MAX7219 side of the PCB should be soldered to the "top floor" anodes. Take care to ensure these wires will not short-circuit against any of the other "floors" of common anodes. If you can find solid wire with clear plastic insulation, you need not strip the entire length. This would avoid the potential shorting issue. If you can find wire like this, please let me know, as I have been unable  to find any! The nearest thing I could find was clear heat-shrink tubing, so that may be an option.


PaulRB

Thanks Riva. I will post the sketch this evening.

marco_c

Arduino libraries http://arduinocode.codeplex.com
Parola hardware & library http://parola.codeplex.com

PaulRB

Code: [Select]
//4x4x4 LED Cube using ATtiny85 and MAX7219
//P.Beard
//Dec 2013

//Pins for serial communications with the MAX7219 IC
#define M7219DATA 0
#define M7219CLK 2
#define M7219LOAD 1
#define LDR 3

//MAX7219 Control Registers
#define INTENSITY 0x0A
#define ON_OFF 0x0C
#define DECODE 0x09
#define SCAN_DIGITS 0x0B
#define TEST_MODE 0x0F

word layer[4]; //Holds the current bit pattern for each layer of the cube

int animSpeed;

void setup() {

  pinMode(M7219DATA, OUTPUT);
  pinMode(M7219CLK, OUTPUT);
  pinMode(M7219LOAD, OUTPUT);
  pinMode(LDR, INPUT);

  send7219(ON_OFF, 1);//Switch on LED Driver
  send7219(DECODE, 0);//Set no decoding on any digits
  send7219(SCAN_DIGITS, 7);//Scan all 8 digits
 
  for (int l=0; l<4; l++) {
    layer[l] = 0;
  }
  updateCube();

  send7219(TEST_MODE, 1);//Display test mode on
  delay (1000);
  send7219(TEST_MODE, 0);//Display test mode off
  delay (1000);
 
  randomiseCube();
  updateCube();

}

void loop() {
  rotatingSquare();
  rotation();
  randomRiseAndFall();
  rain();
  bubbles();
}

void rotatingSquare() {

  //Rotating square

  //  07        03 15 11
  //  02 04 10 12
  //  00 05 08 13
  //  01 06 09 14

  layer[0] = B00000000 << 8 | B10000111;

  animSpeed = 75;

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

    layer[0] = rotateClockwise(layer[0]);
    layer[1] = layer[0];
    layer[2] = layer[0];
    layer[3] = layer[0];
    updateCube();

  }

  layer[1] = rotateClockwise(layer[0]);
  layer[2] = rotateClockwise(layer[1]);
  layer[3] = rotateClockwise(layer[2]);

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

    for (int l = 0; l<4; l++) {
      layer[l] = rotateClockwise(layer[l]);
    }

    updateCube();

  }
}

void rotation() {

  //Rotation

  animSpeed = 151;

  for (int l=0; l<4; l++) {
    layer[l] = 1 << random(16) | 1 << random(16);
  }

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

    for (int l=0; l<4; l++) {
      layer[l] = rotateClockwise(layer[l]);
    }
    animSpeed--;

    updateCube();

  }

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

    for (int l=0; l<4; l++) {
      layer[l] = rotateAnticlockwise(layer[l]);
    }
    animSpeed++;

    updateCube();

  }

  animSpeed = 25;

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

    layer[0] = rotateClockwise(layer[0]);
    layer[1] = rotateAnticlockwise(layer[1]);
    layer[2] = rotateClockwise(layer[2]);
    layer[3] = rotateAnticlockwise(layer[3]);
    updateCube();

  }
}

void randomRiseAndFall() {

  //Random rise & fall

  layer[1] = 0;
  layer[2] = 0;
  layer[3] = layer[0] ^ 0xFFFF; // top layer opposite of bottom layer

  word w;
  int l = 0;

  animSpeed = 100;

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

    do {
      //Pick an led on the bottom row that is lit
      w = 1 << random(16);
    }
    while ((layer[0] & w) == 0);

    //Move that led up to the top layer

    do {
      layer[l] = layer[l++] ^ w;
      layer[l] = layer[l] ^ w;
      updateCube();
    }
    while (l < 3);

    do {
      //Pick an led on the top row that is lit
      w = 1 << random(16);
    }
    while ((layer[3] & w) == 0);

    //Move that led down to the bottom layer

    do {
      layer[l] = layer[l--] ^ w;
      layer[l] = layer[l] ^ w;
      updateCube();
    }
    while (l > 0);

  }
}

void bubbles() {

  //Random Bubbles

  animSpeed = 100;

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

    for (int l=3; l>=0; l--) {

      layer[l] = layer[l-1];

    }
    layer[0] = 1<<random(16) | 1<<random(16) | 1<<random(16);

    updateCube();

  }
}

void rain() {

  //Random Rain

  animSpeed = 50;

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

    for (int l=0; l<3; l++) {

      layer[l] = layer[l+1];

    }
    layer[3] = 1<<random(16) | 1<<random(16) | 1<<random(16);

    updateCube();

  }
}

void send7219(byte reg, byte val) { //Send 2 bytes to the MAX7219 Driver IC

  shiftOut(M7219DATA, M7219CLK, MSBFIRST, reg); //Send the register we want to set
  shiftOut(M7219DATA, M7219CLK, MSBFIRST, val); //Send the value

  digitalWrite(M7219LOAD, HIGH); //Tell MAX7219 to load the data
  digitalWrite(M7219LOAD, LOW);

}

void updateCube() { //Update the cube LEDs

  static int LDRavg;

  send7219(2, highByte(layer[0]));
  send7219(6, lowByte(layer[0]));
  send7219(8, highByte(layer[1]));
  send7219(4, lowByte(layer[1]));
  send7219(3, highByte(layer[2]));
  send7219(7, lowByte(layer[2]));
  send7219(5, highByte(layer[3]));
  send7219(1, lowByte(layer[3]));

  LDRavg = (analogRead(LDR) + LDRavg * 15) / 16;
  send7219 (INTENSITY, (LDRavg >> 6)); //Set intensity
  delay (animSpeed);

}

void randomiseCube() {//Set entire cube to random

  for (int l=0; l<4; l++) {
    layer[l] = random(65536);
  }

}


word rotateClockwise(word w) { //Rotate w0 clockwise
 
  //  07        03 15 11
  //  02 04 10 12
  //  00 05 08 13
  //  01 06 09 14

  byte bit1 = bitRead(w, 1);
  bitWrite(w,  1, bitRead(w,  6));
  bitWrite(w,  6, bitRead(w,  9));
  bitWrite(w,  9, bitRead(w, 14));
  bitWrite(w, 14, bitRead(w, 13));
  bitWrite(w, 13, bitRead(w, 12));
  bitWrite(w, 12, bitRead(w, 11));
  bitWrite(w, 11, bitRead(w, 15));
  bitWrite(w, 15, bitRead(w,  3));
  bitWrite(w,  3, bitRead(w,  7));
  bitWrite(w,  7, bitRead(w,  2));
  bitWrite(w,  2, bitRead(w,  0));
  bitWrite(w,  0, bit1);

  byte bit5 = bitRead(w, 5);
  bitWrite(w,  5, bitRead(w,  8));
  bitWrite(w,  8, bitRead(w, 10));
  bitWrite(w, 10, bitRead(w,  4));
  bitWrite(w,  4, bit5);

  return w;

}

word rotateAnticlockwise(word w) { //Rotate w0 anticlockwise

  //  07        03 15 11
  //  02 04 10 12
  //  00 05 08 13
  //  01 06 09 14

  byte bit1 = bitRead(w, 1);
  bitWrite(w,  1, bitRead(w,  0));
  bitWrite(w,  0, bitRead(w,  2));
  bitWrite(w,  2, bitRead(w,  7));
  bitWrite(w,  7, bitRead(w,  3));
  bitWrite(w,  3, bitRead(w, 15));
  bitWrite(w, 15, bitRead(w, 11));
  bitWrite(w, 11, bitRead(w, 12));
  bitWrite(w, 12, bitRead(w, 13));
  bitWrite(w, 13, bitRead(w, 14));
  bitWrite(w, 14, bitRead(w,  9));
  bitWrite(w,  9, bitRead(w,  6));
  bitWrite(w,  6, bit1);

  byte bit5 = bitRead(w, 5);
  bitWrite(w,  5, bitRead(w,  4));
  bitWrite(w, 04, bitRead(w, 10));
  bitWrite(w, 10, bitRead(w,  8));
  bitWrite(w,  8, bit5);

  return w;

}


Pedro147

Great job Paul. There has been some discussion here before on using a max7219 to control an LED cude and now you have done it   8) I know what my next project is going to be when I can tear myself away from Wordpress and css long enough to pick up a multimeter and soldering iron again. Before anyone else asks you, do you happen to have any spare pcb's that you would be willing to sell me? I realise that you have kindly provided the Eagle files but eleven boards is always overkill and my stockpile of boards from my last two projects is building up. Thanks for taking the time to document the project so well it is always great to be provided with enough information to actually build something rather than just be teased by some sketchy info that beginners haven't got a snowflakes chance in hell of following.

Pedro

PaulRB


Before anyone else asks you, do you happen to have any spare pcb's that you would be willing to sell me? I realise that you have kindly provided the Eagle files but eleven boards is always overkill and my stockpile of boards from my last two projects is building up.


No I don't, sorry. I had originally wanted to have a go at home etching the PCB, but decided it was too much hassle, what with all the preparation, unpleasant chemicals and drilling. I found a vendor, on eBay, based in the UK, who makes one-off or very small runs of PCBs using a Router. A single PCB of this size costs around £10, for which you could get several made in China. But he speaks perfect English, will take the raw Eagle files and turn them into the correct Gerber format for his router, and is very helpful, especially with beginners like me, spotting errors and giving advice and suggestions.


Thanks for taking the time to document the project so well it is always great to be provided with enough information to actually build something rather than just be teased by some sketchy info that beginners haven't got a snowflakes chance in hell of following.


Thanks Pedro, that is what I was aiming for, but wasn't sure if I truly achieved it. I will use feedback from anyone trying to follow these steps to improve the detail where needed, if I can.

Paul

Go Up