February 17, 2013

LED Cube: Smooth BAM-style brightness modulation

I already explained that I use Bit Angle Modulation (BAM) in contrast to Pulse Width Modulation (PWM) in the logic which controls the brightness of individual LEDs in my LED Cube. During the implementation of the brightness routines in my source I realized that it worked pretty as expected at high refresh frequencies but I got very weird results for lower refresh rates.

Especially if I set the brightness to 50% (decimal 128) the LED had a clearly visible on/off-pattern of 4Hz in when running the cube at 1kHz. The explaination of this is pretty straigtforward: when the brightness is 128, standard BAM causes the LED to be active 50% of a full 256-cycle. 1kHz has ~4 full cycles and therefore the LED turns on and off ~4 times/sec.

Wouldn't it make more sense to have the LED alternate on each refresh, so that the average brightness is still 50% but the LED turns on and off with a period of two refreshes instead of 256? Similar to PWM but without the requirement for very high modulation rates?

My overall solution to tackle this issue was to add a simple counter which is increased with each refresh. When it reaches its maximum at 256 it's reset to 0 again. I use this counter to look up a bit-value in a table, which results in the bit to check for in the brightness value in each cycle. I then use this bit to AND it to every single brightness value of each LED and if it's non-zero the LED is turned on for this refresh. The initial values for this lookup table indicate the bit-values and resulting on-time for each bit of the brightness value. See the original code here. Of course this version still suffers from the problem described above, if the value is 50% the LED is on for the last 128 cycles of the 256-cycle period which leads to a slow and visible on/off blinking effect at low refresh rates.

The remedy for this is simple though, as the check-values are already in a lookup table. Just distribute the check-values much more evenly in the table and split up the connected on-time for each BAM-bit across the 256-cycle period. The resulting table is this. In this table every bit-value is strictly evenly distributed and spaced relative to each other so that the same values always have the same period.

The result of this change, which involved the usage of some LibreOffice Calc trickery, now allows for much smoother brightness control at much lower frequencies. Though there are limits to this. If the brightness is 1, it will still cause a visible 4Hz blinking at 1kHz refresh rate of the cube. There's no way around this besides limiting the minimum refresh rate to higher values. But in contrast to expensive PWM and unmodified BAM modulation, this variant should reach a relatively regular ~32Hz flickering already at a brightness as low as 8 (~3%).

So in pseudocode the looping and brightness algorithm for the LEDs works as following:

int counter = 0;
int bamBit = 0;

while (true) {
  // calculate if each LED is on or off during this refresh
  bamBit = getBamBit(counter); // get current BAM bit from lookup table
  for(int layer=0; layer<COUNT_LAYERS; ++layer)
    for(int x=0; x<COUNT_X; ++x)
      for(int y=0; y=COUNT_Y; ++y)
        ledsActive[x][y][layer]  = ledBrightness[x][y][layer] & bamBit;

  // display this refresh
  displayCube(ledsActive);

  // reset counter at end of whole modulation period
  if(counter > 255)
    counter = 0;
}

Update: I'm not sure if this really works in practice as it should in theory. Until today I've used a not perfectly even distributed version of the lookup table (see this version) which I smacked together by just mixing up the values in a much simpler way (splitting the blocks and putting them together in a more distributed fashion) which was much quicker to come up with. Nevertheless I have the impression that the flickering has not smoothened at lower refresh rates and brightnesses as expected. I'll have to look into that sometime in the future again and make a better comparison of both versions. But yet it's still much better than the unmodified BAM brightness algorithm.

February 10, 2013

LED Cube: Status 2013-02-10

Just a quick status update on the progress of the cube since my last post.

On the hardware side there has been activity on two different topics. At first I had the opportunity to analyze my circuit and try to find out what's causing the ghosting effect at higher framerates using a friends oscilloscope. This involved getting comfortable with the usage and application of an oscilloscope in general and then trying to find out the different effects of different measurements and several different wiring variants of the transistors on the board.

From LED Cube Images
From LED Cube Images

