Unusual LED Cube and Shift Register Behaviour?

Hi

I'm pretty new to all this and I'm not sure if what I'm seeing is normal, and I'd like your comments and hopefully suggestions to fix it. First some background.

I have built an 8x8x8 blue LED cube as a learning project for myself and it works. I'm running it with control signals coming from the SPI interface (pins 51, 52 and 53) on a MEGA2560 R3.

The circuit (see attachment - I tried to embed it but I failed) uses 74HC595 shift registers and 2N2222A transistors, with the LEDs powered by a separate 5v power supply (VCC) from a wall wart. The circuit board has a switch so I can turn VCC on and off, with an LED on the circuit board lighting up when VCC is ON. There is a 0.1 microFarad capacitor between VCC and GND on each shift register. The MEGA2560 is connected via USB to my computer and receives power from there.

The LEDs are protected by 220 ohm resistors. With this value, the 70 ma max current limit for the 74HC595 is respected when all 8 attached LEDs are ON. I also put 120 ohm resistors on the base of the 2N2222A transistors, which are connected in pairs. The 120 ohm value is my attempt to ensure the transistor is well into saturation when an entire LED cube layer is ON (560 ma), while also respecting the 35 ma max pin current limit on the shift register. Only one pair of transistors should be active at a time.

Issue #1:

When I switch VCC OFF but leave the MEGA2560 connected, the circuit board LED stays ON but at a slightly reduced brightness. I expected the LED to turn OFF when VCC was disconnected, as it is connected between VCC and GND. The only way this can happen is if current supplied by the MEGA2560 SPI pins to the data, clock and latch pins on the shift registers appears on the VCC bus.

I've also noticed that many LEDS in the cube also light up in seemingly random patterns (but at much reduced brightness) when VCC is OFF. I infer this is a manifestation of the behaviour above.

Is this normal behaviour for a shift register? If not, is there anything I could do to stop it?

Issue #2:

When running animations on the cube, the animations appear noisy. By noisy, I mean ghosting (I think that's what it's called). LEDs that are supposed to be OFF are in fact ON but at substantially reduced brightness compared to the ON LEDs. The ghosting LEDs seem to be limited to a few layers as far as I can tell. I find them annoying.

Perhaps this is also a consequence of Issue #1, or not. In any event, is there any way this could be fixed (hopefully without making a new circuit board).

Regards and thanks in advance for any thoughts / suggestions.

OP here.

Just noticed a problem with my circuit board which explains most of the ghosting in Issue #2.

Specifically, I made the circuit on perf board (see attached photo) and I had to use a few jumpers on the bottom side to connect the outputs of the shift register switching the transistors on and off to the transistor bases. Some of the ends of those jumper wires protruded slightly through to the top side of the board, and when I soldered them, they heated up and melted through the insulation of a couple of topside wires, causing unwanted connections between transistors (and cube layers) that I didn't find with my multimeter.

After I fixed it, the main ghosting issues disappeared so I'm quite happy on that front, although some minor ghosting continues. However, I still don't understand why there is power to my board from the Arduino SPI pins when I shut VCC off.

Whew...very glad to have found that issue.

20200306_072754.jpg

Circuit Board.jpg

Yojimbo55:
When I switch VCC OFF but leave the MEGA2560 connected, the circuit board LED stays ON but at a slightly reduced brightness. I expected the LED to turn OFF when VCC was disconnected, as it is connected between VCC and GND. The only way this can happen is if current supplied by the MEGA2560 SPI pins to the data, clock and latch pins on the shift registers appears on the VCC bus.

So then, never switch off the LED drivers while the Arduino is powered. This is called "phantom powering" and is overloading the protection diodes in the shift registers. The Mega 2560 and the shift registers should always be powered from the same 5 V power supply.

Yojimbo55:
When running animations on the cube, the animations appear noisy. By noisy, I mean ghosting (I think that's what it's called). LEDs that are supposed to be OFF are in fact ON but at substantially reduced brightness compared to the ON LEDs. The ghosting LEDs seem to be limited to a few layers as far as I can tell. I find them annoying.

