- 27 LEDs in a 3x3x3 configuration
- Controller is a AVR tiny13 with 1 kByte of program memory
- Programmed in C
The idea is not new. You can find loads and loads of LED cubes on the net. Some are small, some are insanely large and some even use RGB LEDs. And I must say I like them all. One LED-cube caught my eye because it mentions that it uses charlieplexing to control the LEDs. Its size is 5x4x4. But the instructable also says that with the 14 ports used it could have controlled 182 LEDs instead of the 80 it actually uses.
This got me thinking. How would you go about if you wanted to make a charlieplexed LED cube with efficient use of the available ports? Basically, with charlieplexing you get a multiplexed 2D matrix. How do you fit that into a 3D cube? (For the rest of this post I assume that you know what charlieplexing is.)
If a problem is complex, you start to solve it by making simplifications. Later you can gradually increase the complexity. A 1x1x1 cube is of course trivial. A 2x2x2 cube is pointless, because according to the laws of charlieplexing you need at least 4 ports to control 8 LEDs. You could simply use the same configuration as in the Nano POV to do that. And it really is quite wasteful, because with 4 ports you could actually drive 12 LEDs. So this is not challenging and in fact it is another sparse charlieplexing scheme.
So only for a 3x3x3 LED cube things start to become interesting. The basic figures: You end up controlling 27 LEDs and need 6 IO ports. And you could control no more than 30 LEDs. This is efficient.
I went with a tidy and systematic approach to transform a charlieplexing matrix to a 3D cube. First I layed out the matrix, which is 6x5 LEDs. Immediately, there is an interesting fact. There are 6 rows.
Cut that in two and you get two matrices with 3x5 LEDs.
If you move them next to each other horizontally, you get a 3x10 configuration.
All that is left to do now is to take away one column, stack that structure in the 3rd dimension and you are done.
Originally I had not planned to actually build a cube. It was meant as a kind of mental exercise. But when I realised that this was not really such a complicated structure, I changed my mind. Also, I wanted to explore how far I could get with a really small controller. And in this case, I could just get away with a DIP8 AVR tiny. Why? Because it has up to 6 usable IO ports - if you disable the reset pin and resort to high voltage serial programming. I do have an STK500 development board so that was not really an issue (or so I thought).
As recommended by several cube builders, I made a simple template by drilling holes (3 mm in my case) in the desired 3 x 3 layout of one layer of the cube into a piece of wood. Then I used that template to solder three layers of no-name LEDs into shape. Here of course is a major difference between a conventionally multiplexed cube and its charlieplexed counterpart.
Whereas with the former you connect e.g. the kathodes of all the LEDs in one layer, with the latter you only connect halve the layer. For my cube a layer consisted of 9 LEDs so I ended up with a group of 4 and a group of five LEDs.
Also, when you connect the layers of a normally multiplexed LED cube, you can simply connect all columns of the cube. As you can see from the matrix above, this is not possible for all columns when charlieplexing. The next image shows the vertical connections (the LED'S anodes) in blue.
And finally you need to connect the right layer structures to the right column. When this is done, the structure is ready for action. As in other projects, I did not plan to use current limiting resistors because at low operating voltages the tiny13 would limit the output current whether I wanted it or not.
In order to test the hardware I wrote a simple routine that would just fire up all LEDs one after the other. And I had an immediate disappointment - although every LED was lighting up. The ones connected to port 5 of the tiny13 were very dim. The reason for that was, that the repurposed reset pin has a weak driver. I knew this beforehand, but had expected to be able to equalise different brightness levels by using a differentiated timing scheme (i.e. by activating the dimmer LEDs for longer). But this was beyond all hope.
So in order to rescue the project I had to add a driver to port 5. I chose to use a simple variant with just two transistors. Once that was in place, all LEDs had an acceptable level of brightness - as long as you are not looking at it in bright daylight.
Another thing I wanted to try was to program the tiny13 in C. I hadn’t used C on microcontrollers before and I do prefer assembler. But it is always worth trying something new. And I was curious how a 1kByte controller would manage with a high level language.
I actually found it quite acceptable. I wrote an interrupt routine to do the multiplexing and displaying. And I wrote another routine to generate the animated patterns. I started by setting each LED individually, then waited and then set a new pattern. This works, but means that you fill up the program memory in no time. So I looked for a more systematic approach.
It turned out, that it is quite simple to generate certain elements in a cube with relatively little data. In my software, all LEDs have a number assigned, starting with “1” in the top right front corner, “2” on the top right edge and so on.
If you want to activate a row of three LEDs you can activate LEDs 1 to 3. Or you can activate LED 4 to 6. In the middle layer, e.g. the left row would be LEDs 16 to 18. So in order to light a row, you can use a subroutine that needs only one parameter: the offset.
Equally, if you want to activate the top layer you need to activate LEDs 1 to 9. So again all you need is a special routine that gets only one parameter (the offset) and then lights up 9 LEDs in a row.
Things get a bit more complicated if you try to switch on a row of LEDs, e.g. in the top layer, but in the other direction. Say you want the front edge to light up. You now need to activate LEDs 1, 4 and 7. The routine needed here needs one parameter as well (the offset), but this time only activates every third LED. Just like with the first subroutine, this routine can be extended to light up the whole front of the cube. You perform the same operation, but do it nine instead of three times.
To make things as efficient as possible I wrote a universal pattern generating subroutine. It gets four parameters: An offset, the number of LEDs to light up, a skip value and a repeat value. This routine can light up any rows, layers or even the whole cube. And that includes diagonal patterns and checkerboard type patterns. Then on top of this routine I built another set of more specialised routines which fix one or several parameters (e.g. 3 LEDs to activate, no repetition if you want to activate a row).
Such a set of routines is really an efficient and simple to use tool set for writing pattern sequences. I must admit that this routine was not an idea that came to me in a bright flash. It only developed gradually while I tried to implement more and more patterns and was forced to compress the code.
This project doesn’t make sense if you just want an LED cube. You can get bigger controllers with more ports and more program memory for almost the same price. And if you add drivers in order to increase brightness levels you might just as well use e.g. an ATmega8 controller and could almost avoid multiplexing altogether.
But the whole purpose of this project was actually to exercise “the little grey cells” a bit. And it was fun. It is really a curious thing that a couple of flashing LEDs can be so gratifying.
If you do want to build this, find the source code here. There is no schematic. If you need one, that project isn't for you. I would recommend to use high-brightness LEDs to get a better result than I did.
And finally, here are two videos of the cube in action.