This took a good part of a Saturdays afternoon and in the end I was pretty confident that the cause of my problem is that the base of the PNP transistor is not recovering fast enough to the blocking voltage as the IC leaves the pin floating after disconnecting it from GND. Attempts to remedy this with pull-up resistors allowed me to increase the refresh rate of the cube about 10x up to ~1kHz without noticeable ghosting but that's not really sufficient for my taste. My target is to allow up to 50kHz refresh rate on the 3x3x3 cube without noticeable afterglow of the LEDs. On the internet I found several topics where similar problems are discussed and all of them seem to have a common solution: add a Schottky diode to bring up the base voltage level faster. I had no schottky readily available so I unsoldered one from an old PC motherboard and used it to cover one of the three LED layers on the breadboard. Results are promising so far, I hope this still works when all three layers are equiped with a schottky. For now I think I can manage to work with lower cube refresh-rates until I'm able to pick up correctly dimensioned schottkys for all three layers. Until then this will be the situation on a single transistor at 5kHz (Yellow: base current; Green: collector current):

From LED Cube Images

Update: Short explaination to this graph. It was measured by hooking the oscilloscope to the base and collector of one layer PNP transistor. Pull-Ups are in place. Clearly visible (by the "knobs") are the layer switches causing some disturbance each time. When the layer gets activated the transistor instantly switches to conducting mode. But when it is disconnected again, it requires about half of the next layers visible time (~2us) to completely go into non-conducting state again. The faster the refresh-rate the longer the afterglow in each layer...

The last few days I've also been tingerking and thinking of how I could build the cube skelleton. I found no practical way to solder the LEDs together in a stable manner without adding additional wire. So yesterday I purchased a bit of iron wire and tried to solder the first layer. I drilled a few holes in a wooden plate, put the LEDs in it and soldered the LED pins together where possible.

From LED Cube Images

Next I prepared the iron wire, straightened it, cut out the parts...

From LED Cube Images

... prepared them for being soldered together with the LEDs to build the first layer...

From Building LED Cube

... and failed miserably, because the solder did not stick to the iron wire

From Building LED Cube

And I have absolutely no idea currently, what went wrong. Obviously another gap in soldering knowledge which I have to close when I want my next attempt for creating the LED grid to be successful.

At least on the software side there has been much more progress and success. I created a Github project for the LED cube driver software and improved and refactored the whole code several times now. So far I'm pretty confident that I created a quite flexible architecture and codebase so that it should be scalable for the 8x8x8 cube with minimal effort. Furthermore the animation framework is pretty stable and capable enough that I was able to create a new animation from scratch (one where you can move the bright LED using the cursor keys) within ten minutes. Each animation is encapsulated in a single class and completely independent from the cubes real refresh-rate. Notable features of my code which are finished (for now):

  • smooth BAM (Bit Angle Modulation) for controlling the brightness of single LEDs
  • console status output and ...
  • ... keyboard control for the animations, I/O via ncurses library
  • flexible animation architecture, independent from cube refresh rate
  • stable timing framework for controlling animation and cube refresh rates
  • a small set of prototype animations (whole cube pulsating, a single linear moving light, single random LEDs pulsating, a keyboard-controllable light)

February 5, 2013

Homemade fluid solder flux

When I tried to solder IC components for the first time one of the mistakes I made was that I did not use solder flux during soldering. The results (inevitable failure) can be read in the correstponding blog post.

Nevertheless when I tried to find out which solder flux to buy I realized that this was a pretty expensive attempt as commercial solder fluxes sell in almost unrecognizable tiny quantities at extremely high prices. Think of something like 10EUR for just 10ml and then this stuff is often extremely poisonous and should only be handled with good ventilation. The one I had a look at initially had three pages of health-instructions in its data-sheet. Inspiring confidence.

Well, I decided to don't go that route and pick up good old colophony ('rosin') which was the primary flux component for decades. What has been good in electronics 20 years ago should be more than sufficient for my purposes. And it's pretty cheap, I could pick up 20g for just 2EUR. I tried soldering SMD again using colophony and of course that time was a success (granted, also using a clean solder tip that time). But it was a bit cumbersome as colophony is in cristaline form and I had to scratch a bit of it for usage which also involved tiny bits and pieces flying off in all directions.