Wiring errors aside, "ghosting" is caused by using saturated BJTs to drive the layers. Either you severely restrict the multiplexing speed, or use logic-level FETs instead.

The LEDs are protected by 220 ohm resistors. With this value, the 70 ma max current limit for the 74HC595 is respected when all 8 attached LEDs are ON

That depends on the forward voltage of your LEDs. What colour are they?

some minor ghosting continues.

In addition to Paul__B's suggestion, it can also be caused by software. Is your sketch using the OE line to disable all outputs until a few microseconds after the latch line has been triggered?

one pair of transistors should be active

Is that a good idea? Why would the transistors in a pair share the collector current, or the base current, evenly between them? I would also suggest logic level MOSFETs. Stp16nf06l for example.

OP here.

Thank you for responding to my post. To address the points made....

So then, never switch off the LED drivers while the Arduino is powered. This is called "phantom powering" and is overloading the protection diodes in the shift registers. The Mega 2560 and the shift registers should always be powered from the same 5 V power supply.

Ok, thanks for that.

I put a 5V output on my circuit board so, in the future, I can supply the Arduino via its USB input jack from the circuit board (I would ignore the two USB data lines). But right now, as I'm writing the animations for the cube, I need the Arduino connected to my PC so I can upload the new sketches and I need the LEDs to be ON so I can see if my coding works.

After seeing your response, I initially thought I could connect my 5V output to the 5V pin on the Arduino and Bob's your uncle, but then I read that that should NOT be done if the USB jack is being used at the same time - which in my case it would be.

So, it sounds like I have three options. The first is to do lots of plugging and unplugging of wires whenver I want to update my sketch - this isn't really practical because there are many updates during development.

The second is to leave the LEDs ON all the time, as you suggest. This is my preferred option.

The third is to play arround with USB cables. I could take the two data wires in the USB cable from my PC together with +5V and GND wires from my circuit board and combine them into a new USB cable which I could then connect to the Arduino. This would provide power to the Arduino from the same power source as my circuit board while allowing sketch updates to still come from my PC. Except there would be no GND connection to my PC anymore - not sure what the effect of that would be.

Wiring errors aside, "ghosting" is caused by using saturated BJTs to drive the layers. Either you severely restrict the multiplexing speed, or use logic-level FETs instead.

I undertook this project as a learning exercise with a pretty cool endpoint. At the time I started, I'd never heard of Arduino or shift registers, never used a breadboard, transistor or capacitor, never made a circuit board, and never done anything in C. I knew Ohm's Law from high school and I occasionally did some FORTRAN programming a lifetime ago at work. I'd also never heard of a FET or MOSFET.

Now I know a lot more, so my project has been a success. If I were to do it again, I'm pretty sure I would use different components but for now it's working and I hope to avoid having to remake my board. It took me hours and hours and hours make that thing.

Right now there is no ghosting except when I turn on the top layer, the bottom layer also turns on but at a much reduced intensity. I can't for the life of me figure out why that is - I've checked the wiring and the software but I can't find anything - so I'm missing something.

For information, the code snippet that sends information to the cube is shown below:

void displayCube(uint16_t displayTime) {
  uint16_t steps = 0;
  while (steps <= displayTime) {
    steps++;
    for (int8_t i = 0; i < 8; i++) {
      digitalWrite(SS, LOW);
      SPI.transfer(0x01 << i);       // Turn on cube vertical layer i.
      for (int8_t j = 0; j < 8; j++) {
        SPI.transfer(cube[i][j]);
      }
      digitalWrite(SS, HIGH);
    }
  }
  for (int8_t i = 0; i < 9; i++) {  // Turn all layers off once display is complete.
    digitalWrite(SS, LOW);
    SPI.transfer(0x00 << i);
  }
    digitalWrite(SS, HIGH);
}

The bottom layer of the cube used to be quite bright when the top layer was on, so I added the i=0; i<9 loop at the end of the above because without it, the bottom layer is ON when the function is exited. This made a considerable difference to the brightness, but didn't entirely get rid of the issue.

