8x8x8 multiplexed LED cube with an Arduino Mega 2560

Actually, if the data goes live only when the SS pin is turned back HIGH, then I think this should be enough to switch to the next horizontal LED plane (next multiplexing step):

void displayCurrentPlane ()
{
    digitalWrite (PIN_SS, LOW); //Start transferring data

    for (byte i = N - 1; i >= 0; --i) //Set up the LEDs inside the current plane
    {
        SPI.transfer (cubeData[currentPlaneIndex][i]);
    }

    SPI.transfer (~(1 << currentPlaneIndex)); //Turn on the current plane and turn off all other planes (current plane's bit is set to 0, all other bits are set to 1)

    digitalWrite (PIN_SS, HIGH); //Done transferring data
}

Tonight I've "fired up" the LED cube. For about 2 hours it did not work at all. Turned out I had one short plus some misunderstanding about how my solderless breadboard's +/- rails work. I've solved these and the cube started working :smiley:

Unfortunately it doesn't quite work as I would have expected it to. There are some mysteries that I can't explain:

  1. In theory we have discussed that a cathode column can be turned on by setting its bit to 1. This works as expected. We have also discussed that an anode plane can be turned on by setting its bit to 0. Instead of this it turns on when I set its bit to 1, not to 0. I have followed CrossRoads circuit design 100%.
  2. When I turn an anode plane on (by setting its bit to 1) some other planes also turn on (one or two planes above/below it), but much more faintly than the plane that actually needs to be turned on.
  3. When I set all anode planes to off (by setting all bits to 0), the middle two planes turn on at full brightness.

For the anode/layer, writing a 1 into the shift register bit makes the output go low & turns on the PNP to make the anodes High.
For each cathode, writing a 1 into the shift register bit to make the output go low and turn on the LED.

Can you seperate the SS for the anode from the SS for the cathodes?

Instead of your funny << shift thing, pull the data from an array:

for (layer = 0 to 7){
// turn off all layers
digitalWrite(AnodeSS, LOW);
  SPI.transfer(0x00);
digitalWrite(AnodeSS, HIGH);

// write cathode - I suppose this could be the array structure you have above too
digitalWrite(CathodeSS, LOW);
  SPI.transfer(cathodeData[2*layer]);        // 0,2,4,6,8,10,12,14
  SPI.transfer(cathodeData[(2*layer)+1]); // 1,3,5,7,9,11,13,15
digitalWrite(CathodeSS, HIGH);
// this seems simpler to me tho - just 2 bytes per layer

// turn on anode
digitalWrite(AnodeSS, LOW);
  SPI.transfer(anodeData[layer]);
digitalWrite(AnodeSS, HIGH);

}

Ok, so in order to turn on an LED situated at the intersection of the layer X and column Y, you need to write 1 into the Xth bit of the anode shift register and 1 into the appropriate bit of the appropriate cathode shift register. The main idea is that both the anode and cathode shift registers need to receive a 1 bit in order to turn on the LED. That's nice. It seems that I misunderstood it earlier, but now everything is clear.

I'm not so worried at the moment about how exactly I write those bits into the shift registers, as that seems to be working. On the other hand I'm very much worried about the fact that turning a layer on also turns on 1 or 2 other layers below and/or above it, but not as brightly as the targeted layer. Also, if I turn off all the layers, the two middle layers turn on at full brightness. That's very worrying... seems like a hardware bug...

For testing purposes I have disabled the whole multiplexing and everything that can make debugging hard. My whole loop() function is empty now, I just test things in the setup() function.

For example, if my setup() contains this:

