Roll Your Own Arduino PWM

Most projects are built on abstractions. After all, few of us can create our own wire, our own transistors, or our own integrated circuits. A few months ago, [Julian Ilett] found a problem using the Arduino library for PWM. Recently, he revisited the issue and used his own PWM code to fix the problem. You can watch the video below.

Of course, neither the Arduino library nor [Julian’s] code is actually producing PWM. The Atmel CPU’s hardware is doing the work. The Arduino library gives you a wrapper called analogWrite — especially handy if you are not using an Atmel CPU where the same abstraction will do the same work. The issue arose when [Julian] broke the abstraction to invert the PWM output.

The video does a good job of framing the issue. Setting the PWM hardware to zero still causes a one tick output to occur. That is, the actual count is the count you supply plus one. That’s great on the high end where 255 is treated as 256 out of 256. But at the low end, a zero counterintuitively gives you 1/256. The Arduino library authors elected to detect that edge case and just force the output pin to go low in that case. When inverted, however, the pin still goes low when it ought to go high. You can see the source code responsible, below.

pinMode(pin, OUTPUT);
if (val == 0)
  {
  digitalWrite(pin, LOW);
  }
else if (val == 255)
  {
  digitalWrite(pin, HIGH);
  }
else
{ ...

Oddly, the 255 case appears to be superfluous in the normal case but is also backward if you invert the output. In all fairness, the Arduino library doesn’t provide you a way to invert the output, so you’ve already broken the abstraction and that’s why this isn’t technically a bug in the library.

[Julian’s] code is quite simple. There’s initial set up of the TCCR1A and TCCR1B registers along with ICR1. The DDRB register sets the pin as an output. After that, writing to OCR1A and OCR1B set the PWM value. The video explains it all in great detail.

We’ve looked at PWM on FPGAs at least once, and that post gives some background on PWM in any application. We also have our own video from way back in 2011 about PWM.

18 thoughts on “Roll Your Own Arduino PWM

        1. You set the frequency by another register which sets the frequency division between the system clock and the timer/counter used to generate the PWM signal.

          Then there’s other stuff you can do, like what is the highest number the counter counts to before it resets, or whether it counts up AND down to create a symmetric waveform where the phase difference between two PWM channels remains a constant etc.- all of which isn’t available with the arduino analog write function.

      1. The article seems to go against itself… first it says “nor [Julian’s] code is actually producing PWM.” then it definitely talks about Timer peripheral registers (hardware PWM)… I’m confused to say the least

    1. or simply add (using I2C) a PCA9685, that gives you 16 PWM channels with a resolution of 12bits each.

      12bit PWM resolution allows you to implement the PWM controller as a 16 channel servo controller, just set the duty cycle and repetition values to the correct ranges and you’re ready to go. Used it many times to create multiple jitter free servo signals on a microcontroller with a low pincount.

  1. The hard way of just wrapping it in

    invertedAnalogWrite(byte pin, byte val){
      if(val == 255){
        analogWrite(pin, 0);
      }
      else if(val == 0){
        analogWrite(pin, 255);
      }
      else{
        analogWrite(pin, val);
      }
    }

    And that’s what I told him back then…

    1. Wouldn’t you want 256-val in the last analogWrite? Or are you assuming he still inverts it outside? If you made that one change it would just invert and it ought to be portable to anything that has a 0-255 PWM range built into analogWrite. In fact, if you did that, you don’t need the first if either. Just something like:

      if (val) analogWrite(pin, 256-val) else analogWrite(pin,255);

    2. Perhaps the point isn’t simply inverting the values, but to invert the waveform so it would be out of phase with another channel.

      Consider for example, if you place a small DC motor between two output pins and drive them at 50% duty cycle in opposing phase. What happens? Now, what happens if the duty cycle is changed to 20% or 80%?

      It’s a hardware reversible speed controller.

  2. The reason for the 0/255 check is simply that using Fast PWM you can not go to zero, when you are say using an LED under PWM, having it not go to zero is very noticeable, it’s effectively a runt pulse as I recall (not a full 1/256) as the counter rolls to zero. They didn’t need to check 255, but whatever, there is lots of stuff in the Arduino core which isn’t necessary.

    But, the Arduino developers (Wiring maybe, this is old stuff) could easily have solved this in a much more efficient way by simply using the inverted Fast PWM mode instead of non-inverted. That way instead of “not quite being 100% off” you get “not quite being 100% on”, this is waay less of a problem in almost all situations, it’s very difficult to notice when something is not quite 100% on, but it’s very easy to notice when something is not quite 100% off.

    Of course, it requires no intervention on the part of the coder, if analogWrite just inverts the value given it so that 255 becomes 0 when running in inverted mode. I did exactly this in a fork of ATTInyCore for the Tiny 4/5/9/10 where every byte I can save is important.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.