For the future, how did you get those images to post? I tried the image tags when I was writing my original post, but when I checked the preview, I didn't see the image so I figured it wasn't working.

That depends on the forward voltage of your LEDs. What colour are they?

I am using blue 5 mm diffuse LEDs. I measured the forward voltage (something else I had never heard of prior to this) at 2.8 V.

In addition to Paul__B's suggestion, it can also be caused by software. Is your sketch using the OE line to disable all outputs until a few microseconds after the latch line has been triggered?

I have posted the software snippet used to update the shift registers above. As noted earlier, I am using the SPI interface. After including the SPI library, initialization of that is shown below.

SPI.begin();
  SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));

  pinMode(SS, OUTPUT);
  digitalWrite(SS, HIGH);

Is that a good idea? Why would the transistors in a pair share the collector current, or the base current, evenly between them? I would also suggest logic level MOSFETs. Stp16nf06l for example.

Yes, I agree with you. Without some resistance in there somewhere I wouldn't expect the current flow to be balanced. But, I didn't know that when I started this project, and I had read about / seen examples where other people have used two 2n2222A transistors successfully in a similar manner. According to the datasheet, they can each handle up to 800 ma which is more than than the 560 ma I should get with all 64 LEDs ON in a layer, so I wasn't especially worried about that. I also put them in the same hole in my perf board to encourage current sharing but I know that is no guarantee.

Re MOSFETs, as per my earlier response above, I never heard of MOSFETs when I started this. Thank you for your suggestion though - I have written the number down in case I ever remake the circuit board or if I ever made a RGB cube.

Yojimbo55:
After seeing your response, I initially thought I could connect my 5V output to the 5V pin on the Arduino and Bob's your uncle, but then I read that that should NOT be done if the USB jack is being used at the same time - which in my case it would be.

I could take the two data wires in the USB cable from my PC together with +5V and GND wires from my circuit board and combine them into a new USB cable which I could then connect to the Arduino. This would provide power to the Arduino from the same power source as my circuit board while allowing sketch updates to still come from my PC. Except there would be no GND connection to my PC anymore - not sure what the effect of that would be.

I only mentioned 5 V power. I did not mention ground. You always connect the grounds together in this situation. Unless the signal is referenced to Vcc - 5 V - and feeding opto-isolators in which case you might connect the 5 V together and not ground but this is not such a situation.

So you can prepare a special USB cable with the 5 V line disconnected.

Yojimbo55:
For the future, how did you get those images to post? I tried the image tags when I was writing my original post, but when I checked the preview, I didn't see the image so I figured it wasn't working.

There is a trick with "imgur" - to embed an image in the posting, as always you have to have a link to an image. To locate the image on the Web page you cited, I had to click on the image to activate the JavaScript which displays it in full, then right click on that to copy the actual image location (or perhaps choose "display image" first so that you see the full URL of that image in the URL bar).

I am using blue 5 mm diffuse LEDs. I measured the forward voltage (something else I had never heard of prior to this) at 2.8 V.

In which case you could drop the series resistors down to 180R for a little more brightness. The transistors are probably dropping around 0.7V, your series resistors are dropping 5-0.7-2.8=1.5V. at 220R, the current will be 1.5/220=6.8mA, so 54mA per chip. At 180R, 1.5/180=8.3mA, so 66mA per chip. May not be worth the soldering effort for such a modest increase in brightness.

I have posted the software snippet used to update the shift registers above

Looks like your code is not using the OE line. I thought I saw that line going back to the Arduino in your schematic, but the writing was blurred. What can happen is that when you trigger the latch line ("SS" in your code), not all the SRs update at exactly the same instant. This can result in the wrong layer being activated, or not being deactivated, for a brief instant, resulting in ghosting. Disabling all the SR outputs using the OE line just before triggering the latch line, then delaying a few microseconds before re-enabling them can prevent ghosting.

