Wednesday, September 23, 2015

Arduino 12-bit PWM for Fading Smoothly

Working off GGE5's 12-bit PWM enabling code found here on the Arduino forums, I've been working to "multi-task" an ATmega328 so I can fade LED(s) smoothly while doing other stuff or interrupt a fade to change its direction. 4096 steps go a long way toward nice smooth fades, especially at the lower end of the range where the human can be a little more sensitive.

One thing I noticed is that if I used the analogWrite() command in conjunction with 12-bit PWM, I got an odd little blip or flash somewhere at the beginning of a fade up (fading from 0 to 4095).

However, if I set the PWM value by setting the output compare register OCR1A equal to the desired value (again fading from 0 to 4095) then the fade is uniform.

Well, analogWrite() is a convenient way to control the duty cycle of PWM capable pin. The convenience comes at the price of hiding some code behind the scenes. A look at the function within wiring_analog.c which can be found in hardware/arduino/cores/arduino/wiring_analog.c in your Arduino install, shows that in addition to setting the pin's output compare register, analogWrite() has a snippet of code as follows ...
else if (val == 255)
 {
  digitalWrite(pin, HIGH);
 }
Note - 'val' is the value you pass the analogWrite(), i.e. analogWrite(LED_Pin, val).

So my eyes weren't playing tricks on me. When my code was writing the PWM value 255 using analogWrite(), I was actually getting a digitalWrite(LED_Pin, HIGH) instead for a brief time.

So the lesson learned is when using a 12-bit PWM enabled pin, stick to writing directly to the register and avoid using the analogWrite() function. Or just skip the value 255 ;)