11. Orangutan Servos

This section of the library provides the ability to control up to 16 servos by generating digital pulses directly from your Orangutan without the need for a separate servo controller. These servo control functions are non-blocking; pulses are generated in the background by an interrupt while the rest of your code executes. Required memory is allocated by the servos_start() or servos_start_extended() functions using malloc(), which conserves RAM. The servos_stop() function frees this dynamically allocated memory.

This section of the library uses the AVR’s Timer 1 and several interrupts: TIMER1_CAPT, TIMER1_COMPA (not used on the Orangutan SVP), and TIMER1_COMPB.

For a higher-level overview of how servo control works and how the library works on the different Orangutan models, see Section 3.i of the Pololu AVR C/C++ Library User’s Guide.

Note: The OrangutanServos and OrangutanBuzzer libraries both use Timer 1, so they cannot be used together simultaneously. It is possible to alternate use of OrangutanBuzzer and OrangutanServos routines, however. The servos-and-buzzer example program shows how this can be done and also provides functions for playing notes on the buzzer without using Timer 1 or the OrangutanBuzzer functions. The servo-control-using-delays example shows how to generate servo pulses using delays rather than Timer 1 and the OrangutanServos functions.

Reference

C++ and Arduino methods are shown in red.

C functions are shown in green.

static unsigned char OrangutanServos::start(const unsigned char servo_pins[], unsigned char num_pins)

unsigned char servos_start(const unsigned char servo_pins[], unsigned char num_pins)

Configures the AVR’s Timer 1 module to generate pulses for up to 8 servos. The num_pins parameter should be the length of the servo_pins array. A nonzero return value indicates that the needed memory could not be allocated.

The servo_pins parameter should be the RAM address of an array of AVR I/O pin numbers defined using the IO_* keywords provided by the library (e.g. IO_D1 for a servo output on pin PD1).

On the Orangutan SVP: this function takes an array of AVR pins that you have wired to the demux selection pins. The length of the array should be 0–3. The number of servos you can control is 2num_pins. The servo pulses are generated on pin PD5, which is a hardware PWM output connected to the input of the demux, and servos should be connected to demux (servo port) outputs 0 – 2num_pins on the SVP. See the “Servo Demultiplexer” portion of Section 4 of the Orangutan SVP user’s guide for more information.

On the other Orangutans: this function takes an array of AVR pins that you have connected to the signal pins on your servos. The length of the array should be 0–8. Each pin controls one servo.

This function was previously called servos_init(). To maintain compatibility with older versions of the library, servos_init() still exists, but use of servos_start() is encouraged.

Example

// Orangutan SVP: configure PD0 and PD1 to be demux inputs SA and SB,
//   which generates servo pulses on demux pins 0 - 3
// All else: configure for two servo outputs on pins PD0 and PD1
servos_start((unsigned char[]) {IO_D0, IO_D1}, 2);
// C++: OrangutanServos::start((unsigned char[]) {IO_D0, IO_D1}, 2);

static unsigned char OrangutanServos::start(const unsigned char servo_pins[], unsigned char num_pins, const unsigned char servo_pins_b[], unsigned char num_pins_b)

unsigned char servos_start_extended(const unsigned char servo_pins[], unsigned char num_pins, const unsigned char servo_pins_b[], unsigned char num_pins_b)

Configures the AVR’s Timer 1 module to generate pulses for up to 16 servos. A nonzero return value indicates that the needed memory could not be allocated. The servo_pins and num_pins parameters serve the same purpose here as they do in the function above. The num_pins_b parameter should be the length of the servo_pins_b array. The servo_pins_b parameter should be the RAM address of an array of AVR pin numbers defined using the IO_* keywords provided by the library (e.g. IO_D1 for servo pulses on pin PD1). The pins should be connected to the signal pins on your servos. If you don’t want this second set of servos, use a num_pins_b value of 0 (and a servo_pins_b value of 0) or use the servos_start() function above. Similarly, if you want to only use the second set of servos, you can use a num_pins value of 0 (and a servo_pins value of 0).