Using SPI to update the cube is a good idea. But there is no proper timing control in your code, it is updating the cube too frequently. If you want to keep each layer on for longer, send the data once, then use delay(). Even delay(1) would result in 125 refreshes of the cube per second which is fast enough to prevent visible flickering. Better still, change your code to a "non-blocking" approach, so you can use millis() or micros() for timing and free up that wasted time for other processing. If your other code is also non-blocking, you won't need to turn off the last layer in order to prevent it being brighter than the others, they will naturally all get the same time lit and same brightness.

For the future, how did you get those images to post?

You can attach them to the post, as you did in your 1st & 2nd posts. Post the message, right-click on the attachment and copy the link address, then modify the post send insert the image using the image tags and the paste in the address. Its a bit of a pain.

I initially thought I could connect my 5V output to the 5V pin on the Arduino and Bob's your uncle, but then I read that that should NOT be done if the USB jack is being used at the same time

If you look at the mega2560 schematic you can see at the bottom of the page there is a circuit that protects the PC from current flowing back from the Arduino if powered via the Vin pin. The p-channel MOSFET labelled "T2", controlled by a comparator, does that by switching off when more than ~6.6V is present on Vin. But if power is supplied via the 5V pin, what prevents current flowing back to the PC? I don't know. That MOSFET will allow current to flow in either direction when switched on, which it would be if Vin is zero.

Uno is the same, but if you look at the schematic for Nano, it's different.


Here, there is no comparator or MOSFET, just a blocking diode. The downside is you don't get the full 5V when powered by USB, you only get around 4.7V due to the voltage drop of the diode. But at least the PC is protected when power is supplied through the 5V pin. Why was this not done for Uno/Mega?

Gents

I really appreciate the advice you are giving me.

So you can prepare a special USB cable with the 5 V line disconnected.

To be clear on this, I believe you are suggesting that I use 3 wires from the PC USB (GND plus the two data wires) and take the 5V from my circuit board. That would have the PC grounded to the Arduino, and the Arduino grounded to my circuit board (because I already have a separate ground wire connecting these two). Is that correct? It didn't occur to me - thanks.

But there is no proper timing control in your code, it is updating the cube too frequently. If you want to keep each layer on for longer, send the data once, then use delay().

I need some sort of delay because the thing refreshes so quickly, the animations wouldn't be visible. As you can see from my code snippet, I achieve that by just refreshing the cube over "displayTime" iterations. I tune that parameter when I see how the display looks and I feed it into the calling statement of the animation - it's different for different animations.

I could change that code snippet that updates the cube so that instead of refreshing the same information over and over, it instead updates the cube once and implements a delay. Now that you pointed this out, this seems more elegant to me and I will implement it - but is there any real advantage over what I currently have? You say the cube updates too frequently (which I agree it does) - is there a downside to this?

I've used milli() in other parts of the sketch when I was more interested in the real time, but for the most part I just use my tunable parameter.

In which case you could drop the series resistors down to 180R for a little more brightness.

I came up with the 220 value using the 5V supply, the measured 2.8 forward voltage for the LED, and (according to my notes) a 0.3 Vce at saturation for the transistor (from the datasheet). Given the 70 ma max limit on the 74HC595, that implies 70/8 = 8.75 ma per LED, and a 217 ohm resistor.

At the time I was worried about brightness given that the LEDs can take up to 20 ma, but I put one on my breadboard and tried it out with a few different resistors - I couldn't really see much difference between running the LED at 15 or 16 ma and running it at 8 or so. So if the real world turned out to be a little different from my assumptions, I wouldn't be especially bothered because I couldn't see much sensitivity of brightness to current.

I will study those figures you sent and figure out what they mean - this is all new stuff to me.

I could change that code snippet that updates the cube so that instead of refreshing the same information over and over, it instead updates the cube once and implements a delay

Delay after updating each layer, not the whole cube. If you delay after the whole cube, you will make that last layer really bright again.

but is there any real advantage over what I currently have?

The ghosting may be caused each time you switch layers. You only need to do that 8 times to refresh the cube once, and only refresh the cube ~100 times per second to avoid flickering. So that's around 800 layer changes per second. Doing it more often, as your code is doing now, could make the ghosting worse.

0.3 Vce at saturation for the transistor (from the datasheet)

Fair enough then.

