3.i. Orangutan Servo Control Functions

Overview

This section of the library provides commands for generating digital pulses to control servos.

Complete documentation of these functions can be found in Section 11 of the Pololu AVR Library Command Reference.

Note: The OrangutanServos and OrangutanBuzzer libraries both use Timer 1, so they will conflict with each other and any other code that relies on or reconfigures Timer 1.

Servos

A servo motor (also called hobby servo or RC servo) is a device containing a motor that you can command to turn to a specific location. To control a servo, you must connect its three wires properly. The black wire is ground, and should be connected to the ground line of your Orangutan. The red line is power, and should be connected to power supply with a voltage that is within the operating range of your servo, and that is capable of supplying all the current that your servo might draw. The white line is signal, and should be connected to a pin that generates servo pulses, such as an I/O line on the Orangutan. The Orangutan I/O header blocks make it easy to connect your servos, because each column of the block contains ground, power, and an I/O line in the correct order.

To make your servo move, you must output a high pulse to the signal line every 20 ms. The pulse width (also called pulse length or pulse duration) determines which position the servo will move to. Thus, every pulse width (typically measured in microseconds) corresponds to some angle (typically measured in degrees) of the servo’s output shaft. Typical servos have a limited range of motion, and this entire range of motion can be reached with pulse widths between 1 ms and 2 ms.

Take care when choosing pulse widths, because some servos are capable of breaking themselves if they are commanded to move to a position outside their range of motion. To start off, you can send pulse widths of 1.5 ms and then slowly change the pulse width until you discover its upper and lower limits.

Orangutan Servo Control

The OrangutanServos section of the library allows you to generate the control pulses for up to 16 servos.

On every Orangutan except the Orangutan SVP, each servo requires one free I/O pin. The library allows you to choose which I/O pins to use for your servos. On the Baby Orangutan B, there are enough free I/O lines for you to control the full 16 servos. On the Orangutan SV and Orangutan LV-168, there are 8 free I/O lines so you can easily control eight servos, but you can control more servos if you remove the LCD or other unused hardware. The pulses are generated using software PWM.

On the Orangutan SVP, the pulses (for your first 8 servos) are all generated on pin PD5. This pin is a hardware PWM output (OC1A), so the OrangutanServos library generates the servo pulses using hardware PWM, which is more accurate and takes less CPU time than software PWM. Pin PD5 is connected to the input line of an on-board 8-output demultiplexer. If you just need to control one servo, you can leave the demultiplexer input lines disconnected, and plug your servo in to servo port 0. If you want to control more than one servo, then you must choose which free I/O lines to connect to the demultiplexer’s three output-selection lines. If you use one I/O line, you can control two servos. If you use two I/O lines, you can control up to four servos. If you use three I/O lines, then you can control up to eight servos. If you need to control more than 8 servos then you can use software PWM to control up to eight more servos (for a total of 16).

Usage Examples

This library comes with several examples in libpololu-avr\examples.

1. svp-one-servo

This example program demonstrates how to control one servo on the Orangutan SVP using PD5.

#include <pololu/orangutan.h>

/*
 * svp-one-servo: for the Orangutan SVP.
 *
 * This example uses the OrangutanServos functions to control one servo.
 * The servo pulse signal is sent on pin PD5, which is hardwired to the
 * input of the demux.  The servo signal is available on demux output 0.
 * This example uses the OrangutanPushbuttons functions to take input
 * from the user, and the OrangutanLCD functions to display feedback on
 * the LCD.
 *
 * http://www.pololu.com/docs/0J20
 * http://www.pololu.com
 * http://forum.pololu.com
 */

int main()
{
	const unsigned char demuxPins[] = {};
	servos_init(demuxPins, sizeof(demuxPins));

	set_servo_target(0, 1300);	// Make the servo go to a neutral position.

	clear(); // Clear the LCD.

	while(1) // Loop forever.
	{
		// When the user presses the top button, execute a pre-programmed
		// sequence of servo movements.
		if (button_is_pressed(TOP_BUTTON))
		{
			// Set the servo speed to 150.  This means that the pulse width
			// will change by at most 15 microseconds every 20 ms.  So it will
			// take 1.33 seconds to go from a pulse width of 1000 us to 2000 us.
			set_servo_speed(0, 150);

			// Slowly move the servo to position 1800.
			set_servo_target(0, 1800);
			delay_ms(700);

			// Disable the speed limit
			set_servo_speed(0, 0);

			// Make the servo move back to position 1300 as fast as possible.
			set_servo_target(0, 1300);
		}

		if (button_is_pressed(BOTTOM_BUTTON))
		{
			// While the user holds down the bottom button, move the servo
			// slowly towards position 1800.
			set_servo_speed(0, 60);
			set_servo_target(0, 1800);
			wait_for_button_release(BOTTOM_BUTTON);

			// When the user releases the bottom button, print its current
			// position (in microseconds) and then move it back quickly.
			clear();
			print_long(get_servo_position(0));
			print_from_program_space(PSTR(" \xE4s"));
			set_servo_speed(0, 0);
			set_servo_target(0, 1300);
		}
	}
}

