How does arduino control a servo motor?(Technical qn)

Hi guys,

Using the arduino to control a servo motor, I would like to know and understand how an arduino controls a servo motor.

From what I have researched online, I understand that the servo motor is controlled by the PWM in the arduino. The potentiometer inside the servo motor translates the PWM into angle of the servo motor to determine its position. From my understanding, If the pulse is 1ms (high), the angle is 0 degrees and if it is 2ms, the angle is 180 degrees. ANd the total period is 20ms.

But I still not able to convince myself with these information as to how the arduino controls the servo motor.

How is the PWM determined in the arduino? i.e how is the pulses varied in the arduino from 1ms to 2ms?
How does the potentiometer translate this pulses into angles?
And I am using the example code "sweep". So from 0 to 180 degrees, in 1 degree increment, how does the pulses increase?

Hope someone could provide a better explanation as to how the arduino controls the servo motor.

The sweep code is below:

void servo() 
{ 
 
  for (int i= 0; i < 1; i ++)
  {
    for(servoposition = 0; servoposition < 180; servoposition += 1)        // goes from 0 degrees to 180 degrees 
    {                                                                      // in steps of 1 degree 
      myservo.write(servoposition);                                        // tell servo to go to position in variable 'pos' 
      delay(15);                                                           // waits 15ms for the servo to reach the position 
    } 
    for(servoposition = 180; servoposition>=1; servoposition-=1)           // goes from 180 degrees to 0 degrees 
    {                                
      myservo.write(servoposition);                                        // tell servo to go to position in variable 'pos' 
      delay(15);                                                           // waits 15ms for the servo to reach the position 
    } 
  }
}

The potentiometer inside the servo motor translates the PWM into angle of the servo motor to determine its position.

Not quite. The pot in the servo provides an analog voltage back to the chip in the servo that is a function of the pot position.The below shows what is inside a typical hobby servo.

http://www.seattlerobotics.org/encoder/200009/S3003C.html

The Servo library uses timer interrupts to sequence all the output pulses - if you really want
the nitty-gritty details go and look at the source code to the library - remember you have all
the code, Arduino is open source.

For each timer the library can control upto 12 servo outputs - on the Uno et al it uses timer1
only so 12 servos are supported, many more on the Mega.

Using timer1 means that analogWrite on pins 9 and 10 is not compatible with using the servo
library, note.

But I still not able to convince myself with these information as to how the arduino controls the servo motor.

Technically an arduino using the servo library commands does not control a servo directly, but rather just sends it the desired 'setpoint' of the position desired via a continuous pwm pulse. The servo has an internal controller that continuously compares the desired PWM 'setpoint' position command with the actual position via it's internal pot coupled to the gear train and will drive the motor until setpoint = actual position where it will then stop moving.
That make sense?

Lefty

MarkT:
Using timer1 means that analogWrite on pins 9 and 10 is not compatible with using the servo
library, note.

What does this mean? My servo motor is connected to Pin 9..

retrolefty:

But I still not able to convince myself with these information as to how the arduino controls the servo motor.

Technically an arduino using the servo library commands does not control a servo directly, but rather just sends it the desired 'setpoint' of the position desired via a continuous pwm pulse. The servo has an internal controller that continuously compares the desired PWM 'setpoint' position command with the actual position via it's internal pot coupled to the gear train and will drive the motor until setpoint = actual position where it will then stop moving.
That make sense?

Lefty

Hi,

what is meant by a "continuous pwm pulse"?
DOes it mean it will send 1ms pulse and gradually increase to 2ms? If so, how is the increment decided?
Thanks.

kurtselva:

retrolefty:

But I still not able to convince myself with these information as to how the arduino controls the servo motor.

Technically an arduino using the servo library commands does not control a servo directly, but rather just sends it the desired 'setpoint' of the position desired via a continuous pwm pulse. The servo has an internal controller that continuously compares the desired PWM 'setpoint' position command with the actual position via it's internal pot coupled to the gear train and will drive the motor until setpoint = actual position where it will then stop moving.
That make sense?

Lefty

Hi,

what is meant by a "continuous pwm pulse"?
DOes it mean it will send 1ms pulse and gradually increase to 2ms? If so, how is the increment decided?
Thanks.

