In this blog post, I will show you how to properly control an LEDs brightness using PWM. It isn't as simple as you think!

PWM works by varying the duty cycle of a square wave, like so:

The average voltage of the output is equal to VCC * k, thus changing the duty cycle will change the brightness of an LED connected to output. Pretty much every microcontroller supports PWM, either through a dedicated peripheral, or you can do it yourself in software!

However, there is a problem with the common approach of PWMing an LED. Take this Arduino code for example:

voidloop(){bytei=0;while(1){analogWrite(2,i);delay(10);i++;}}

This will fade an LED on pin 2 of the arduino, from dark to bright. But you may notice the LEDs don't fade very nicely. They will appear to fade very quickly at first, and then spend a long time at full brightness:

The reason behind this is because the human eye doesn't respond to light linearly, but logarithmically. That sure complicates things!

To fix it, we have to correct the PWM values to make them appear linear to the human eye.

A common misconception is that gamma correction should be used, as it does have a very similar response to the eye. However, gamma correction has nothing to do with how humans perceive light, it is just coincidence that it appears to work. The CIE 1931 lightness formula is what actually describes how we perceive light:

Where Y is the luminance (output) between 0.0 and 1.0, and L* is the lightness (input) between 0 and 100

EDIT: I mixed up Y and L*, and thus the relationship was wrong. the article has been updated to fix this

The formula needs to be rearranged in terms of L*:

Y = (L* / 902.3) if L* ≤ 8
Y = ((L* + 16) / 116)^3 if L* > 8

Of course, this formula would be too slow to implement on a microcontroller due to the power and division, so you should use a look-up table instead. I created a simple python script to generate a C header file that can be simply included into the project:

Depending on the microcontroller being used, you may want to change the type so it stores the values in ROM instead of RAM.

The constants at the top can be changed to suit the microcontroller. For instance, if you wanted to use a 10 bit PWM, you could set INT_SIZE=1024. This would still generate a table with 256 entries, but the output will be 10 bits.

Because this conversion reduces the resolution of the PWM, it may indeed be wise to use a 10 bit PWM. This is exactly what I did in my LED Coffee Table Project.

Overview

A friend asked me if I could turn a damaged samsung LED TV into a coffee table, so I created this project. The TV was damaged in the Christchurch Earthquake a year or so ago, but only the front LCD panel was damaged. The backlight was still intact, so the TV made a really nice coffee table!

Disassembly

The first task was to disassemble the panel, which wasn't too hard to do. The circuitry was separated into the Power Supply and Computer board, and it turns out the Computer board isn't necessary to control the backlight.

Disassembled Panel

The next thing to do was to figure out how to control the backlight. After some experimentation shorting out various wires, I figured out how to turn on the PSU and LEDs.

It turns out the backlight is separated into 4 rows of LEDs, and I realised I could make some really cool patterns! You can see two of the rows active in one of the photos above.

Once I had figured out how to drive it, I started prototyping a circuit.

Electronics

I decided to use a dsPIC30F because I had one on hand, and I didn't want to waste one of my more powerful dsPIC33F chips. The project greatly benefited from the extra power, since it allowed me to have 4x 10 bit PWM channels. (much better than an Arduino!)

Prototype Circuit

Firmware

The firmware supports 4 animation channels, and a brightness channel.

To make sure the animations look as smooth as possible, I used gamma correction to make it appear linear, and 10 bit PWM to give the widest dynamic range possible.

I discovered at 8 bits, the resolution of animations at low brightness was very poor, especially since I had applied gamma correction. After expanding it to 10 bit resolution, the animations were very nice.

I used inline assembly in parts to make use of the dsPIC instruction set. While not necessary, I wanted to learn it.

There are 8 modes that I added:

Fully on

Middle rows on

Outer rows on

Animated waves, from inner to outer rows

Animated waves, from one side to the other

Pulsing animation, varies from half to full brightness, kind of like a heartbeat
7. Slow flash mode

Fast flash mode (aka. Seizure mode!)

The screen is really really bright at full brightness, and it's very weird to look at since it's spread over such a large surface.

I am happy to provide source code on request!

Hardware

After I had gotten the firmware working how I wanted it to, I built a rough frame using spare lumber. It's a little rough around the edges, but it's not bad for a first try!

DIY Wood Frame

Final Product

All that was left was to add some buttons and mount it into its frame:

Open the PicKit 2 device file, usually located at C:\Program Files\Microchip\PicKit 2\PK2DeviceFile.dat

I started by duplicating a similar device, the PIC24F16KA102. Most of the settings appear to be the same for that particular family, so by looking at the differences you can work out what needs to be modified.

This is the total viewable bandwidth around the 458 MHz band. The red line is centered on that band, and the maximum allowable bandwidth around the band is 70kHz (Contrasted with a total viewable bandwidth of approx 2MHz)

You can also see a strong signal on the left, which appears to be some sort of coded signal.
The faint bands on each side are just artefacts.

OOK

OOK Modulation

The first mode I tried is OOK, where the signal is simply turned on and off to code the data.
You can see faint bands on either side of the signal, this is spectral splatter from the sharp signal.

Unlike the next two modes, OOK operates at only one frequency, at 458.58MHz.

FSK

FSK Modulation

The next mode I tried was FSK, where the coded data switches between two frequencies on either side of the center frequency. As with the OOK modulation, there is unwanted spectral splatter on each frequency band, though not as visible here.

Incidentally, the bandwidth of 50kHz is just within the allowable frequency band for 458MHz.

GFSK

GFSK Modulation

GFSK is the module's most efficient mode, as it reduces spectral splatter. As you can see, it spreads the frequencies out using some sort of gaussian function, which reduces the harshness of the
signal.

Footnote

The above screenshots were captured without an antenna on the RFM23 module, with the power level set to only 1.3mW! So obviously it will have very good range once I add a proper antenna.

My next task will be to test the maximum range of this module, and hopefully it will extend far enough for my needs!