Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Copy link

commented Mar 17, 2015

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

This comment has been minimized.

Copy link
Owner Author

commented Mar 17, 2015

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

@eagl1

This comment has been minimized.

Copy link

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 🥇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.