void setup ()
{
    pinMode (PIN_MOSI, OUTPUT);
    pinMode (PIN_SCK, OUTPUT);
    pinMode (PIN_SS, OUTPUT);

    SPI.begin ();
    SPI.setBitOrder (MSBFIRST);
    SPI.setClockDivider (SPI_CLOCK_DIV4);

    digitalWrite (PIN_SS, LOW); //start transferring data
    SPI.transfer (B00000000); //turn off cathode columns 8-15
    SPI.transfer (B00000001); //turn on cathode column 0, turn off cathode columns 1-7
    SPI.transfer (B00000001); //turn on the first anode plane (anode plane 0)
    digitalWrite (PIN_SS, HIGH); //done transferring data
}

The code cannot get simpler than this. There's clearly no error in it and yet anode planes 1 and 2 also turn on in a dim manner next to anode plane 0 which turns on at full brightness, as instructed.

If in the last SPI.transfer(), which sets the anode planes, I transfer all 0 bits, the two middle anode planes (1 and 2) turn on.

Remember that I have a 4x4x4 cube driven by 3 daisy chained shift registers. The first 4 outputs of the first shift register on the line control the 4 anode layers through the MOSFETS and the next two shift registers on the line use all their 8 outputs to control the 16 cathode columns.

At the moment I don't have the slightest clue, what can be wrong, but it smells like a hardware problem to me...
Let me know if you suspect anything.

Thanks!

I suspect hardware problem also. If you put a meter on the anodes for the other layers, what do you measure?
Perhaps a small pulldown (5k) is needed to keep the anodes low when the PNP is turned off, or a stronger pullup is needed on the PNP bases.

Thanks, CrossRoads! :slight_smile:

When I'll arrive home tonight, I'll take some current measurements on the other anode layers. I plan to do this by inserting a multimeter between these other anode layers and the drain of the MOSFET which drives them. I hope that's right...

Honestly the thought that some layers turn on in addition to the layer which is really supposed to be on, has planted the feeling in me too that there's something wrong with the pullup resistors that I have today between the gate and the source of the MOSFETS (I have 5.6 KOhm there today). I'll try replacing them with some stronger resistors. I think I have 220 KOhms lying around, I'll give them a try. I hope that's not too much. I wonder if this can also explain why the two middle layers turn on when no layer is supposed to be on.

You mentioned that if I don't manage to solve the problem with bigger pulllups, I might need some pulldowns. From what I've read on Wikipedia, I understood that a pulldown is a resistor which you connect between the gate of the MOSFET and the ground. Is this correct? Also, is it good practice to use pullups and pulldowns combined, or you need to decide which one and use just one?

Thank you!

