The idea for this project came along mostly because I needed a second project for this class and because of my fascination with LEDs. I was browsing youtube for fun LED project ideas and came across the LED Cube 3x3x3. Unfortunately, our camcorder is not focusing correctly in darkness, so the video clip is not so good.
Video Clip | Front View | Side View | ||
![]() clip (AVI, ~8.9Mb) |
---|
The purpose of this device is purely for fun and blinking some lights :) You can turn on 1 to 27 LEDs on in any combination with many combinations of colors. We can also do a fade effect fading both in an out. The LEDs have one minor problem because they are clear and placed so close to one another they can send light through LEDs above them. This creates an illusion to the viewer making them think that we have turned those LEDs on that are actually off.
There are only a few major parts to this project specified below.
We made several changes to the original hardware. The main changes were made to the board. The original board was double-sided and we managed to make ours one-sided. We accomplished this be moving the links between the color pins to wire jumpers connecting the LEDs that are off of the board. The foil on the top part of the board is used as ground. Two vias connect the PIC's Vss pins with the top side of the PCB. They are placed below the IC socket. The tiny 0.1μF ceramic cap is soldered directly to the corresponding PIC (socket) pins, it is circled on the right image below. The middle image shows the components layout on the PCB (top view with blue wires on the bottom). The 4-pin connector on board is used for programming the PIC.
Schematic | PCB layout | Back side of PCB | ||
The 27 multi-colored LEDs are wired together according to the original PDF tutorial as follows. There are three vertical planes standing side by side. We call those planes back, middle and front. The back plane is formed by 9 LED columns on positions 3, 6, and 9 on board (see the right image below). Similarly, the middle plain is formed by LEDs on positions 2, 5, and 8. Finally, the front plane is formed by LEDs on positions 1, 4, and 7.
Within each plane the GRB pins are connected together by wire jumpers above the board (see photos above), but the common pins of 9 LEDs belonging to the plane are distinct. The common pins of every row of three LEDs which is parallel to the board are connected together to the corresponding C pin on the PCB. The indexing of LEDs within a plane if you look on it from the power jack, as well as on schematic, matches the one on the board. That is, the LED #1 is the one in the lower left corner and LED #9 is the one in the top right corner. This allows us to control the color of each LED individually by grounding the corresponding common pin of three LEDs in a row and setting the RGB color of all LEDs in the corresponding plane.
LED schematic | LED placement | |
The software is written on C and adjusted from the original one to be used with HiTech C compiler. The RGBcube() method is used to display the LEDs. It starts by turning off all the LEDs by setting to 1 the corresponding pins in PORTB and PORTC that control the commons of the LEDs. It then goes into the switch statement and grounds one of common pins corresponding to the row. defined by the corresponding parameter. After this we clear all colors by zero-ing the corresponding pins of PORTA and PORTC. Finally, we pass the colors to PORTA and PORTC by OR-ing them. Unfortunately, we could not make the library delay method to compile, so we use a custom 1 msec delay method written in assembly language.
// this is the main funcion for displaying the LEDs void RGBcube (unsigned short row,unsigned short front,unsigned short mid,unsigned short back) { PORTB |= (0b00111111); PORTC |= (0b00111000); // turn all LEDs off switch (row) { case 0: break; // all OFF case 1: resBit(PORTC, 3); break; // ground common pin #1 case 2: resBit(PORTC, 4); break; // ground common pin #2 case 3: resBit(PORTC, 5); break; // ground common pin #3 case 4: resBit(PORTB, 0); break; // ground common pin #4 case 5: resBit(PORTB, 1); break; // ground common pin #5 case 6: resBit(PORTB, 2); break; // ground common pin #6 case 7: resBit(PORTB, 3); break; // ground common pin #7 case 8: resBit(PORTB, 4); break; // ground common pin #8 case 9: resBit(PORTB, 5); break; // ground common pin #9 } PORTA = 0; // clear all colors PORTC &=~ (0b00000111); PORTA |= front; // front level colors PORTA |= (mid << 3); // mid level colors PORTC |= back; // back level colors delay1msec(); } |
Here is an example of RGBup() method that takes each horizontal layer, colors it the same and then rotates the colors upwards. Looking at the first for loop that calls RGBcube() you see it turns the top level red, middle level green, and bottom level blue. The numbers in front of the color names are the ground pins that need to be set. Each of the inner loops runs 16 times, which determines the speed of color changes.
// this funcion lights each layer and the colors move upwards void RGBup () { for (count2 = 0; count2 < 25; ++count2) { for (count = 0; count < 16; ++count) { RGBcube (1,red,red,red); //top level of cube RGBcube (4,red,red,red); RGBcube (7,red,red,red); RGBcube (2,green,green,green); //middle level of cube RGBcube (5,green,green,green); RGBcube (8,green,green,green); RGBcube (3,blue,blue,blue); //bottom level of cube RGBcube (6,blue,blue,blue); RGBcube (9,blue,blue,blue); } for (count = 0; count < 16; ++count) { RGBcube (1,green,green,green); //top level of cube RGBcube (4,green,green,green); RGBcube (7,green,green,green); RGBcube (2,blue,blue,blue); //middle level of cube RGBcube (5,blue,blue,blue); RGBcube (8,blue,blue,blue); RGBcube (3,red,red,red); //bottom level of cube RGBcube (6,red,red,red); RGBcube (9,red,red,red); } for (count = 0; count < 16; ++count) { RGBcube (1,blue,blue,blue); //top level of cube RGBcube (4,blue,blue,blue); RGBcube (7,blue,blue,blue); RGBcube (2,red,red,red); //middle level of cube RGBcube (5,red,red,red); RGBcube (8,red,red,red); RGBcube (3,green,green,green); //bottom level of cube RGBcube (6,green,green,green); RGBcube (9,green,green,green); } } } |
Once you understand how the RGBup() method works, it is easy to add your own effects. Each effect is encoded within a corresponding method. The effects are sequenced from the main function. All it needs to do is call the method desired and loop forever. The DIAspin() method is too long to be directly compiled with the used compiler, so we splitted it into two parts. It is helpful to comment out all methods and leave only one of then enabled to understand how it looks and works.
void main () // main funcion { initPORTS () ; while (1) // order of patterns { RGBrand () ; RGBcolor(); RBGfun(); RGBbox (); RGBup (); RGBhoriz(); RGBspin (); RGBspin2(); for (count3 = 0; count3 < 3; ++count3) { // goes around the cube 5 times DIAGspin1(); DIAGspin2(); } RGBredspin(15); } } |
If I were to spend more time with this, I would probably redo it in assembly. In the beginning I thought it might be hard to transform the code into assembly but I do not believe this is the case now.
I do have to give some thanks to the person who I got this idea from, the website is here at Coil Gun Power. I did have some trouble putting this together because on this page he posts code for a 40 pin pic and then he gives the PDF to build this project using a 28 pin pic. The pictures he has are also using the 40 pin PIC. I am not sure why he switches between it like this, it is not very clear when reading it, at least to me. On top of this the code he posted for the 28 pin PIC (he is selling it with the cube) was very simple and does not do much.
Even with these issues, we still managed to build the cube and rewrite the code in C, which is available for download below. After some inspection of the code we found that we had built the cube to the specifications but the way it was explained to work was incorrect. We modified the code to work with ours and after understanding one method it is usually easy enough to write your own.
One key note is that you should watch how long your methods get when writing in C. We had to split the DIAspin() method into two parts because it was too long and did not compile.
We hope this project helps anyone who would like to recreate/reproduce something of this sort.
Last modified:Mon, Jan 23, 2023.