This function was previously called servos_init_extended(). To maintain compatibility with older versions of the library, servos_init_extended() still exists, but use of servos_start_extended() is encouraged.

static void OrangutanServos::stop()

void servos_stop()

Stops Timer 1, sets all used servo pins as driving-low outputs, and frees up the memory that was dynamically allocated by the servos_start() or servos_start_extended() function. If you want to use Timer 1 for some other purpose, such as playing sounds using OrangutanBuzzer functions, you should call servos_stop() first. Servos cannot be used after calling servos_stop() without first calling servos_start() or servos_start_extended() again.

static void OrangutanServos::setServoTarget(unsigned char servo_num, unsigned int pos_us)

static unsigned int OrangutanServos::getServoTarget(unsigned char servo_num)

static void OrangutanServos::setServoTargetB(unsigned char servo_num, unsigned int pos_us)

static unsigned int OrangutanServos::getServoTargetB(unsigned char servo_num)

void set_servo_target(unsigned char servo_num, unsigned int pos_us)

unsigned int get_servo_target(unsigned char servo_num)

void set_servo_target_b(unsigned char servo_num, unsigned int pos_us)

unsigned int get_servo_target_b(unsigned char servo_num)

These functions set and get a servo’s target position in units of microseconds. This is the pulse width that will be transmitted eventually, but this pulse width may not be transmitted immediately if there is a speed limit set for the servo, or if the target was changed within the last 20 ms. A position value of 0 turns off the specified servo (this is the default). Otherwise, valid target positions are between 400 and 2450 us. The servo_num parameter should be a servo number between 0 and 7 and should be less than the associated num_servos parameter used in the servos_start() or servos_start_extended() function.

static void OrangutanServos::setServoSpeed(unsigned char servo_num, unsigned int speed)

static unsigned int OrangutanServos::getServoSpeed(unsigned char servo_num)

static void OrangutanServos::setServoSpeedB(unsigned char servo_num, unsigned int speed)

static unsigned int OrangutanServos::getServoSpeedB(unsigned char servo_num)

void set_servo_speed(unsigned char servo_num, unsigned int speed)

unsigned int get_servo_speed(unsigned char servo_num)

void set_servo_speed_b(unsigned char servo_num, unsigned int speed)

unsigned int get_servo_speed_b(unsigned char servo_num)

These functions set and get a servo’s speed limit in units of tenths of a microsecond per 20 ms. A speed value of 0 means there is no speed limit. A non-zero speed value means that each time the library sends a servo pulse, that pulse’s width will be within speed/10 µs of the previous pulse width sent to that servo. For example, with a speed value of 200, the pulse width will change by at most 20 µs each time the library sends a pulse. The library sends a pulse every 20 ms, so it will take 1000 ms (50 pulses) to change from a pulse width of 1000 µs to a pulse width of 2000 µs. The servo_num parameter should be a servo number between 0 and 7 and should be less than the associated num_servos parameter used in the servos_start() or servos_start_extended() function.

static unsigned int getServoPosition(unsigned char servo_num)

static unsigned int getServoPositionB(unsigned char servo_num)

unsigned int get_servo_position(unsigned char servo_num)

unsigned int get_servo_position_b(unsigned char servo_num)

These functions get a servo’s current position in units of microseconds. This is the last pulse width that was transmitted to the servo, but this pulse width might not be equal to the target if there is a speed limit set for the servo, or if the target has changed with the last 20 ms. This method does not rely on feedback from the servo, so if the servo is being restrained or overly torqued or simply cannot physically move as quickly as the pulse widths are changing, it might not return the actual position of the servo. If there is a speed limit set for this servo, you can use this method to determine when the servo pulse signal has reached its desired target width. The servo_num parameter should be a servo number between 0 and 7 and should be less than the associated num_servos parameter used in the servos_start() or servos_start_extended() function.