# Algorithm for simulating PWM through 595's

I am trying to get a sort of pwm effect in an led matrix controlled by shift registers. I don't need super fine control, so I'm only shooting for around 16 brightness levels.

For a 24 row matrix, going for 100Hz refresh, I need to drive rows at 2400Hz. To get 16 distinct brightness levels, I need 16 times that number, right? That means 38.4kHz, which shouldn't be out of range even for an arduino or similar.

That all makes pretty good sense to me, but what I am not sure of is the best way to control the various brightness levels. I will need a distinct 4 bit number for each bit in the array, and a counter that keeps track of 16 cycles.

If I am at brightness 7/16, I could just light it up for the first 7 cycles like so:

11111110000000000


Or I could try to space it out somehow like this:

10101010101010000


Will there be a visible difference between these two approaches? I am leaning now towards the first one for ease of implementation, but if the second (or some variation on it) has a nice algorithm for different values, then it might be worth looking into. Does it even matter? Is there a better way to acheive this kind of thing? I have looked into led matrix ic's, but they are pricey, and tricky to integrate with my 2 color matrix. 595's are cheap and simple.

-

I am leaning now towards the first one for ease of implementation, but if the second (or some variation on it) has a nice algorithm for different values, then it might be worth looking into.

There is, and it's fairly simple.

Given a duty cycle as a rational fraction p/q between 0 and 1, do the following (pseudo-code in Javascript)

var state = 0;
// q should be a positive constant, but vary p between 0 and q
function pseudopwm(p,q)
{
state += p;
if (state < q)
return 0;
state -= q;
return 1;
}


To get 16 distinct brightness levels, I need 16 times that number, right?

Sort of. The human perception of brightness is close to logarithmic, so the perceived difference between 0 and 1/16 duty cycle is huge, whereas the perceived difference between 15/16 and 16/16 duty cycle is very small.

-
That looks great for a single output value, but I will be outputting 48 columns for each of q states. Do I need to maintain a state variable for each individual bit in my matrix, in addition to the brightness value? –  CMP Nov 7 '11 at 16:47
yes, you'd need to maintain a state variable for each individual bit in the matrix. That's a drawback between PWM (extra state variable = 1 ramp, can be shared between all outputs) –  Jason S Nov 8 '11 at 0:35