Saturday, September 12, 2009

LED Cube



I've wanted to create one of these for ages. There are a ton of instructables out there for how to make various versions of these from 2x2x2 cubes to 8x8x8 monsters. I wanted to make something reasonable for a first attempt, but fun enough to look at. I figured a 3x3x3 is a good start.

There's also been quite a buzz the last few months around using LEDs as both light emitters and light detectors. I wondered if the LED cube could be made more interesting by also making it detect changes in ambient light. Perhaps that's a little too ambitious for the first iteration, but I can at least plan for this feature as a software (rather than a hardware) update later on. It turns out that there's nothing extra needed, hardware wise, for light detection with LEDs (check out this post on spritemods, which is a great example: minimalism; Jeroen also posts the source code, which I'm going to "borrow" from rather liberally when I get round to trying the light detection part). Don't expect any of the light detection fangling in this post - I'm only going to describe making and testing a 3x3x3 LED cube. I mention the light detection stuff because it's cool and I wanted to pressure myself into getting round to doing it by publicly announcing my intent :)

Anyway, first things first, lets get a working cube soldered and tested.

For the initial test set up I used the following: 9 LEDs (I chose all blue), an ATmega8, 12 breadboard jumper cables, 3 alligator clips (for the cathodes), a couple of breadboards and my regular USB AVR programer and associated paraphernalia.

To make the soldering process easier I created a jig from a plastic box in the recycling bin. I made a 3x3x3 grid of holes, spaced 25mm apart (just a little less than the length of the LED cathode leads). I marked out the holes with a sharpie and then blasphemed by melting the holes with my soldering iron...

I pushed in the LEDs into each hole with the anodes (long leads) facing outwards - we're going to be soldering all the anodes in a column together later on and it seemed sensible to have them on the outside of the cube, where they'll be easier to get at. Then I bent the cathodes down 90o so that they touched the cathode of one of their neighbours.

Here's the jig with the first set of LEDs in place, ready to solder:



The middle LED only makes one connection, which makes it a weak point in the layer. I was tempted to add a couple of extra connections from it to make it more stable, but I figured that, after soldering the layers together it should be stable enough. I just added a bit more solder to that particular connection in order to strengthen it.

After soldering all the leads together, you gently pull each LED out of their hole. You can then pull the whole assembly away and marvel at your soldering skill ;). Then it's time to start over again...

Here's the full set of 3 layers all soldered nicely (still separate though):

The final step in creating the cube is to solder the layers together. There must be a better way than what I tried, but here it is: I placed one layer back in the jig, cut off a few small strips of sellotape, positioned the next layer on top so that the anodes from the lower layer overlapped enough to solder to the next layer and then secured the two layers together with the sellotape (synonym:scotch tape). I soldered the corners first to give some amount of stability to the emerging structure.

Whilst soldering the third layer I worked out that the best method was to position the lower anode lead on the outside of the upper LED, then bend about 2mm of the lower lead 90o towards the upper lead and then solder them together. The bottome left LED column in the second picture below shows what I mean:


And here's the completed cube:

I'd not actually programmed an ATmega8 before, but it was fairly straight forward to modify my ATtiny setup. I just had to work out which pins to attach the programmer to (by looking at the atmega8 datasheet). The trickiest part ended up being getting the cube securely placed into the breadboards (I wish I'd given into a previous whim to buy one of the bigger boards now). The picture below shows my test current set-up with the cube installed.


I tried to come up with the simplest test I could imagine whilst ensuring I was addressing each LED correctly. So my first test just powered up each individual LED in order, one layer at a time.

Here's a video of it in action:



I've included the code below for those interested. Just bear in mind that this was specific to the way I wired up the cube. It's a shame 9 columns have to be used for this as it means using multiple registers for the LEDs, which means we have to specify which columns are on which registers. Maybe someone out there can show me a better way to do it?

/*
* led_cube.c
*
* Created on: September 6th, 2009
* Author: Paul
*/
#include <avr/io.h>
#include <avr/delay.h>

//define the cathode pins
//I just happened to connect them to these pins
#define LAYER1 PD1
#define LAYER2 PD0
#define LAYER3 PD2

//define which pin each column (anode) is connected to
uint8_t kColumnConfig[] = {PD3, PB7, PB3, PD4, PD6, PB2, PB6, PD7, PB1};
//store the layers in an array as well
uint8_t kLayerConfig[] = {LAYER1, LAYER2, LAYER3};

//this function switches a column on or off
//we have to know which port the column is a part of
//hence the switch statment:
// PORTD pins are at indices 0,3,4,7 in the kColumnConfig array
// PORTB pins are at indices 1,2,5,6,8 in the kColumnConfig array
void setColumn(uint8_t pVal, uint8_t pCol)
{
switch(pCol)
{
//portD pins
case 0:
case 3:
case 4:
case 7:
if(pVal)
{
PORTD |= (1 << kColumnConfig[pCol]);
}
else
{
PORTD &= ~(1 << kColumnConfig[pCol]);
}
break;
case 1:
case 2:
case 5:
case 6:
case 8:
// portB pins
if(pVal)
{
PORTB |= (1 << kColumnConfig[pCol]);
}
else
{
PORTB &= ~(1 << kColumnConfig[pCol]);
}
break;
}
}

int main(void)
{
uint8_t i = 0;
uint8_t j = 0;

DDRB = 0xFF; // all set to output
DDRD = 0xFF; // all set to output
//swich off all layers by applying voltage to the cathode pins thereby preventing current flow
PORTD |= (1 << LAYER1) | (1 << LAYER2) | (1 << LAYER3);
while(1)
{
//loop through each layer
for(i = 0; i < 3; i++)
{
//set the output value for this cathode layer to '0'
//this allows current to flow in this layer
PORTD &= ~(1 << kLayerConfig[i]);
for(j = 0;j < 9; j++)
{
//turn on the LED in column j
setColumn(1, j);
_delay_ms(250);
//trun off the LED in column j
setColumn(0, j);
}
//set the output value for this cathode layer to '1'
//this prevents current flow for this layer
PORTD |= (1 << kLayerConfig[i]);
}
}
return 1;
}


3 comments:

Miss Mossley said...

Totally off topic. But I also have a copy of The Dynamic Universe from college, 15 years ago. Its size and weight make it handy for many projects!

PaulBo said...

I have to own up to the fact that the book is my wife's and I've only flicked through it. I have definitely found more use out of it's size and weight :)

kw said...

Love your blog and your ideas! Have you seen http://scratch.mit.edu/ and PicoCrickets from MIT's Lifelong Kindergarten Lab?

Scratch is programming for kids. A lot of fun!