Close

LED PWM Frequency, Brightness, & Bit Depth

A project log for D-DAQ

automotive parameter & performance monitor & logger

michael-obrienMichael O'Brien 05/24/2014 at 23:180 Comments

Unless you're new to HaD, you already understand why driving a LED with PWM is really good thing. If you're knew to this kind of thing, it's still relatively simple. It means that you can drive an LED with higher current without burning it out and if you can have a fast enough frequency, you will not see any flicker. However, if you're like me, it's far from this simple ;)

Now I'm designing D-DAQ's displays to accommodate several different types of LEDs. The problem is compounded not because they have different forward voltages, but because they have different steady state and peak/surge/max current limits as well as different pulse widths with those limits. from my minor collection of datasheets, the trend seems to be somewhat linear with a max pulse width of 0.1 ms, 10% DC, 10x steady state current. However, I do have a few outliers that have a max pulse with of 10 us and 6x steady state current. So, for the lowest common denominator and assuming 10% DC since it wasn't stated, it seems that I have to have a PWM frequency of 10 kHz. Thankfully, this is a relatively easy frequency to achieve with a PIC32MX.

There is a little bit of a problem though. As I mentioned in my log from yesterday, my solid state relays have a 10 ms lag before they turn on. I'd rather not go and find another SS relay and add it to Eagle, so using what I have on hand, I open up the datasheet to figure out if there is any way to speed this up. Well, as luck would have it, the 10 ms turn on time is the rated maximum. In the graphs and charts of the CPC1117N, 22 out of 50 units turn on within 0.32 ms and none of the graphs go beyond 0.7 ms for the turn on time for <1 mA drive current. At 10 mA, turn on time is no worse than 0.4 ms.

As for the signal to turn on the SS relay, I've altered my plans and will be using a single p-channel MOSFET on the accent/backlight PWM circuit. From it, I'll be running a 2nd order high pass RC filter in parallel to the accent/backlight LED load so it will always see 14 V, thus not requiring modification when changing the LED load. Now, since this is driving the warning light circuit, I want to ensure that that circuit doesn't come on until I've reached about 10% of the visual brightness of the accent/backlight LEDs. Even though I'm now dealing with a nominal 320 us turn on time, I'm dealing with a max of a 10 us pulse with an off time of 90 us between pulses. Unfortunately this means a simple 5x RC time constant will not suffice. Time to break out the circuit simulators.

I personally like using circuitlab.com. Yes, there is circuits.io surely others out there, but having a browser based simulator, plenty of parts and variants to work from, and a fairly decent UI makes this one my go-to choice. I'm sure it may have it's inaccuracies with the simulation engine, but we'll see how close it is when D-DAQ is constructed and I'm testing everything out.

You noticed how I said visual brightness a second ago instead of DC, right? My background in photography is bit more than a decade and I'm also red-green color deficient. Now, it should be apparent that I'm a detailed oriented person who doesn't mind getting into the technical details, even if I may be wrong. That being said, due to my color vision, I've developed a decent understanding of the human eye's anomalies with light. That being said, I figured it'd be somewhat useful to rung a Google search regarding the LED PWM brightness. Well, as soon as I saw the logistical curve, I knew I had to go a bit deeper into the PWM frequency of choice.

Though the PIC32MX can easily do a 10 kHz PWM signal, there is a relation to the PWM frequency and the Peripheral bus frequency that affects the bit depth at which you can control the DC. Many years ago I used to be active in the ticalc.org community and even with the power of C via TIGCC, the only way to code a fast 3D engine was to use an integer-based trig lookup table. You'd think that 0-255 would be plenty of depth (8-bits in case you missed it) for a curve, but if you're trying to do anything with precision, you'll quickly find that you'll get a heavy amount of aliasing on quick transitions of the curves. Now, looking at the logistical curve of the brightness plot, it's apparent that I'm going to need a fair about of bits.

