Ring of Fire – Simulator

November 28, 2007 at 4:20 pm

To make some progress before my parts arrive (they showed up yesterday), and to have a good way to test my animations, I decided to build a simulator.

The simulator is build in windows forms, and is intended to prototype both the approach I’m going to take in the real software. I’m also hoping I can steal code, as the real software is going to be built in C this time, rather than directly in assembler.

To build the simulator, I had to decide how I was going to encode the animations, and how the main loop would work.

Because of time constraints, my plan was to build this as a sequencing system rather than something involving dimming. So, I coded up a very simple scheme of encoding the output in 3 bytes:

Count byte            first 8 lights            second 8 lights

Where the light bytes tell you what lights are on, and the count byte tells you how long to stay in that state.

I then tried to write a “lights go around in a circle animation”. The table looks roughly like this:

0x08, 0x80, 0x00
0x08, 0x40, 0x00
0x08, 0x20, 0x00

0x08, 0x00, 0x01

So that’s 16 lines of entries, taking up 48 bytes for a very simple animation, with me doing a lot of bitwise math in my head.

I then decided to do a more complex animation – there’s one light on at the bottom, the other lights circle around, and give a whack to the fixed one, which starts moving. So, in essence, a fixed spot moves backwards as the main animation goes forwards.

A full cycle of that involves 16 full circle cycles, or 768 bytes. Each of which had to be hand-figured out, basically by overlaying a fixed mask on top of the moving circle animation. I got through two cycles through a lot of debugging and testing, and decided I’d had enough. It was like programming a system without any looping constructs.

So, I decided to abandon the table-based approach to take a programmatic approach. Well, strictly-speaking, it’s a hybrid approach, where the animation is controlled programatically, but the low-level stuff is handled on an interrupt.

I also decided to incorporate dimming, since I really wanted to. So, here’s the design.

Interrupt Handler

The interrupt handler executes N times for each cycle (arbitrarily set to around 1/100th of a second to avoid flickering), where N is the number of dim levels I want to support. Consider N to be 64 for sake of argument.

PWM Cycle

The lowest level operation is to implement the PWM loop. Each time the interrupt fires, a byte counter is incremented, and compared to the dim level array elements (one per led), and the output bit is cleared if the counter is greater than the dim level for that light.

So, we have this array of lights all turned on, each with a number saying how bright it is, and as the PWM cycle continues, lights get turned off as their dim level is reached. Those with a low dim level are turned off early, those with a high level are turned off later.

The current levels are stored in a

byte current[16];

When a PWM cycle is finished, we reset all the outputs to high and the counter to zero, and then move to the animation cycle to see what to do next…

Animation Cycle

The animation cycle implements changes to the current array values over time. This is done through a delta vector and a count. Basically, we have:

byte delta[16];
byte deltaCount;

and every time we go through the animation cycle (ie each time a PWM cycle finishes), we add the elements of delta to current, and decrement the deltaCount.

So, if current[0] was 63 (ie full on), and we want to dim it to zero and turn current[1] full one, we would set up the following:

delta[0] = 255;
delta[1] = 1;
deltaCount = 63;

The first time through the cycle, current[0] = 63 + 255 => 62, and current[0] = 0 + 1 => 1. We’re just adding 1 to current[1] 63 times, and subtracting 1 (by adding 255 to it) from current[0] 63 times.

If we didn’t want dimming, we could also encode this as:

delta[0] = 193;    // 256 – 63
delta[1] = 63;
deltaCount = 1;

That would flip from one light to the next in a single cycle.

Note that we can achieve a hold by clearing the delta array and setting deltaCount to whatever delay we want, which leaves current[] unchanged for that period.

After the animation cycle has completed, we need to get the delta[] and count values for the next cycle. We do this by copying from deltaNext[] to delta[] and from deltaCountNext to deltaCount, and then setting deltaCount to zero. That gives us the delta set, and we continue as before.

That’s all for the interrupt routine, but it rather begs the question – where did the values in deltaNext[] and deltaCountNext get set?

Main animation loop

In previous incarnations, the main animation was just handled in the same section of code as everything else – when the animation cycle ended, you’d figure out what you needed next (well, actually, you did it right before you needed it).

This has two whopping disadvantages.

The first is that you have to do it in the spare time between cycles. That’s not bad if you are doing simple animation (ie not dimming) or you’re doing dimming in hardware (which I’m not), but in this case there may not be enough time to do the main animation loop in the time left for an interrupt (at 64 dimming levels, about 156 microseconds for everything, including the interrupt routine).

The second is that you have to write the animation as a state machine – for any given set of counters, you need to know what the next delta[] should be. That’s not going to be much fun for more complex animations.

I therefore decided to let the main animation loop run in the main execution thread (well, there is only one thread, which the interupt preempts as needed). It therefore has the full animation cycle to come up with the next set of values (though, if the count=1, that’s the same as the PWM cycle), or 10 mS, and there should be enough time leftover to do the work I need to.