OK, so I started experimenting with pullups and pulldowns. First I replaced the 5.6K pullups with 220K pullups (I didn't have anything in between 5.6K and 220K). Nothing has changed really... Then I took out the pullups and replaced them with 5.6K pulldowns (instead of having the resistors between the gate and the 5V I put them between the gate and the GND). The situation became worse. Now all the planes are lit all the time.

Some interesting things I've noticed:
1.When I plug in the cube, but the Arduino is not connected, it starts up with all LEDs off. But if I put my hand near the 5V rail (I don't touch anything, I just take it 1-2 cm close tot he 5V rail), the LEDs start lighting up. The closer my hand, the more LEDs light up. I take my hand away, the LEDs go off...
2. When I unplug the cube, the LEDs stay on for 5-10 seconds (Depending on homw many are lit up). I guess that's because the power supply has some capacitor in it...

If I take out all pullup and pulldown resistors, then planes 1 and 4 are always on at full brightness, planes 2 and 3 behave as instructed. This is kind of the opposite of the case when I have the pullups in place and set all planes to off. In that case planes 2 and 3 are on at full brightness.

It's got to be something related to the pullups/pulldowns, but I can't figure out what is wrong...

I would start by putting the resistors back the way you think they should be (5.6k gate/source I think). Then with the arduino completely disconnected, try to manually light up an LED by connecting the approprate voltage to the appropriate pins to turn on an LED.

I think your case this would be to hook up a ground, and have a couple 5v probes. hook one probe to the gate on one mosfet, and another on the leds anode lead (make sure to connect it before the resistor, so that you are using a resistor in circuit with the LED, or you may blow an LED. If you can manually light each LED one at a time, your LED setup is fine, you need to look at your programming or other circuitry.

I've now tried 1K pullups instead of the 5.6K pullups. The behavior is pretty much the same, except that the planes which are "ghosting" (slightly lit up besides the planes that really should be on) are now brighter, but still not fully bright.

I've managed to figure out one more important detail: The planes which are "ghosting" are always planes 2 and 3 (planes 1 and 4 are fine). When I turn all planes off, the "ghosting" planes (2 and 3) turn on at full brightess.

There's definitely something wrong around the anode planes 2 and 3...

@Hippynerd: My LEDs (the cube itself) is defintely fine. I've tested it throughly before connecting it up to anything. The devil is hiding somewhere in the driver circuit. It seems to me that it's impossible for the software to be wrong as I'm only writing out 3 bytes to the 3 shift registers in the setup() function, loop() is completely empty, there's no sorcery in the software (yet :slight_smile: )

The mystery has been solved! It was clear that the problem was around the anode planes, so I started changing the components around that part, thinking that maybe something is defective. I first changed the anode driving shift register, that did not change anything. Than I changed the MOSFETS of planes 2 and 3 and... surprise! Now everything works well. They must have become damaged in the first phase when I had some wrong connections... I truly hope that they did not arrive damaged form Farnell, because 2 bad out of 4 would be very concerning.

Congrats! Glad to hear it wasn't my circuit design :wink:

Ladies and gentleman, it's alive! It works! 100%!
I've even tried multiplexing and that also works well. At 4 ms multiplexing cycles it looks nice, at 6 ms my eye can already see the "trick" (it starts vibrating). I'll probably aim for 4 ms or lower.

I would like to thank you all for all the help you've given me with this project. I wouldn't have done it in a thousand years by myself, without your answers. My thanks are aimed especially at you, CrossRoads! Your design and your help is something that MasterCard cannot buy (priceless!). Seriously, I cannot thank you enough for having the patience to assist me across the steps of building the cube. If there's any way ever I can hep you back with anything, don't hesitate to let me know!

Now that the 4x4x4 cube works, I'll continue experimenting with writing software for it. After I'll have something usable, I will start putting together a 8x8x8 cube :smiley: I'll start by turning that printed circuit design into something real and transferring the current 4x4x4 cube onto that board, populating only part of it.

@Hippynerd: It's very late at night again here, so I'm going to sleep now, but if you're still interested in that BOM, let me know and I'll gather my notes and write you a comlete list with stuff that you need for the cube and the approximate prices for which I've bought the components.

The 4x4x4 cube is just a test cube, but it's a great success for me. From here to the 8x8x8 cube it's just an immense amount of soldering. The knowledge is in my head and it's all tested now. This is a great day for me :slight_smile: I've wanted to build a LED cube for 2-3 years now, but I didn't have the necessary knowledge. I've learned a lot from this 4x4x4 test cube. Now I can build the real thing. This is a dream come true.

Thank you all for everything again! I am most grateful for your help.

I'll borrow a camera soon (mine got broken) and I'll be back with a demo video of the 4x4x4 cube in a few days! Also, I make it my goal to thoroughly document the process of building the big cube. I will write a series of articles on my blog about it, which will help other people achieve the same result.

Hooray! :slight_smile:

Can you add on to the 4x4x4 to expand it into the 8x8x8? If so, then you're already 25% done.

:slight_smile: I've thought of extending the 4x4x4 cube into an 8x8x8 one... but I've reached the conclusion that I'll start fresh with the big one. With the little one I've learned how to solder nd how not to solder the LEDs together, so it has some minor aesthetic flows. The big one is planned to be a work of art :slight_smile:

That would be great if you could put together a little document. I dont need source or price of parts, but listing the specific parts, and the specific calculations and schematics would be great.

Hey there, Hippynerd!

Here's the BOM that I've promised, in form of a Google spreadhseet:
https://docs.google.com/spreadsheet/ccc?key=0An4wEyr8LJXZdFR4MjhVXzNBZ2R3WWFkQzFYeWlCa0E&usp=sharing

I tried to make it as complete as possible, so there might be some things there that you don't need. The included prices are estimations of the prices that I have paid myself. My own total is 189$, but in reality it's probably 30% more because from several components I've bought more than needed, just to be safe.

I have bumped into a very interesting (and annoying effect) with the LED cube.

Let's say that we turn on one LED on the lowest anode plane, the corner LED which is leftmost and frontmost, like this:

    digitalWrite (PIN_SS, LOW); //Start transferring data

    SPI.transfer (B00000000);
    SPI.transfer (B00000001); //leftmost, frontmost corner cathode column
    SPI.transfer (B00000001); //bottom anode plane

    digitalWrite (PIN_SS, HIGH); //Done transferring data

Then we turn that LED off and instead we turn on another LED on the top layer, in the opposite (rightmost, backmost) corner, like this:

    digitalWrite (PIN_SS, LOW); //Start transferring data

    SPI.transfer (B10000000); //rightmost, backmost corner cathode column
    SPI.transfer (B00000000);
    SPI.transfer (B00001000); //top anode plane

    digitalWrite (PIN_SS, HIGH); //Done transferring data

The extremely strange thing that happens is that at the moment when we switch from the first LED to the second, for a very brief moment we can see a ghosting (a pale turn-on, but still visible) of a third LED, which is in the newly turned on anode plane but in the old cathode column, in other words in our example, the LED in the top plane, in the leftost, frontmost column.

This becomes very bothering as soon as we start using multiplexing (and that's the goal here). The way it manifests itself during multiplexing is that the image shown in the current multiplexed plane is ghosted in the previously multiplexed plane.

I can't find an explanation for this. It's like the cathode column that was turned on first is not turning off fast enough and when the second anode plane is turned on in combination with another cathode column, for a brief moment the intersection of the new plane and the old column is lit up. But why is the old cathode column not turned off fast enough? Could it be due to some characteristic of the TPIC6B595 shift registers? Some weird latency? Or maybe it's something related to how the data is pushed from one shift register to the next one?

I've tried CrossRoads' suggestion and separated the SS pin for the anode layers from the cathode SS pins. I turned off the anode planes first, then I set up the cathodes and finally I turned the anode planes back on. It doesn't help. The ghosting remains.

I got around the problem in the software by doing this: first I turn off everything by pushing all 0s into all shift registers, then I wait for 100 microseconds (1/10 milliseconds), after that I push the new data into all shift registers (including cathode and anode shift registers). This makes the ghosting go away by allowing the old cathode column to turn off before a new cathode column is turned on in a new anode layer. The only problem is that this approach introduces a barely noticeable tiny flickering in the LEDs when they are constantly on (through multiplexing). It's not a very big problem, because usually the LEDs won't be constantly on and the flickering is very hard to notice, but it still bothers me a little. And I'm curious what is behind that latency in turning off the cathode columns. I'm also worried that if this is somehow caused by some delay in the shift registers, the problem will be more severe in the 8x8x8 cube, where there will be 9 shift registers instead of the current 3 and the flickering will become more visible...

Try this: use the TPIC6B595 OE to disable the outputs when switching.

Use direct port manipulation to make it go faster.
So that OE is bit 2 and SS/Latch is bit 3, both on port D:

PORTD = PORTD | B00000010; // bring OE/ high
// xfer data
    SPI.transfer (B00000000);
    SPI.transfer (B00000001); //leftmost, frontmost corner cathode column
    SPI.transfer (B00000001); //bottom anode plane
PORTD = PORTD & B11111011; // bring SS/latch low
PORTD = PORDT | B00000110;  //  bring OE/ an SS/latch high