There is another factor to consider: flicker. At the beginning, I mentioned the frequency to drive the PWM signal at. Now, how do you know if it is too slow? Have you ever been behind a car at night with their brake lights on and you've looked from one side of them to the other so that your persistence of vision allowed you to see the on time and off time of the taillight? Annoying huh? I certainly don't want that to happen with D-DAQ. I give special thanks to a friend of mine who's done some eye tracking stuff back in the day and he quickly confirmed that the human eye can move up to ~900˚/s. The displays I'm making will have an average distance from the users face of about 2 ft, give or take. My personal limit to having flicker noticed at this distance is something I'd set to about 1-2 mm. Recall that the minimum off time of the LEDs at 10 kHz @ 10% DC is 90 us? This works out to 0.86 mm when you run the trig. Going to 1% DC, that gets up to ~0.95 mm. If I doubled that distance, we'd be talking about 5 KHz, which has the possibility of being too slow. Lucked out there, eh?

Well, this next pondering is what brought me into this whole post. A max DC of 10% which means 10% of the available bit-depth, couple with the appropriate transfer function recalling those bits to fit the logistical LED PWM brightness curve. I had to pull up the datasheet for calculating the available bit depth of a given PWM frequency. Turns out it's tied to the peripheral bus speed. After running the numbers, I get about 12.67 bits or a highest value of 6547 and 10% of that is 654, or ~9.4 bits. If I were at 5 kHz, I'd have a max of 1309. As we're dealing with logarithms (human vision) the logarithmic median between those two is ~926 which gives me a wonderfully oddball frequency of 7.07 kHz. I know my 14 V rail isn't strong enough to drive everything at full power so derating LED currents is okay with me. Oh, and  dumb luck that it's a reminder of sqrt(2)/2, used for RMS calculations ;)

Anyhow, back to the original problem of an RC circuit turning off at of at a visual brightness of 10%. A common formula that has been derived for this calculation is listed here. After some trial and error I found 3 flaws that were easily fixed. First off the divisor, 21 in the example, is actually the rounded half of the max value, 255, divided by the offset value 6. Second, you'll never hit the max value due to the nature of a logistic function so you need to use the number of points you're plotting, i.e. 8-bit = 256. So, using the range 0-925, the max value 926, I solved for the divisor that would get me to 925 without having to round up, thus giving me the maximum dynamic range of the function, which turned out to be ~6.8446. I plugged 0 back in with the divisor at got ~0.9853. I subtracted half of this value to remove the offset and got my tabulated results.

Last note, if you wish to just run an 8-bit table, the divisor with the maximum dynamic range is ~5.5849 and subtract 0.4787 from the result to remove the offset.


Edit: If you choose a divisor greater than what I've stated the curve will become more dramatic and steeper. If you choose a values smaller, then it'll be more linear. Keep in mind though, that it is not possible to create a "linear" "curve" from this type of function. If you wish to use this function to play with or compute your own tables other bit depths:

=ROUND(($A$2)/(1+EXP(-(A4/($A$2/(2*$B$2))-$B$2)))-offset,0)

Where A2=points (which is 2^(bit depth) in case that isn't intuitive) and B2=divisor

To compute the divisor with the greatest dynamic range use:

divisor = -points * ln( 1 / ( points - 1 ) ) / ( points - 2 )


Edit 2: I was a bit mentally exhausted and distracted from the primary goal after running that math so I missed following through to the final conclusion: I need enough bits to scale the PWM DC smoothly enough to have visual brightness vary smoothly. 926 levels are nice, but I'm running at 10% DC, thus I'd only have 92.6 levels. In order to expand that to 100 levels, PWM frequency would be limited to ~6.547 kHz. I wish I could make the CPU faster, but at this time, the clock dividers and SPI frequency limits make that a non-option.

To limit a PWM pulse to 10 us, I can only go up to 66 of the 100. I'm still under my 2mm upper limit for distance traversed upon rapid eye movement, ~1.46 mm, so I hope flicker won't be an issue. At the same time I'll be happy to test to see if the LEDs will survive at a 15.3 us pulse at 10% DC though.

Discussions