So I decided to take a hint from somewhere (don't remember where), solve it into some isopropyl alcohol and fill it into a small flask with a pipette I still had at home from some overdue nosedrops. This should make a decent selfmade liquid solder flux for hobby purposes. Picking up IPA was no problem, I got 100ml for 3.85EUR at the local drugstore.

From Homemade liquid solder-flux
From Homemade liquid solder-flux

Then I scratched up a larger portion of the colophony so that it was in tiny chips and dust...

From Homemade liquid solder-flux

...and poured it into the flask which was half-filled with the IPA. The larger parts of the colophony took some time to dissolve (barely visible in the photo). I added colophony until the solution had a viscosity which was good to use on PCB.

From Homemade liquid solder-flux

Using a drop of this solution on the IC pins before soldering worked like a charm and I was able to solder the IC onto the PCB without much hassle (can be seen in my first test posting).

One note though, the usage of colophony leaves back some light brownish residues after solding. Those are completely inert, do not interfere with the circuit and cause no harm. To the contrary it has even a certain conservation effet. If desired those residues can be removed also with isopropyl alcohol (still got plenty of it left from the purchase, remember?) but I decided to leave it on for the nostalgic effect ;)

Some warning notes: Although colophony is far from being as poisonous as other fluxes, the fumes should not be inhaled and skin contact should be avoided as it can cause allergies. Make sure the soldering place is properly ventilated and do not touch the fluids. If it comes onto your skin, wash it off with lots of water and soap.

February 1, 2013

LED Cube: Finished 3x3x3 prototype circuit, first hardware issue

In the last few days I've spent some time in the evenings/nights to set up a complete prototype circuit of the 3x3x3 cube on breadboard and create the first parts of the driver software.

There have been some minor issues (e.g. realizing that the larger breadboard has a different layout and connection scheme than the small one or running out of wire jumpers and cables) but in the end I managed to build the circuit on the available larger breadboard space in its full beauty.

From LED Cube Images

Starting the tiny test application which I wrote for the first IC test circuit even brought up the desired result on the first attempt so I've been pretty happy with the initial outcome.

From LED Cube Images

The next logical step was of course to enhance the driver software to allow for more sophisticated patterns and also do measurements on how big the impact on the multiplexing performance would be. Well, before I could finally finish that measurements the first non-expected issue manifested and left me puzzling.

I set up the cube pattern to light up the voxel (1,1) on the first layer, (2,2) on the second and (3,3) on the third layer and removed all delays to perform the multiplexing at maximum speed. Let's say the result did not exactly represent what I had in mind.

From LED Cube Images

When I limited the multiplexing to ~10Hz everything looked ok and only one LED per layer was lit but already at 100Hz additional LEDs became dimly lit and with 1kHz it became pretty clear that there was a speed-dependend issue, as on every currently active layer the corresponding LED which was active on the previous layer still received some current which increased with multiplexing speed.

From LED Cube Images

I re-checked the circuit for obvious and not-so-obvious problems (using my limited knowledge) but there were no aparent issues. Since the LED driver ICs are rated up to 30MHz my only possible explaination of this effect is that the transistors for some reason do not turn off fast enough after a layer switch. I'm somewhat sure that it's not based on the transistor type itself as I can't imagine that a standard transistor can't even manage to switch fast enough to produce a clean 1kHz signal. These are PNP transistors, so it allows current to flow E->C as long as B is connected to GND (or at least much lower than E). The LED driver IC is, according to its data sheet, fast enough when disconnecting the OUT pins from GND so that there should be no recognizable delay caused by this component. So my current understanding brings me to the conclusion that there is a small timespan where there is still current flowing out of the B of the transistor even if the OUT pin on the IC is not active anymore. Has simply the wire itself a capacitance which is just large enough to cause this?

In the meantime I've found a more-or-less acceptable workaround for this (thanks to my work colleagues for throwing some ideas back and forth with me), but more on that in a later posting. Yet, the underlying cause for this strange behaviour is still not clear and I'm determined to understand and resolve it eventually.