3 Simple Ways to
Share What You Make

With Instructables you can share what you make with the world — and tap into an ever-growing community of creative experts.

PhotosPhotos

Share one or more photos of a project, recipe, or whatever you've made, quickly and easily.

Step by StepStep-By-Step

Share your step-by-step photos with text instructions of what you made so others can do it too!

VideoVideo

Share your how-to video. You'll need your embed code from a video site such as YouTube.

LED Cube 8x8x8

Step 54Software: Low level functions

Software: Low level functions
We have made a small library of low level graphic functions.

There are three main reasons for doing this.

Memory footprint

The easiest way to address each voxel would be through a three dimensional buffer array. Like this:

unsigned char cube[x][y][z]; (char means an 8 bit number, unsigned means that it's range is from 0 to 255. signed is -128 to +127)

Within this array each voxel would be represented by an integer, where 0 is off and 1 is on. In fact, you could use the entire integer and have 256 different brightness levels. We actually tried this first, but it turned out that our eBay LEDs had very little change in brightness in relation to duty cycle. The effect wasn't noticeable enough to be worth the trouble. We went for a monochrome solution. On and off.

With a monochrome cube and a three dimensional buffer, we would be wasting 7/8 of the memory used. The smallest amount of memory you can allocate is one byte (8 bits), and you only need 1 bit to represent on and off. 7 bits for each voxel would be wasted. 512*(7/8) = 448 bytes of wasted memory. Memory is scarce on micro controllers, so this is a sub-optimal solution.

Instead, we created a buffer that looks like this:

unsigned char cube[z][y];

In this buffer the X axis is represented within each of the bytes in the buffer array. This can be quite confusing to work with, which brings us to the second reason for making a library of low level drawing functions:

Code readability

Setting a voxel with the coordinates x=4, y=3, z=5 will require the following code:

cube[5][3] |= (0x01 << 4);

You can see how this could lead to some serious head scratching when trying to debug your effect code ;)

In draw.c we have made a bunch of functions that takes x,y,z as arguments and does this magic for you.

Setting the same voxel as in the example above is done with setvoxel(4,3,5), which is _a lot_ easier to read!

draw.c contains many more functions like this. Line drawing, plane drawing, box drawing, filling etc. Have a look in draw.c and familiarize yourself with the different functions.


Reusable code and code size

As you can see in draw.c, some of the functions are quite large. Writing that code over and over again inside effect functions would take up a lot of program memory. We only have 32 KB to work with. Its also boring to write the same code over and over again ;)


« Previous StepDownload PDFView All StepsNext Step »
9 comments
Apr 12, 2012. 2:35 AM#iluvleds says:
This is the coolest thing ever!!!
Anyway, i have code which goes like this...

resetLayer(); //function
resetLeds(); //function
PORTC &= ~(1 << latchLed) //set latch low
shiftOut(PORTC, dataLed, clockLed, 0xFFFFFFFFFFFFFFFF); //shifts out register //states
PORTC |= (1 << latchLed) //set latch high
_delay_us(50); //pauses a while
nextLayer(); //function
...


My question is, what should the delay be in microseconds for the layer switching. Thanks in advance. #iluvleds
Jan 17, 2012. 9:12 AMlscarmic says:
Can anyone elaborate on the way the X coordinate is stored using the left shift? For the example chr gives, setting a voxel with the coordinates x=4, y=3, z=5 will require the following code:

cube[5][3] |= (0x01 << 4);

I guess I could just accept that it works but I'd really like to understand what's actually happening. I realize we're shifting 0x01 four places to the left, then I guess ORing it with cube[5][3] then assigning it to cube [5][3]. It's the OR with a 2D array that's confusing.

thanks in advance.
Jan 19, 2012. 10:27 PMtriumphtotty says:
The 2D array of unsigned chars (bytes) represents the state of every "row" of 8 LEDs from front to back.  Setting bit "0" in any entry in the array sets the "front" bit.  Setting bit "7" sets the back bit.  So to set the bottom left hand corner ONLY, we could write:
cube[0][0]=0x01
0x01=00000001 in binary.

