(Mirror Image) Bit Angle Modulation issues

Hi all,

Some weeks ago I released my first version of my universal LED cube library, topic HERE. Currently I added a software brightness control. However I'm experiencing some problems with smooth fading.

The technique I'm using is called Bit Angle Modulation (at least that's how I found it). It's quite simple, you run a timer on a fixed frequency. Within this timer you will keep track of a counter which is increased with 1 every interrupt until equals a specified bit value and then is reset to 0. This end value will double every time the counter hits the current value(1...2...4...8...repeat) it will evaluate different ON or OFF states each interrupt according to the specified brightness. So lets say we have brightness 10, that means that bit 1 and bit 3 should be on (2 + 8 = 10) and bit 0 and bit 2 to be off. So there will be 15 interrupts in total to have evaluated bit 0, bit 1, bit 2 and bit 3. You will get a signal that looks exactly like the image below.

More info about BAM can be found HERE.

As you can see this will ensure that the LED will be dimmed, and indeed it does dim very well.

But here comes the problem... Whenever I try to fade (so cycle through brightness 1 to 15 and back again) the LEDs I will get a funky blink effect. So last week I hooked up my oscilloscope and found out an interesting issue with Bit Angle Modulation.

Lets say we are changing the brightness from 8/15 to 7/15. For brightness 8 bit 0, bit 1 and bit 2 will be off and bit 3 will be on. For brightness 7 bit 0, bit 1 and bit 2 will be on and bit 3 will be off. When we combine those two cycles we will get a signal shown in the image below (upper part). As you can see the ON period will be 15/15 for a very tiny moment, the next cycle will of course be normal again. Now when we change the brightness from 7/15 to 8/15, exactly the same will happen however the signal will be mirrored (lower part of the image below), what results in a ON period of 0/15 for a very tiny moment. I thought that this was causing the funky flicker I was seeing.

With this in mind I googled and found out someone that had exactly the same problem and had the same conclusion as I have (CLICK for topic). In that topic they seemed to found out a solution called Mirror Image Bit Angle Modulation. Simply said, this technique is doing exactly the same as the normal BAM however it is reversing the bit order every duty cycle. For more information how that works see that topic.

So with that in mind I did implemented a mirrored bit angle modulation for a single LED to test out if it removes the flicker. And in my case it didn't remove the flicker completely, however the people from the MIBAM topic seemed to fix the flicker with MIBAM. So I started drawing the expected signal in my notebook and verified it with my oscilloscope. And indeed I think MIBAM won't fix the flicker. Consider the image below. As you can see when we change the brightness from 8 to 7 (or vice versa), you will get a significant lower OFF time then a 'normal' cycle will have. I did put a red circle around the faulty OFF time and a green circle around the correct OFF time. As you can see when changing brightness you will come short exactly half the required OFF time to prevent the funky blinking.

I think MIBAM isn't that great at all, of course it will eliminate some of the funky blinking, however it also needs twice the required refresh frequency than the normal BAM.

I know only of one solution that will definitely work, that is cranking up the refresh frequency. When this is very high you won't notice that one 'faulty' cycle at all. While this will work for a small amount of LEDs, I think I can't get it to work at a higher amount of LEDs, for instance a 8x8x8 LED cube. Does anyone have any suggestions on the BAM technique, or maybe something that I'm missing with the MIBAM technique (as it seems to work for other people...). Thanks for your time!

EDIT:
Images are quite small, it's best to open them in a separate tab page.

Hi,

It sounds to me like the problem is caused by attempting to adjust the brightness levels part way through the BAM cycle. Can you change your code so that brightness levels are adjusted only at the start of each BAM cycle?

If that is not possible because the processing would take too long and cause a delay at the start of the cycle, you could maybe get around this by using the "double buffering" technique. This would use twice as much ram memory to store the curent brightness levels, but it means that your code can be using one copy to send data to refresh the cube, while another part updates the other copy to animate the patterns. At the start of each cycle, the two pieces of code swap over to using the other copy of the data.

Hope that makes sense, just ask if not.

Paul

