USB LED "Ice Cube"

Required Components

Step 1: Bend LED Cathodes

All the LEDs in the cube have their cathodes (the shorter wire) bent at 90 degrees. These cathodes are joined together in each layer of the cube.

ledBend.jpg

The anodes (longer wires) remain vertical, and are joined to form each column. In each layer, a column can be activated (turning on a single LED). In a scan over all the layers (very quickly) individual LEDs can be flashed on. Due to the effect of persistence of vision (our eyes can't tell when things are flashing very quickly) the layers appear to be on at once, and individually controllable by layer and column. We have 16 columns (4 x 4) and 4 layers.

Step 2: Make a template

To keep the construction of the layers simple, make a template out of a piece of wood or MDF. Drill holes slightly larger than the diameter of the LEDs in a 4x4 square. We found a spacing of 2.5cm was ideal for our LEDs.

template.jpg

Step 3: Solder a corner

Tip: Placing small balls of blu-tack in the template holes helps to keep the LEDs secure whilst soldering.

Arrange the LEDs in a corner and solder together the cathodes.

corner.jpg

Step 4: Finish a layer

Put the remaining three rows in place and solder together the cathodes.

layer.jpg

Step 5: Hook the Anodes

On the top of the anodes (pointing towards the ceiling) make a small right angle hook in the direction of the common cathode column in the layer. You only need to hook the first 3 layers, leaving the last with straight anodes (to make it easier to attach wires, or slot through a presentation surface).

hooks.jpg

Step 6: Another Layer

Make another layer in the same manner. This layer attaches to the previous layer offset by 90 degrees. Solder each hook of the previous layer onto the anode of the LED in the same column in the layer above.

2layers.jpg

Continue this same process for all 4 layers.

Step 7: Wiring

Connect wires to each anode column (16 wires) and a wire to each cathode layer (4 wires).

The image below shows how to connect these wires to the Penguino AVR.

wiring.jpg

The cathodes from each layer connect to ground through transistors (we're using BD681): Emitter to GND, Collector to the layer, Base via resistor to a Penguino output.

The LED anodes (column connections) also connect to the Penguino outputs via resistors. We are using 390 ohm resistors on the anode outputs (all pins of port A and B) and 1.5K ohm resistors on the bases of the cathode transistors (pins D2-D5).

Tip: Stripped solid core CAT5 ethernet cable provides an easy way to link up 8 wires to a breadboard (see picture above). Solid core ethernet cable is available from most electronics stores for in-wall wiring (interior cables are usually multi-core, and much harder to breadboard).

Step 8: Software

The following software will display a simple rotating effect on the cube. By using UART functions this software can be extended so that effects can be controlled by computer software over USB.

This program compiles with the Penguino library and can be uploaded using Flipper. See the Penguino AVR Documentation for details.

   1 
   2 #include <stdio.h>
   3 #include <string.h>
   4 
   5 #include "penguino/uart/uart.h"
   6 #include "penguino/uart/uart-stdio.h"
   7 #include "penguino/io.h"
   8 #include "penguino/time.h"
   9 
  10 #define UART_BAUD_RATE 115200
  11 #define NUM_LAYERS 4
  12 
  13 #define MAX_FRAMES 10
  14 
  15 // A frame in an animation on the cube
  16 typedef uint16_t frame_t[NUM_LAYERS];
  17 
  18 // specifies a set of frames for an effect (and the delay between frames)
  19 typedef struct {
  20         int frameDelay;
  21         
  22         int numFrames;
  23         
  24         frame_t frames[MAX_FRAMES];
  25 } effect_t;
  26 
  27 // outputs a frame onto the cube (with 1ms delay between layers)
  28 void output_cube( frame_t *frame ) {
  29         for ( int layer = 0; layer < NUM_LAYERS; ++layer ) {
  30                 uint8_t layerPin = D2 + layer;
  31                 drivePin( layerPin, High );
  32                 
  33                 drivePort( B, (*frame)[layer] & 0xFF );
  34                 drivePort( A, (*frame)[layer] >> 8 );
  35                 
  36                 delay_ms( 1 );
  37                 drivePin( layerPin, Low );
  38         }
  39 }
  40 
  41 int main( void ) {
  42         statusLed_init( );
  43         
  44         // flash status LED for a bit
  45         statusLed_orange( );
  46         delay_ms( 500 );
  47         statusLed_green( );
  48         
  49         // initalise UART
  50         uart_init( UART_BAUD_RATE );
  51         
  52         // enable UART as stdio
  53         uart_stdio_init( );
  54         
  55         // enable echo on the UART stdin so the user can see what they are typing
  56     uart_stdio_echo( true );
  57         
  58         // init ground layers
  59         output_init( D2 );
  60         output_init( D3 );
  61         output_init( D4 );
  62         output_init( D5 );
  63         
  64         // initialise columns as outputs
  65         output_initPort( A );
  66         output_initPort( B );
  67         
  68         frame_t *currentDisplay = NULL;
  69         
  70         // spinning a plane on different axes
  71         effect_t x_spin = {
  72                 100, // 100ms per frame
  73                 6, // number of frames
  74                 {
  75                         {0x000F, 0x00F0, 0x0F00, 0xF000},
  76                         {0x00F0, 0x00F0, 0x0F00, 0x0F00},
  77                         {0x0F00, 0x0F00, 0x00F0, 0x00F0},
  78                         {0xF000, 0x0F00, 0x00F0, 0x000F},
  79                         {0x0000, 0xFF00, 0x00FF, 0x0000},
  80                         {0x0000, 0x00FF, 0xFF00, 0x0000},
  81                 }
  82         };
  83         
  84         effect_t y_spin = {
  85                 100, // 100ms per frame
  86                 6, // number of frames
  87                 {
  88                         {0x1248, 0x1248, 0x1248, 0x1248},
  89                         {0x03C0, 0x03C0, 0x03C0, 0x03C0},
  90                         {0x0C30, 0x0C30, 0x0C30, 0x0C30},
  91                         {0x8421, 0x8421, 0x8421, 0x8421},
  92                         {0x4422, 0x4422, 0x4422, 0x4422},
  93                         {0x2244, 0x2244, 0x2244, 0x2244},
  94                 }
  95         };
  96         
  97         effect_t z_spin = {
  98                 100, // 100ms per frame
  99                 6, // number of frames
 100                 {
 101                         {0x1111, 0x2222, 0x4444, 0x8888},
 102                         {0x0000, 0x3333, 0xCCCC, 0x0000},
 103                         {0x0000, 0xCCCC, 0x3333, 0x0000},
 104                         {0x8888, 0x4444, 0x2222, 0x1111},
 105                         {0x4444, 0x4444, 0x2222, 0x2222},
 106                         {0x2222, 0x2222, 0x4444, 0x4444},
 107                 }
 108         };
 109         
 110         #define NUM_EFFECTS 3
 111         
 112         // array of different effects to cycle through
 113         effect_t *effects[NUM_EFFECTS] = {
 114                 &x_spin,
 115                 &y_spin,
 116                 &z_spin
 117         };
 118         
 119         int currentEffectNum = 0;
 120         effect_t *currentEffect = &z_spin;
 121         
 122         uint16_t counter = 0;
 123         uint8_t frame = 0;
 124         
 125         uint8_t effect_iterations = 0;
 126         
 127         while ( 1 ) {
 128                 
 129                 // frame counter
 130                 counter++;
 131                 
 132                 // choose the frame to display
 133                 if ( currentDisplay == NULL || counter >= currentEffect->frameDelay/NUM_LAYERS ) {
 134                         
 135                         // choose the current frame
 136                         currentDisplay = &currentEffect->frames[frame];
 137                         
 138                         frame++;
 139                         frame %= currentEffect->numFrames;
 140                         
 141                         // scroll through different effects
 142                         if ( frame == 0 ) {
 143                                 effect_iterations++;
 144                                 
 145                                 // cycle each effect 4 times before moving to the next
 146                                 if ( effect_iterations == 4 ) {
 147                                         currentEffectNum++;
 148                                         currentEffectNum %= NUM_EFFECTS;
 149                                         
 150                                         currentEffect = effects[currentEffectNum];
 151                                         
 152                                         effect_iterations = 0;
 153                                 }
 154                         }
 155                         
 156                         counter = 0;
 157                 }
 158                 
 159                 // output the current frame on the cube
 160                 output_cube( currentDisplay );
 161         }
 162 
 163         return 0;
 164 }

Projects/PenguinoAVR/USB LED Cube (last edited 2010-01-17 21:24:52 by TheoJulienne)