In the case of the above example, we want to change the state of one bit in a byte, i.e. switch on one LED in a row without changing the rest of it.  In this case it's the row 5 from the bottom and 3 from the left of the cube. In order to change the state of ONE bit in that byte, it is OR'd with (0x01<<4) which is binary 00010000 (i.e. bit 4 is set). If bit 4 in that byte is unset, then it becomes set.
Jan 20, 2012. 9:08 AMlscarmic says:
awesome. after staring at this for a few days i finally had the revelation this morning. for some reason i was thinking the z and y axis had bits that needed to be set somewhere in the array because, well, the layer has to be selected somehow and the row/latch address has to be set somewhere. finally see how the ISR takes care of it, basically treating the array index as the reference to which layer and row we're on.

still seems really wasteful with memory but i guess to compact it any more would be even harder to decipher. or maybe i just don't know that this is as compact as it can get. either way, glad i understand it now.

thanks for your help!
Feb 17, 2012. 1:10 PMKrisztian28 says:
I still don't understand clearly that how this buffer works. Can you explain it with more details?
Feb 18, 2012. 2:50 AMtriumphtotty says:
It's not exactly a buffer. cube[z][y] stores the state of the cube. The simplest way to think of it is as each element represents one "row" of LEDs. If one element is at 0x00 (or 0b00000000) then those 8 LEDs are off. If it is at 0xff (or 0b11111111) then those LEDs are on. So starting with the array itself, the z value are "up" and the y values are "across". So setting cube[0][0] to 0xff would light up all the LEDs along the bottom-left edge of the cube. Setting cube[7][7] to 0xff would light up all the LEDs along the top-right edge of the cube. Setting each of cube[7][0]..[7][1]..[7][2] up to cube[7][7] to 0xff would light up the top plane of the cube. The slightly tricky part of addressing individual voxels is the values of the bits within each element. If we just think of cube[0][0]. Setting it to 0x00 is "all off" and 0xff is "all on" as I said above. If we want to switch on just the front LED, we set bit0. bit0's value is 1, so cube[0][0]=0x01 will switch on just that one LED. To switch on the rear LED we just set bit7. bit7=128, or 0x80. So cube[0][0]=0x80 will switch that one LED on. To do multiple LEDs, just add together (or OR together in C) the values you want. So to set every odd LED on (bits1,3,5 and 7) we would just OR together 0x02, 0x08, 0x20 and 0x80 to give 0xAA. In binary this would look like 0x00000010, 0x00001000, 0x00100000, 0x10000000 which OR'd together would give 0x10101010. Every second bit. Does that help?
Feb 18, 2012. 12:29 PMKrisztian28 says:
Yes, it does. I appreciate it!

Thank you!
Jan 20, 2012. 10:21 AMtriumphtotty says:
Yeah, it's not a hard concept, but it's slightly non-intuitive.  It's not wasteful at all though.  64 bytes in cube[8][8] of 8 bits each is exactly 512 bits.  One for each LED.  :o)  From the point of view of writing to the arrays, you don't need to consider how the ISR code works anyway, but it's useful from a practical point of view, e.g. to know the duty cycles of the transistors, or the refresh rate of the cube as a whole in "frames per second"

Out of 'interest'(?) my .c code now has two "cube" arrays, and a kind of hacked PWM method of driving the LEDs in the ISR code, so each LED can be at 4 states (off, dim, brighter, full-on)  This makes the "rain" and "sparkle" effects very subtle.  My fireworks code is pretty flashy, but not entirely finished yet.
Aug 30, 2011. 4:40 AMpaler31 says:
hi, Where can I find the source code. I have built my cube and it works fine but I want to try playing with the software now but cant seem to find draw.c or any of the other source code.

Regards

John
Sep 1, 2011. 1:34 PMTechNotes says:
The source code is on the following pages.

http://www.instructables.com/id/Led-Cube-8x8x8/step49/Software-Introduction/

http://www.instructables.com/id/Led-Cube-8x8x8/step46/Program-the-AVR-with-test-code/

http://www.instructables.com/id/Led-Cube-8x8x8/step70/Run-the-cube-on-an-Arduino/

http://www.instructables.com/id/Led-Cube-8x8x8/step64/PC-Software-Introduction/
Aug 30, 2011. 6:56 AMpaler31 says:
its ok, I found it did not realize they were all in compressed files.

John

Pro

Get More Out of Instructables

Already have an Account?

close

All Steps Viewing
View all steps of an Instructable on the same page when you're a Pro Member.

Upgrade to Pro today!
651
Followers
7
Author:chr
I like microcontrollers and LEDs :D