// Local Variables: **
// mode: C **
// c-basic-offset: 4 **
// tab-width: 4 **
// indent-tabs-mode: t **
// end: **

2. svp-eight-servo

This example program demonstrates how to control up to eight servos on the Orangutan SVP using the hardware demultiplexer.

#include <pololu/orangutan.h>

/*
 * svp-eight-servo: for the Orangutan SVP.
 *
 * This example uses the OrangutanServos functions to control eight servos.
 * To use this example, you must connect the correct AVR I/O pins to their
 * corresponding servo demultiplexer output-selection pins.
 *   - Connect PB3 to SA.
 *   - Connect PB4 to SB.
 *   - Connect PC0 to SC.
 *
 * http://www.pololu.com/docs/0J20
 * http://www.pololu.com
 * http://forum.pololu.com
 */

int main()
{
	// This array specifies the correspondence between I/O pins and DEMUX
	// output-selection pins.  This demo uses three pins, which allows you
	// to control up to 8 servos.  You can also use two, one, or zero pins
	// to control fewer servos.
	const unsigned char demuxPins[] = {IO_B3, IO_B4, IO_C0}; // 8 servos
	//const unsigned char demuxPins[] = {IO_B3, IO_B4};      // four servos
	//const unsigned char demuxPins[] = {IO_B3};             // two servos
	//const unsigned char demuxPins[] = {};                  // one servo

	servos_init(demuxPins, sizeof(demuxPins));

	// Set the servo speed to 150.  This means that the pulse width
	// will change by at most 15 microseconds every 20 ms.  So it will
	// take 1.33 seconds to go from a pulse width of 1000 us to 2000 us.
	set_servo_speed(0, 150);
	set_servo_speed(1, 150);
	set_servo_speed(2, 150);
	set_servo_speed(3, 150);
	set_servo_speed(4, 150);
	set_servo_speed(5, 150);
	set_servo_speed(6, 150);
	set_servo_speed(7, 150);

	// Make all the servos go to a neutral position.
	set_servo_target(0, 1300);
	set_servo_target(1, 1300);
	set_servo_target(2, 1300);
	set_servo_target(3, 1300);
	set_servo_target(4, 1300);
	set_servo_target(5, 1300);
	set_servo_target(6, 1300);
	set_servo_target(7, 1300);

	while(1)  // Loop forever.
	{
		// When the user presses the top button, execute a pre-programmed
		// sequence of servo movements.
		if (button_is_pressed(TOP_BUTTON))
		{
			set_servo_target(0, 1800); delay_ms(350);
			set_servo_target(1, 1800); delay_ms(350);
			set_servo_target(2, 1800); delay_ms(350);
			set_servo_target(3, 1800); delay_ms(350);
			set_servo_target(4, 1800); delay_ms(350);
			set_servo_target(5, 1800); delay_ms(350);
			set_servo_target(6, 1800); delay_ms(350);
			set_servo_target(7, 1800); delay_ms(1000);
			
			set_servo_target(0, 1300); delay_ms(350);
			set_servo_target(1, 1300); delay_ms(350);
			set_servo_target(2, 1300); delay_ms(350);
			set_servo_target(3, 1300); delay_ms(350);
			set_servo_target(4, 1300); delay_ms(350);
			set_servo_target(5, 1300); delay_ms(350);
			set_servo_target(6, 1300); delay_ms(350);
			set_servo_target(7, 1300); delay_ms(350);
		}
	}
}

3. svp-sixteen-servo

This example program demonstrates how to control up to sixteen servos on the Orangutan SVP using the hardware demultiplexer.

#include <pololu/orangutan.h>

/*
 * svp-sixteen-servo: for the Orangutan SVP.
 *
 * This example uses the OrangutanServos functions to control sixteen servos.
 * To use this example, you must connect the correct AVR I/O pins to their
 * corresponding servo demultiplexer output-selection pins.
 *   - Connect PB3 to SA.
 *   - Connect PB4 to SB.
 *   - Connect PC0 to SC.
 * Servos a0-a7 will be on the servo demux outputs.
 * Servos b0-b7 will be on pins PA0-PA7.
 *
 * http://www.pololu.com/docs/0J20
 * http://www.pololu.com
 * http://forum.pololu.com
 */

