Skip to content

Instantly share code, notes, and snippets.

@ajfisher
Last active September 12, 2022 17:34
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ajfisher/8c5146bff264b88d04cc to your computer and use it in GitHub Desktop.
Save ajfisher/8c5146bff264b88d04cc to your computer and use it in GitHub Desktop.
Description of Servo PWM as opposed to normal PWM

Servo PWM vs "Normal" PWM

This started from a chat in the [https://gitter.im/rwaldron/johnny-five](Johnny Five Gitter) and I thought I'd put some notes together because this comes up relatively often as people run into the terminology confusion that is caused by the Servo Manufacturers adopting the term PWM and it's usage amongst the Arduino community in relation to analogWrite().

This is my attempt at an explanation so if I've made any mistakes then please PR and we can make this better for everyone.

Servo is generally served by PWM support, right?

Sort of - PWM is typically a reference to an on/off duty cycle time - if I set my duty cycle to 50% then my pulses are on for 50% of the time and off for 50% of the time. If it's 10% then it's on 10% of the time, off 90%.

How PWM - and thus analogWrite() - works

This duty cycle is typically implemented (in AVR world at least) as a counter-comparator incremented by a PWM timer each cycle, which in turn is governed by the clock speed.

In Arduino world (or AVR world generally) different pins are attached to different timers (Timer 0 and Timer 2), and use a prescaler value to get the resolution you're after (this is 64 by default which means 16MHz / 64 / 256 = ~976Hz which is rather close to 1000Hz which is a nice round 1msec PWM counter time - thus the duty cycle operates over about 256Msec or about 1/4 of a second).

The pin turns on, the counter increments each step and compares against the duty cycle value and then turns off when it is greater until the byte overflows and goes back to zero. Thus if you set your analogWrite to 64 the pin will turn on for the first 64 clock cycles, and turn off on the 65th and then remains off for the next 190 before the counter overflows back to 0.

This is Fast PWM, there's another version called phase-correct PWM and it typically operates at a lower timer frequency using more bits for the counter but is more accurate on the timing.

How a Servo PWM works

Servos modulate the width of the ON pulse within a fixed period. The "standard" period (it's not really a standard, more a guideline that is somewhat apopted) is that the fixed period is 20ms. However, this period is somewhat elastic because most servo controllers wait for the ON pulse after being pulled low for a few ms. The width of the actual ON pulse is what governs the position or speed of the servo not the frequency between the pulses.

The neutral position (center or stop) is typically an ON pulse of 1.5ms. Full left is typically an ON pulse of 0.5ms and full right is typically an ON pulse of 2.5ms. As you can see the range from neutral to one extreme or the other is about +- 1ms of pulse ON duration.

So say you have a neutral position 1.5ms ON pulse in 5ms - this would be a 30% duty cycle according to the traditional PWM model, but if you did it over the "standard" 20ms that would be a 7.5% duty cycle. In practice pretty much all servos would adopt their neutral position regardless of whether you sent the pulse every 5ms or every 20ms as it's the width of the pulse once it is detected that governs the servo position.

So, within arduino world at least, the servo class doesn't use analogWrite to control a servo - it uses timers and interrupts to do the pulse at the correct frequency. This is why you can run many more servos off an arduino than you have PWM pins. Behind the scenes it leverages the fact that there is inherent elasticity in the timing requirements so pulses can be easily spread out across many pins and still hit the frequency the servos expect to see.

Conclusion

You can happily run a servo off a non-PWM pin and firmata works like this - simply using the Arduino Servo class. Don't believe me? Plug a servo into pin 7 (non PWM pin) and use the servo class - it will work absolutely fine. Plug it into PWM pins 5 or 6 and you won't notice a difference.

It should also be noted that whilst it's highly unlikely, it's possible to damage a servo using analogWrite, especially at higher duty cycles. Regardless you'll probably end up with lots of twitchiness on your servo which isn't what you want for most applications.

Further reading:

[http://en.wikipedia.org/wiki/Servo_control] [http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM]

@Resseguie
Copy link

Good info @ajfisher. One typo fix (I think, please check my math). Don't know how to do PR against gist (can you?) so here is my fork: https://gist.github.com/Resseguie/b73038df374c87c6955b

Change is just 976HZ ~= 0.001MHz instead of 1000MHz

@ajfisher
Copy link
Author

OOps - good catch... And yes - I'm not sure I know how to do a PR on a gist either. Updating now

@eagl1
Copy link

eagl1 commented Feb 9, 2019

wow thanks! really didn't have a clue how to drive the servos I have. Of course I'm a fan of the servo class library but I just want to just manipulate the programming method to drive it with simple AVR-GCC coding.

So, I guess I have to prepare certain timings with millis() for ON duty and other millis() for OFF duty. The ON timings should be precisely 0.5, 1.5 and 2.5 milli second! Got it 👍

Thank you dude 🥇

@KMurphs
Copy link

KMurphs commented Feb 10, 2022

I have spent already a good 10min trying to find a difference. Thank you for making this available. I won't have to spend another 10min before I decide (as I was planning to) that the servo signal was just a particular case of PWM.

Regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment