Sep 052010
 
Linear LED Fading

Using PWM to fade a LED in and out is dead-simple. Except that the brightness does not seem to change linearly: It gets bright very fast, and then it nearly does not change anymore.

The fix: linearize it.

Since floating point operations are not that popular on a low power microcontroller as the Arduino, the easiest way to do this is via a lookup table to map 8 bit values for bridgeness into PWM values to drive the LED.

Perl to the rescue!
perl -e 'for(my $i=0;$i<256;$i++){ print int(exp($i/46)),",";}'

Manually change the first one to 0 (0 intensity is supposed to be off).
And now LED fading is easy. The example below shows also how to use TimedAction Library which I personally found very useful if you want to do 2 things independently on the Arduino (in this case: blink one LED and fade another one)

#include 

/* pwm9

Fade pin on PIN9 up and down and blink LED13

The circuit:
* LED attached from digital pin 9 to Vcc.

*/

#define ledPWMPin 9    // LED connected to digital pin 9
#define ledBlinkPin 13 // LED PWM

static unsigned char exp_map[256]={
  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
  3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,5,5,5,
  5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,8,8,8,8,8,8,9,9,
  9,9,10,10,10,10,10,11,11,11,11,12,12,12,13,13,13,13,
  14,14,14,15,15,15,16,16,16,17,17,18,18,18,19,19,20,
  20,20,21,21,22,22,23,23,24,24,25,26,26,27,27,28,29,
  29,30,31,31,32,33,33,34,35,36,36,37,38,39,40,41,42,
  42,43,44,45,46,47,48,50,51,52,53,54,55,57,58,59,60,
  62,63,64,66,67,69,70,72,74,75,77,79,80,82,84,86,88,
  90,91,94,96,98,100,102,104,107,109,111,114,116,119,
  122,124,127,130,133,136,139,142,145,148,151,155,158,
  161,165,169,172,176,180,184,188,192,196,201,205,210,
  214,219,224,229,234,239,244,250,255
};

TimedAction timedBlink=TimedAction(1000,blink);
TimedAction timedPWM=TimedAction(100,fade);

void setup()  {
  pinMode(ledBlinkPin,OUTPUT);
  pinMode(ledPWMPin,OUTPUT);
}
// loop() is always called again when it exists
void loop()  {
  timedBlink.check();
  timedPWM.check();
}
// Blinking on PIN13
void blink() {
  static int ledState=LOW;

  ledState ? ledState=LOW : ledState=HIGH;
  digitalWrite(ledBlinkPin,ledState);
}

// Fading on PIN9
void fade() {
  static int currentPWM=0;
  static int dir=1;

  // 0 is supposed to be dark
  // Since LED connects to 5V, 0 is bright, so invert
  analogWrite(ledPWMPin,255-exp_map[currentPWM]);
  currentPWM+=dir;
  if (currentPWM > 255 || currentPWM < 0) {
  currentPWM-=2*dir;
  dir = -dir;
  }
}