int main()
{
	// This array specifies the correspondence between I/O pins and DEMUX
	// output-selection pins.  This demo uses three pins, which allows you
	// to control up to 8 servos from the demux.
	const unsigned char demuxPins[] = {IO_B3, IO_B4, IO_C0}; // B3=SA, B4=SB, C0=B5.

	// This array specifies the correspondence between I/O pins and
	// software-PWMed servos.
	const unsigned char servoPinsB[] = {IO_A0, IO_A1, IO_A2, IO_A3,
	    IO_A4, IO_A5, IO_A6, IO_A7};

	servos_init_extended(demuxPins, sizeof(demuxPins), servoPinsB, sizeof(servoPinsB));

	// Set the servo speed to 150.  This means that the pulse width
	// will change by at most 15 microseconds every 20 ms.  So it will
	// take 1.33 seconds to go from a pulse width of 1000 us to 2000 us.
	set_servo_speed(0, 150);   // Servo a0 = Demux output 0
	set_servo_speed(1, 150);   // Servo a1 = Demux output 1
	set_servo_speed(2, 150);   // Servo a2 = Demux output 2
	set_servo_speed(3, 150);   // Servo a3 = Demux output 3
	set_servo_speed(4, 150);   // Servo a4 = Demux output 4
	set_servo_speed(5, 150);   // Servo a5 = Demux output 5
	set_servo_speed(6, 150);   // Servo a6 = Demux output 6
	set_servo_speed(7, 150);   // Servo a7 = Demux output 7
	set_servo_speedB(0, 150);  // Servo b0 = pin A0
	set_servo_speedB(1, 150);  // Servo b1 = pin A1
	set_servo_speedB(2, 150);  // Servo b2 = pin A2
	set_servo_speedB(3, 150);  // Servo b3 = pin A3
	set_servo_speedB(4, 150);  // Servo b4 = pin A4
	set_servo_speedB(5, 150);  // Servo b5 = pin A5
	set_servo_speedB(6, 150);  // Servo b6 = pin A6
	set_servo_speedB(7, 150);  // Servo b7 = pin A7

	// Make all the servos go to a neutral position.
	set_servo_target(0, 1300);
	set_servo_target(1, 1300);
	set_servo_target(2, 1300);
	set_servo_target(3, 1300);
	set_servo_target(4, 1300);
	set_servo_target(5, 1300);
	set_servo_target(6, 1300);
	set_servo_target(7, 1300);
	set_servo_targetB(0, 1300);
	set_servo_targetB(1, 1300);
	set_servo_targetB(2, 1300);
	set_servo_targetB(3, 1300);
	set_servo_targetB(4, 1300);
	set_servo_targetB(5, 1300);
	set_servo_targetB(6, 1300);
	set_servo_targetB(7, 1300);

	while(1)  // Loop forever.
	{
		// When the user presses the top button, execute a pre-programmed
		// sequence of servo movements.
		if (button_is_pressed(TOP_BUTTON))
		{
			set_servo_target(0, 1800); delay_ms(350);
			set_servo_target(1, 1800); delay_ms(350);
			set_servo_target(2, 1800); delay_ms(350);
			set_servo_target(3, 1800); delay_ms(350);
			set_servo_target(4, 1800); delay_ms(350);
			set_servo_target(5, 1800); delay_ms(350);
			set_servo_target(6, 1800); delay_ms(350);
			set_servo_target(7, 1800); delay_ms(350);
			set_servo_targetB(0, 1800); delay_ms(350);
			set_servo_targetB(1, 1800); delay_ms(350);
			set_servo_targetB(2, 1800); delay_ms(350);
			set_servo_targetB(3, 1800); delay_ms(350);
			set_servo_targetB(4, 1800); delay_ms(350);
			set_servo_targetB(5, 1800); delay_ms(350);
			set_servo_targetB(6, 1800); delay_ms(350);
			set_servo_targetB(7, 1800); delay_ms(1000);
			
			set_servo_target(0, 1300); delay_ms(350);
			set_servo_target(1, 1300); delay_ms(350);
			set_servo_target(2, 1300); delay_ms(350);
			set_servo_target(3, 1300); delay_ms(350);
			set_servo_target(4, 1300); delay_ms(350);
			set_servo_target(5, 1300); delay_ms(350);
			set_servo_target(6, 1300); delay_ms(350);
			set_servo_target(7, 1300); delay_ms(350);
			set_servo_targetB(0, 1300); delay_ms(350);
			set_servo_targetB(1, 1300); delay_ms(350);
			set_servo_targetB(2, 1300); delay_ms(350);
			set_servo_targetB(3, 1300); delay_ms(350);
			set_servo_targetB(4, 1300); delay_ms(350);
			set_servo_targetB(5, 1300); delay_ms(350);
			set_servo_targetB(6, 1300); delay_ms(350);
			set_servo_targetB(7, 1300); delay_ms(350);
		}
	}
}

// Local Variables: **
// mode: C **
// c-basic-offset: 4 **
// tab-width: 4 **
// indent-tabs-mode: t **
// end: **