The main loop will use a blocking function to tell it when it needs to proceed. It’s:

void SpinWait()
{
while (deltaCountNext != 0)
{
}
}

So, we’ll just hang in that tight loop. An interrupt will come along, do what it needs to do, and if it’s at the appropriate point, deltaCountNext will be set to zero and when the interrupt returns, we can go on to generate the next values.

That allows the animation to be coded as something like:

SpinWait()
deltaNext[0] = 255;
deltaNext[1] = 1;
deltaCountNext = 63;

SpinWait()
deltaNext[1] = 255;
deltaNext[2] = 1;
deltaCountNext = 63;

which is a whole lot easier to understand, and you can even use this thing called a “loop” so that it’s easy to write. The simplification is roughtly analogous to how it is easier to write an enumerator using yield than the old way.

Back to the Simulator

The simulator implements this by having a timer fire off every 10 mS, and it calls into the interrupt code (which does everything except the PWM loop). The main loop runs in a separate thread, and everything is peachy.

That’s all for now. I think my next task is to write a few more simulations to make sure the code does anything, and then move the code to the microcontroller, hook up a scope, and see what I get.

 

Ring of Fire

November 20, 2007 at 11:39 am

No, not that

Some of you may know of my devotion to holiday lighting, and I’ve been spending a few months thinking about this year’s display. Last year’s windstorm did a number on a few of the displays, but most were fixable (the spiral tree that took flight was not…).

I wanted to build something new, something different. I settled on using an Atmel AVR as the microcontroller, and LEDs as my light source. I considered a lot of different designs, but procrastination and my unexpected encounter with the pavement have left me with less time and unable to do any heavy fabrication, so things had to be simplified.

In the front of our house, we have a tall tree (30+ feet (2.34 hectares)) that I believe is of the Norway Spruce variety. Last year is was decorated with a number of the 50-light globes, and a big white 200 light globe for the top. I lost about half the 50 light globes and the 200 light globe only has about 20 working, so I’ve decided that the replacement top ornament will be a ring perhaps 18 inches (1.66 decalitres) across with 16 red leds distributed evenly around the ring. It’s 16 because I think that’s a nice number, and it lets me use a simpler microcontroller since I only need to control 16 outputs. And using LEDs will mean that I don’t have to use solid state relays, which are a bit pricey…

Hence “ring of fire”

Here’s the part slist:

  • Atmel AVR ATTiny861 (8K Flash, 512 bytes EEPROM, 512 bytes (!) SRAM, 16 I/O pins, 20 MHz)
  • 20 LTL911VESKA LED (LITEON)
  • 20 2n3904 transtors
  • 20 43 ohm resistors
  • 20 10K resistors
  • 20 100K resistors
  • 1 100uF capacitor
  • 1 waterproof box

I chose the 861 mostly because it had 16 pins, and at $3, what’s not to like?

The LEDs are designed for sign and signal applications. They run at 70mA rather than the 20mA most LEDs use, and they have a wide viewing angle (ie the light is spread out rather than going straight ahead). The 43 ohm resistors will go in series with the leds to get that 70mA at 2V on the LEDs from the 5V power supply.

The transistors and 10K/100K will be used to switch the LEDs off and on. The AVR can sink 20mA of current, which is a fair amount, but not enough for the LEDs, and even at 20mA it couldn’t drive them all at once.

I think I have a 5V power supply that will work. The LEDs pull 2.24 watts when they are all on, but unfortunately at 5V the 42 ohm resistors will pull 3.36 watts, which puts me up near 6 watts.  If I drop down to 3.3V, 20 ohm resistors would give me 65 mA, and that would mean 2.08 watts for the LEDs but only 1.35 watts for the resistors, so I could get by with a 4 watt supply.

The AVR will run on anything from 1.8V to 5.5 V, though it won’t be quite as fast at the lower voltages.

Software is in two stages.

The first stage will be a simple table-based animation system. I’ve built it a few times before, and last night I built a simple simulator in winforms. That will get it up and running.

The second stage will be to add dimming to all the LEDs. That requires doing PWM on every channel, and I’m not sure that there is enough horsepower to do that in the AVR.

To do flicker-free dimming will require an update frequency of around 100Hz – the dimmest setting would have a brief spike of “on” every 10 mS. That period would need to be divided by the number of light levels – to do 256 would mean that the code would have to update the output state about every 40 microseconds. That’s roughly every 750 clock cycles at 20MHz, which seems possible but difficult.

On the other hand, 64 levels would increase that to 3000 clocks, and 16 levels to 12000 clocks, so it looks like some sort of dimming is doable. It helps that the table-driven code doesn’t use a lot of resources.

All my previous projects have been written in assembler, but I’ve decided to use CodeVisionAVR as the development environment, which gets me a C compiler and IDE.

I’ll post other updates as things progress…