# 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? – captncraig 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

If you want to get sixteen linear brightness levels, and if either your PWM rate is high enough or you don't need to change lights' brightness levels too often, you can get by only sending four updates per frame. The trick is to send a frame of data which will output for 8/15 of a "PWM interval", a frame which will be output for 4/15, a frame which will be output for 2/15, and one which will be output for 1/15.

If sending two frames of data separated by only 1/15 of a PWM interval would be difficult, one could use the output-enable control signal to shorten the length of time that the 'short' frame is output. In a somewhat extreme version of such a scenario, one could send out one frame for 8/32 of a PWM interval with the output enabled the whole time, one frame form 8/32 of a PWM interval with the output enabled for half of that (i.e. 4/32 of the entire interval), one frame for 8/32 with the output enabled for a quarter (2/32), and one for 8/32 with the output enabled for an eighth (1/32). This would mean that even at maximum brightness the output would only be on about half the time, but the output shift rate would be uniform. One probably wouldn't want to push things quite that far in the direction of uniform frame rate, but one could easily formulate other patterns which would trade off maximum shift rate for maximum duty cycle.

Another alternative would be to use four sets of 74HC595's, and arrange for some logic to have one of them output half the time, one of them output a quarter of the time (while the first was idle), one output an eighth of the time (while the first two were idle), and one a sixteenth (while all three of the above were idle).

In any of these methods, it's important to note that updating the brightness setting of an LED may cause it to momentarily be slightly brighter or dimmer than it should, especially between consecutive settings in which many bits flip (e.g. going from 7--0111 to 8--1000). This effect may be minimized by splitting the time interval associated with the more significant bits into multiple smaller pieces, but such splitting may be more readily accomplished in hardware than in software. If one can't afford a "pure hardware" solution, however, one may be able to use a hybrid solution (e.g. two 595's per bit; one gets loaded once per 'frame' with the MSB of the brightness, while the other gets loaded multiple times per frame, with the othe bits). Using two 595's would cut by half the required data update rate; using three 595's would cut the update rate by a factor of four. Using four 595's for 16 levels of brightness would cut the required update rate to zero (since hardware could maintain the "pwm" automatically).