couldn't see much sensitivity of brightness to current.

You don't see much change, even though it is changing, because the human eye's response is not linear. At 16mA, twice as much light energy is produced as at 8mA, but it doesn't look anything like twice as bright. The eye is much more sensitive at low levels of light. Don't forget that each led is only on for 1 period in 8 as the cube is refreshed, so if you set your resistors for 8mA instantaneous current, the average current is only 1mA. Its the average current that determines the perceived brightness.

OP here.

Is there a way to determine or estimate the refresh rate? I see you are mentioning values for refresh rates but where are those coming from?

Yes, the refresh rate is determined by your code, or could be.

Right now, your code repeats refreshes as fast as it can, and speed of animation is controlled by how many repeats of the refresh cycle are done. So your refresh rate is unknown and too high, and the number of repeats to achieve the desired animation speed has to be arrived at by trial and error because the refresh rate is unknown. This is what I was getting at in post #7 when I said there is no proper timing control.

If you put a known delay in your code, say 1ms for each layer of the cube, then you will get a known refresh rate, in this case 1000/8=125 refreshes per second. It won't be exactly that because of the execution time of the rest of your code, but it will be a reasonably good estimate.

You can control the speed of animation better by giving the displayCube() function a parameter which is in milliseconds (instead of a count of refreshes required). For example displayCube(50) would repeat the 8ms refresh loop until 50ms had passed (so in practice it would be more like 56ms, having done 7 repeats).

OP again

FYI regarding this:

If you put a known delay in your code, say 1ms for each layer of the cube, then you will get a known refresh rate, in this case 1000/8=125 refreshes per second. It won't be exactly that because of the execution time of the rest of your code, but it will be a reasonably good estimate.

You can control the speed of animation better by giving the displayCube() function a parameter which is in milliseconds (instead of a count of refreshes required). For example displayCube(50) would repeat the 8ms refresh loop until 50ms had passed (so in practice it would be more like 56ms, having done 7 repeats).

I did some experimenting with my cube display function along the lines you suggested, and the ghosting I was experiencing disappeared. The problem I was having was whenever a voxel in the top layer turned on, the same voxel in the bottom layer would turn on as well, but at a much reduced intensity level. I still don't understand why this happens for only these two layers, but changing the display function made it go away. I haven't yet decided how I will finally implement it, but I will probably go with some kind of time based delay using either milli() or micro().

Thanks for the tip.

PaulRB:
You only need to do that 8 times to refresh the cube once, and only refresh the cube ~100 times per second to avoid flickering. So that's around 800 layer changes per second. Doing it more often, as your code is doing now, could make the ghosting worse.

So using millis() is just perfect - either wait for it to advance by 1 to get a whole cube refresh of 125 Hz, or advance by 2 to get 62.5 Hz which is similar to the field rate of your TV. No need to use micros() at all.

There is no need to attach an image to the post here to show it - it is perfectly fine to host it on imgur or another reputable site, albeit with two caveats.

  • You must as I explained, post a link to the image, not a Web page.
  • You are at the mercy of the image posting site as to how long the image is available. You can argue that this site could be more reliable to host images or any other attachment used by posts on this site as other sites may not be so reputable or concerned about longevity. Notionally, if this site were to go down one would expect both attachments and forums would go do down together! :astonished:

PaulRB:
What can happen is that when you trigger the latch line ("SS" in your code), not all the SRs update at exactly the same instant.

Really? How could that happen? :astonished:

Paul__B:
Really? How could that happen? :astonished:

I really don't know. I would have thought any discrepancy between the times that shift registers take to propagate the update to their outputs would be so incredibly small that it would not produce any visible ghosting. But in similar forum topics to this one, that was the conclusion of more experienced forum members than me. The fix was to use OE to blank the outputs while the new data "settled" in some way before re-enabling the outputs. Why the Latch signals cause different propagation delays in different chips but OE signals do not, I don't understand. As in this topic, the latch and OE lines in those previous topics were shared between the chips and connected to the same Arduino pins. Maybe it was bunkum, but I didn't feel I had the level of experience to call it out!