Well Paul, I indeed noticed when I was scoping that the value is sometimes changed within the duty cycle itself, causing it to be longer ON than it should be. I tackled this problem with a singular LED by copying the brightness value after a duty cycle was finished. This fixed the random flicker on different brightness levels, however the brightest and most annoying flicker on brightness level 7 to 8 or vice versa still remained, and I think what I posted above is causing it (I'm not sure though because I don't know a solution for it other then MIBAM which doesn't seem to work).

I can only think of shifting the bits location inside the duty cycle so that the ON time won't be 15/15 for a tiny moment when the brightness is increased to 8. This will not remove the problem as a combination of different values will give you the exact same result. Another solution could be that I break up that largest bit, that has influence of 8/15 of the total duty cycle, into smaller chunks (say like 2/8) and place them randomly between the other bits.

I first like to have it working on a singular LED so that I can see if the used method can be achieved. For instance I doubt if I can get the double buffering to work on my 8x8x8 LED cube as it will have major impact on the performance if you have to copy the entire frame after each cycle (because the other frame does not contain the same values as the one used for showing the output), correct me if I'm wrong. The only way I could think of is to buffer the user input (so the x, y, z and brightness) and place the values of that buffer to the main frame buffer when a cycle is completed. This should, in theory, cause less delay as only the actual changed voxels of the cube are updated instead of the entire frame buffer itself. But like I said I first want it to work on smaller scale with a singular LED to see if it is even possible to eliminate the flicker.

Thanks for you post though, it was stupid of me to not post that I did work out this issue already, but hey now we know :smiley:

WonderTiger:
I tackled this problem with a singular LED by copying the brightness value after a duty cycle was finished. This fixed the random flicker on different brightness levels, however the brightest and most annoying flicker on brightness level 7 to 8 or vice versa still remained

Ah, right. I did misunderstand the issue then. Re-reading your OP, I think I get it now.

WonderTiger:
I first like to have it working on a singular LED so that I can see if the used method can be achieved.

I'm thinking that you might see the issue with a single led but not see it in the cube: after your code has completed a BAM cycle, does it not move on to the next cube layer, causing a naturally long gap before any of the leds on that layer get lit again? What is your multiplex strategy? Do you switch layers at the end of a 4-period BAM cycle, or in between each period of the BAM cycle? (i.e. layer1: period:1,2,4,8, layer2: period:1,2,4,8 etc. or period1: layer:1,2...8, period2: layer:1,2,...8 etc).

WonderTiger:
I doubt if I can get the double buffering to work on my 8x8x8 LED cube as it will have major impact on the performance if you have to copy the entire frame after each cycle

No, you wouldn't copy one frame to the other. You would simply switch over which copy the refresh code was using and switch over which copy the animation code was using.

For example, if you current stored the cube as "byte cube[8][8][8];", you would change this to "byte cube[2][8][8][8];" where that first index is the frame buffer/copy number. Your refresh code would start off accessing index 0 and your animation code accessing index 1. When a full multiplex cycle ends, you would then swap over the indexes so that the refresh code accesses index 1 and animation code accesses index 0.

I do see it in the cube as well, in the code of the cube I only added the BAM modulation but no double buffering or MIBAM. The way it works is I go through the layers on a fixed frequency. Every time we cycled through all the layers we increase the BAM counter. So it will be BAM bit 0 (1 cycle) layer 1, layer 2, layer 3, etc... then BAM bit 1 (2 cycles) layer 1, layer 2, layer 3, etc...

The double buffering technique you will get you a decrease of brightness of 50% right? Because buffer 1 won't contain the same voxel states as buffer 2 which results that the voxel will be on for 50% of the time in buffer 1 but will be off for 50% of the time when buffer 2 is used. Maybe I'm wrong on this please tell me if so.

I will post the code later this day, I think that will be useful.

WonderTiger:
I do see it in the cube as well

I don't understand why. In your diagrams above, you explained it in terms of an led being on for 15/15 periods (7 periods from one BAM cycle immediately followed by 8 periods from the next BAM cycle. But i,f after 7 periods, the cube moves on to the next layer, it would be a long time before that same led is lit for the other 8 periods. Do you see what I'm saying?

WonderTiger:
The double buffering technique you will get you a decrease of brightness of 50% right?

It would have no effect on the brightness whatsoever. Obviously I still have not explained the idea well enough...

Okay let me explain it in detail. First forget the whole bit angle modulation. What I'm doing is cycle through each layer. This means we start at layer 1 and increment a layer variable with 1. Next time the interrupt is triggered we will turn on layer 2 and again increase the variable with one. We do this until we hit the last layer, then we will reset the layer variable back to (layer) 1 again. This will mean we have an 1/8 duty cycle just by going through these layers. Also if you have an 8x8x8 cube with refresh frequency of 8kHz this means every layer is updated with frequency of 1kHz. Now we add a bit angle modulation with a bit depth of 4 bits. Now we're still cycle through the layers with a speed of 8kHz. Every time we completed the cycle through the 8 layers we increase the BAM counter with 1. When this bam counter hits a bit value it is reset to zero , this bit value ranges from 1..2..4..8. Depending on which bit value we are we and which brightness we want we set the LED on or off for the length of that bit. So essentially a single cycle of the BAM + 8 cycles each BAM counter increase will give us 81 + 82 + 84 + 88 = 120 interrupts. This will still result in the same signal as I drawn in the imaged except that every brightress step will be on for 1/8 of the time and off for 7/8 of the time, this is due the refresing of the cube. This refreshing is executed on a fixed rate and only affects the totaL brightness. This does not take away the problem of switching between 7 and 8 brightness as it will still be on for a longer period of time. I'll hope you understand it better now, anyway I will post an oscilloscope screenshot tommorow, 1 picture says more than 1000 words :stuck_out_tongue: (will post the code as well).

About the double buffering. As I understand you have two frame buffers. One is used as buffer to set the LEDs on or off for the current BAM duty cycle. The other buffer is then used to store any new user input while the current BAM cycle is still not finished. Once the BAM cycle finishes both buffers switch roles. The problem I see. Let's say buffer 1 and 2 contains the on state of the LED on position <0,0,0>. The BAM cycle starts, buffer 1 is used for the displaying the data on the cube and buffer 2 for new user input. While the BAM cycle is still busy the user adds a LED with an on state on position <1,0,0>. This means buffer 2 contains two on states on position <0,0,0> and <1,0,0>. Finally the BAM cycle completes and the roles are switched between buffer 1 and 2. This results that the LED cube is shining two LEDs at positions <0,0,0> and <1,0,0>. While this BAM cycle is busy the user adds another on LED state at position <2,0,0>. This means buffer 1 now contains two on LEDs at positions <0,0,0> and <2,0,0>. Finally the BAM cycle completes again and the roles between the buffers are reversed again. And here comes the problem buffer 1 now don't have any knowledge of the LED on position <1,0,0>. The same goes for buffer 2, it doesn't anything about the LED on position <2,0,0>. This is because neither of those values were copied when we switched the roles of the buffers. Maybe I'm missing something critical with the double buffering technique if so let me know :D:D.

Your long and very patient explanation of the multiplexing strategy didn't help much I'm afraid. I understood it and it was one of the two strategies I was imagining you would have used, but I still can't mentally picture how it leads to the flickering you describe. At the speeds you are talking about, i would expect the eye to percieve the % of time each led is on vs. off, and the effect your theory describes would be too fast to perceive. Perhaps those scope images will help.

The double buffering thing you are concerned about. I was imagining that the animation code would redraw the complete contents of its buffer each time, perhaps because it is retrieving this from flash memory or an sd card or an encoded algorithm that calculates the coordinates of each led that should be lit, having first cleared its buffer before starting. That way it would not keep forgetting about leds it had lit on previous frames. But if it did need to do that, as in your example, it could read from the other buffer, modify and write to its own buffer (there is no reason it can't read from the other buffer as long as it doesn't write to it). With this method, one buffer is being copied to the other, but not between frames in a way that causes a delay in the multiplexing.

Okay I've made some oscilloscope images of my 4x4x4 cube running on 4kHz. In the first image you see 2 signals the blue one represents the start of a new BAM duty cycle and is bit 0 in the BAM series. As you can see this LED will have a brightness of 10.

Every pulse in the BAM cycle is on for 1/4 of the time and off for 3/4 of the time. This is caused due I multiplex through layer 1 to layer 4 and only on layer can be activated at the time. I'll made a close up image of this pulse to verify on by time base. As you can see in the period is 1ms and the off time width is 750us. This means that the pulse has an on width of 250us which is exactly 1/4 of the multiplex frequency I'm using I told above.

Below I've added an image of one wrong duty cycle, the brightness starts with a value of 8. Exactly after this cycle the brightness is switched to 7. This action is causing to momentarily have a brightness of 15/15. As the next BAM cycle starts the brightness values are correct again.

I'll hope these images clarify the problem :D.

Well currently I have a buffer you just fill with LED states. The interrupt is then retrieving data from the buffer and draws it on the cube. When I run two buffers along each other both should know the same LEDs states otherwise a LED state which is put in buffer 1 will not be drawn on the cube when the ISR uses buffer 2, which doesnt know that buffer 1 had received a new LED state because it was used for drawing the LEDs states for the current BAM cycle, and vice versa. I'll included the whole library so could also take a look at that. The main file will be LedCube.cpp

LedCube-1.zip (14.9 KB)

Well, its all very impressive! Is this running on Uno or Due?

My only suggestion at the moment, and I'm not at all sure how much it will help, is this:

The 4x4x4 cube is refreshed at 1KHz, layer switching is at 4KHz. In the BAM cycle's first period, the cube is refreshed once, twice in the second period, 4 times in the third period and 8 times in the 4th period. The BAM cycle takes 16ms in total i.e. 62.5Hz. Am I correct so far?

How about speeding up the BAM cycle at the expense of the refresh cycle? Complete one BAM cycle before moving to the next layer. BAM period 1=250us, period 2=500uS, period 3=1mS, period 4=2ms.So BAM cycle 4ms or 250Hz. Switch to new layer every 4ms, cube refreshed every 16ms, i.e. 62.5Hz.

If this causes the cube to flicker sightly (like old CRT TV sets), do you have scope to halve all the periods, i.e. BAM period 1 is 125us?

You can run this on almost any arduino device, its only limited to the memory available on the device and the supported pin conversion. The first version used the digitalWriteFast library. However the direct port instructions are way faster. So I decided to predefine a digital pin to port pin table. So you're also limited to these tables, almost any arduino is supported. But to be sure take a look in the pinConversion.h file.

I myself use a atmega 1284P for my 8x8x8 cube to run more animations because of the more onboard memory.

I did benchmark the 4x4x4 cube (arduino uno) and it can succesfully run up to 30KHz. The 8x8x8 cube will run up to about 15KHz. I think I've got enough room to play with different update and BAM frequencies.

The the technique what you suggested is pretty smart! This way you always have a long off time between the next BAM cycle. This might just work, Im curious to try it out. Once Ive implemented it I will post the results. Thanks!!!

Another strategy to consider, which would be a compromise between the above and your current strategy, would be to switch to the next layer after every BAM period. This would mean that the layer switching would change from 4KHz to 2 KHz to 1KHz to 500Hz and back to 4KHz again for the next BAM cycle. So the cube refresh never falls below 125Hz and you still have long gaps between the BAM periods from the point of view of any one layer.

Good luck with getting it sorted.

I have only built 4x4x4 cubes myself, and used max7219 to drive the leds. Makes the whole thing very easy. Next I might build an rgb 4x4x4 using the 5mm thru-hole versions of the ws2812b leds.

I can't face building an 8x8x8 though, you must have a lot of patience! Or you have figured out a quick way of constructing the layers?

What model of Rigol do you have there? Would you recommend it, and do you often wish you had purchased the next model up (assuming its not already the top model).

Thanks I'll could indeed try that as well.

The 8x8x8 cube took a lot of time to build. However once you finish it you will have a lot of satisfaction. Soon I will build an 16x16x16 RGB cube as final project for my EE study. I will construct that one differently than my 8x8x8 though. I was thinking about making some kind of thin column strips with smd LEDs. This would ease the construction of the cube.

About a month ago I bought the rigol ds1054z and until this day I do not regret the purchase. The build quality is very nice it has a nice large screen and a nice user interface. Besides that you can fully upgrade (read hack :P) it to a higher end oscilloscope which unlocks higher bandwidth logic analyzing functions etc...

Hi Paul,

So I'd changed the way the BAM cycle works to what you suggested. I've also did some more research into the double buffering (page flipping) technique and implemented that as well. I'm using two buffers, a front and back buffer. Once the users calls the drawVoxels() function to notify that an animation is completely drawn the front and back buffer will swap memory addresses. The function also supports vsync (very basic one, but it works :P), so that the buffers are swapped at the end of each multiplex cycle. This will prevent any tearing and other artifacts.

With the double buffering technique and the changed BAM cycle I have no weird flicker whatsoever. In my large 8x8x8 cube I only notice the steps between the brightness increase. This is obviously caused by the limited resolution as it is only 4 bits, which means there are only 15 steps between off and full brightness. When I increase it to 5 bits resolution my refresh frequency will be too low. I optimized the ISR the best I can, but I can't get it to reach beyond the 12KHz range(on the 8x8x8 cube), maybe that you have any idea to further improve my ISR to save some cpu ticks. If not I'm still very happy with the results on a 8x8x8 cube with 4 bit resolution on a simple arduino haha :P. Thanks again Paul, I'm very pleased with the suggestions you made!

Greetings,

Mike

LedCube.zip (16.6 KB)

Well, your coding skills are ahead of mine, so its unlikely I will spot anything you have not though of, but...

In refreshData():

	for(uint8_t i = _layerArrSize; i-->0;) 
		SPI.transfer(_voxelMappingFrontBuffer[ i + _zPositionCounter * _layerArrSize + _modulationOffset * _cubeArrSize]);

I don't know how clever the compiler's optimisation is, but might it be re-calulating this index each time around the loop?

How about something like:

        int j = layerArrSize +_zPositionCounter * _layerArrSize + _modulationOffset * _cubeArrSize;
	for(uint8_t i = _layerArrSize; i-->0;) 
		SPI.transfer(_voxelMappingFrontBuffer[j--]);

Indeed that is calculated each iteration of the loop, which isn't needed at all! I placed it outside the loop now, thanks again Paul :P.

I don't know if I gain a performance increase as the compiler maybe already optimized it for me, but it doesn't hurt to explicitly set it outside the loop.

Well I think that did the trick, I can just barely run the 8x8x8 cube on 5 bit modulation which gives me a nice smooth fading effect. After all this is the end result: VIDEO.

Very cool!