# 8.a. Controlling a servo with an Arduino Uno

The example Arduino Uno code below shows how to control a single servo using Timer 2. Because it uses Timer 2 instead of Timer 1, this code does not interfere with the ZumoMotors library, but it will interfere with the ZumoBuzzer library, so you will not be able to use this and the buzzer at the same time. You can integrate this code with other code that drives the motors.

/** Arduino Uno Timer 2 Servo Example
This example code for the Arduino Uno shows how to use Timer 2 on
the ATmega328P to control a single servo.  This can be useful for
people who cannot use the Arduino IDE's Servo library.  For
example, the ZumoMotors library uses the same timer as the Servo
library (Timer 1), so the two libraries conflict.

The SERVO_PIN macro below specifies what pin to output the
servo on.  This pin needs to be connected to the signal input
line of the servo.  The Arduino's GND needs to be connected to
the ground pin of the servo.  The servo's ground and power pins
need to be connected to an appropriate power supply.
*/

// This line specifies what pin we will use for sending the
// signal to the servo.  You can change this.
#define SERVO_PIN 11

// This is the time since the last rising edge in units of 0.5us.
uint16_t volatile servoTime = 0;

// This is the pulse width we want in units of 0.5us.
uint16_t volatile servoHighTime = 3000;

// This is true if the servo pin is currently high.
boolean volatile servoHigh = false;

void setup()
{
servoInit();
}

void loop()
{
servoSetPosition(1000);  // Send 1000us pulses.
delay(1000);
servoSetPosition(2000);  // Send 2000us pulses.
delay(1000);
}

// This ISR runs after Timer 2 reaches OCR2A and resets.
// In this ISR, we set OCR2A in order to schedule when the next
// interrupt will happen.
// Generally we will set OCR2A to 255 so that we have an
// interrupt every 128 us, but the first two interrupt intervals
// after the rising edge will be smaller so we can achieve
// the desired pulse width.
ISR(TIMER2_COMPA_vect)
{
// The time that passed since the last interrupt is OCR2A + 1
// because the timer value will equal OCR2A before going to 0.
servoTime += OCR2A + 1;

static uint16_t highTimeCopy = 3000;
static uint8_t interruptCount = 0;

if(servoHigh)
{
if(++interruptCount == 2)
{
OCR2A = 255;
}

// The servo pin is currently high.
// Check to see if is time for a falling edge.
// Note: We could == instead of >=.
if(servoTime >= highTimeCopy)
{
// The pin has been high enough, so do a falling edge.
digitalWrite(SERVO_PIN, LOW);
servoHigh = false;
interruptCount = 0;
}
}
else
{
// The servo pin is currently low.

if(servoTime >= 40000)
{
// We've hit the end of the period (20 ms),
// so do a rising edge.
highTimeCopy = servoHighTime;
digitalWrite(SERVO_PIN, HIGH);
servoHigh = true;
servoTime = 0;
interruptCount = 0;
OCR2A = ((highTimeCopy % 256) + 256)/2 - 1;
}
}
}

void servoInit()
{
digitalWrite(SERVO_PIN, LOW);
pinMode(SERVO_PIN, OUTPUT);

// Turn on CTC mode.  Timer 2 will count up to OCR2A, then
// reset to 0 and cause an interrupt.
TCCR2A = (1 << WGM21);
// Set a 1:8 prescaler.  This gives us 0.5us resolution.
TCCR2B = (1 << CS21);

// Put the timer in a good default state.
TCNT2 = 0;
OCR2A = 255;

TIMSK2 |= (1 << OCIE2A);  // Enable timer compare interrupt.
sei();   // Enable interrupts.
}

void servoSetPosition(uint16_t highTimeMicroseconds)
{
TIMSK2 &= ~(1 << OCIE2A); // disable timer compare interrupt
servoHighTime = highTimeMicroseconds * 2;
TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
}

## Related Products

(702) 262-6648
Same-day shipping, worldwide