USB LED "Ice Cube"
|
Required Components
- 64 (Blue) LEDs
- 16 LED driving resistors (to limit current pull and prevent over-driving the LEDs)
- 4 transistor driving resistors
- 4 NPN transistors (rated over 300mA, we used BD681)
- Penguino AVR and USB cable
- Breadboard and Breadboard Wire
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.
|
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.
|
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.
|
Step 4: Finish a layer
Put the remaining three rows in place and solder together the cathodes.
|
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).
|
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.
|
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.
|
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 = ¤tEffect->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 }
![icy [labs]](/moin_static171/common/wikilogo.png)