No, it means the servo library will continue to send the same value pulse width that you last commanded via a servo.write command. It's repeated every 20 milliseconds forever, or until you write a new servo.write() command and then a new pulse width will be sent continuously (every 20 msec) to the servo.

Lefty

kurtselva:
Hi,

what is meant by a "continuous pwm pulse"?
DOes it mean it will send 1ms pulse and gradually increase to 2ms? If so, how is the increment decided?
Thanks.

If you hooked an oscilloscope to the pin you are controlling, you will see that 50 times per second that a pulse is sent to the servo. This pulse will be close to 1.5mS in duration after you issue a myservo.write(90) function call.

Internally, the servo also has a pulse generator whose width is controlled by an internal potentiometer connected to the output shaft. A comparator continuously compares the two pulse trains and will rotate the servo until they match internally (until there is no more "error").

Various calls to myservo.write() will set the pulse width between .5 and 2.5mS depending upon the "angle" you requested. The servo will rotate in the appropriate direction until the error is cancelled.

The servo library hijacks Timer1 to do its work so that means that the arduino PWM library can't perform an analogWrite() function on pins 9 or 10 to use the default PWM ability of the arduino that refreshes about 490 times per second. You can still use the servo library to control those pins though.

The way it works is that it keeps a list of up to 12 servos and there associated pins that you have told it about. Using just the one timer and by making efficient use of interrupts, it can control all 12 servos with high precision.

Anything else anybody wants to know about how it works, I've picked apart last night and have a pretty good understanding of how it works. I can also tell you guys that Timer0 can steal up to 6uS of time running its ISR. This can result in that much jitter in the servo pulses. Also writing the servo position can cause another 1uS of jitter.

Thanks for the info guys.

I have the servo library code here:

#ifndef Servo_h
#define Servo_h

#include <inttypes.h>

/*
 * Defines for 16 bit timers used with  Servo library
 *
 * If _useTimerX is defined then TimerX is a 16 bit timer on the curent board
 * timer16_Sequence_t enumerates the sequence that the timers should be allocated
 * _Nbr_16timers indicates how many 16 bit timers are available.
 *
 */

// Say which 16 bit timers can be used and in what order
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define _useTimer5
#define _useTimer1
#define _useTimer3
#define _useTimer4
typedef enum { _timer5, _timer1, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t ;

#elif defined(__AVR_ATmega32U4__)  
#define _useTimer3
#define _useTimer1
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;

#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
#define _useTimer3
#define _useTimer1
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;

#elif defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
#define _useTimer3
#define _useTimer1
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;

#else  // everything else
#define _useTimer1
typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t ;                  
#endif

#define Servo_VERSION           2      // software version of this library

#define MIN_PULSE_WIDTH       544     // the shortest pulse sent to a servo  
#define MAX_PULSE_WIDTH      2400     // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH  1500     // default pulse width when servo is attached
#define REFRESH_INTERVAL    20000     // minumim time to refresh servos in microseconds

#define SERVOS_PER_TIMER       12     // the maximum number of servos controlled by one timer
#define MAX_SERVOS   (_Nbr_16timers  * SERVOS_PER_TIMER)

#define INVALID_SERVO         255     // flag indicating an invalid servo index

typedef struct  {
  uint8_t nbr        :6 ;             // a pin number from 0 to 63
  uint8_t isActive   :1 ;             // true if this channel is enabled, pin not pulsed if false
} ServoPin_t   ;  

typedef struct {
  ServoPin_t Pin;
  unsigned int ticks;
} servo_t;

class Servo
{
public:
  Servo();
  uint8_t attach(int pin);           // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
  uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes.
  void detach();
  void write(int value);             // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds
  void writeMicroseconds(int value); // Write pulse width in microseconds
  int read();                        // returns current pulse width as an angle between 0 and 180 degrees
  int readMicroseconds();            // returns current pulse width in microseconds for this servo (was read_us() in first release)
  bool attached();                   // return true if this servo is attached, otherwise false
private:
   uint8_t servoIndex;               // index into the channel data for this servo
   int8_t min;                       // minimum is this value times 4 added to MIN_PULSE_WIDTH    
   int8_t max;                       // maximum is this value times 4 added to MAX_PULSE_WIDTH  
};

#endif

A few qns:

There is a "Min Pulse Width", Max Pulse width" and "Default pulse width"
What are the purpose of these?

And what does the remaining codes do?
Eg those "typedef struct", "and class servo"...