Servo Library

This library can control a great number of servos. It makes careful use of timers: the library can control 12 servos using only 1 timer. Derived from the Arduino Servo library v1.8.0.

This library has a slightly diffent user interface than the usual singleton libraries. This allows for handling more than one servo per Sketch but it requires some more attention when porting an existing sketch from C++ to C.

Each Servo instance is identified by a channel-ID. In order to match the C++ class constructor syntax it is defined as a type Servo.

This channel-ID is used as a file-descriptor-like value and need to be passed to all API functions except for Servo_attach(). This function claimes the next free channel number.

Example

Read a potentiometer on analog input 0 and set a servo pulse length between 1000us and 2023us:

#include <Arduino.h>
#include <Servo.h>

Servo myservo;          // just a simple unsigned char to hold the channel-ID

int val;                // variable to read the value from the analog pin

void setup() {
  myservo = Servo_attach(9);    // attaches the servo on pin 9, returns channel-ID
}

void loop() {
  val = analogRead(0);          // reads the value of the potentiometer
  Servo_write(myservo, val+1000);// sets the servo position
  delay(15);
}

Original Arduino C++-Sytax:

#include <Servo.h>

Servo myservo;          // create servo object to control a servo

int val;                // variable to read the value from the analog pin

void setup() {
  myservo.attach(9);    // attaches the servo on pin 9 to the servo object
}

void loop() {
  val = analogRead(0);          // reads the value of the potentiometer
  myservo.write(val+1000);      // sets the servo position
  delay(15);
}

API

data type Servo: A type definition for a simple unsigned char to hold the channel number returned my Servo_attach(). Needed for every servo. Syntax identical to the Arduino class constructor.

Arduino syntax sduino syntax
Servo myservo; Servo myservo;
myservo.attach(pin); myservo = Servo_attach(pin);
myservo.attach(pin,min,max); Servo_attach_minmax(pin,min,max);
myservo.detach(); Servo_detach(byte channel);
myservo.write(val); Servo_write(myservo, val);
myservo.writeMicroseconds(val); Servo_writeMicroseconds(myservo, val);
val = myservo.read(); val = Servo_read(myservo);
val = myservo.readMicroseconds(); val = Servo_readMicroseconds(myservo);
myservo.attached() Servo_attached(myservo);

uint8_t Servo_attach(int pin); attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure.

uint8_t Servo_attach_minmax(int pin, int min, int max); as above but also sets min and max values for writes.

void Servo_detach(byte channel);

void Servo_write(byte channel, int value); if value is < 200 it is treated as an angle and scaled according the minimum and maximum pulsewidth defined using the attach() function earlier, otherwise as pulse width in microseconds, unscaled.

void Servo_writeMicroseconds(byte channel, int value); Write pulse width in microseconds, unscaled.

int Servo_read(byte channel); returns current pulse width as an angle between 0 and 180 degrees.

int Servo_readMicroseconds(byte channel); returns current pulse width in microseconds for this servo (was read_us() in first Arduino release).

bool Servo_attached(byte channel); return true if this servo is attached, otherwise false .

Relationship between PWM/analog output and Servo output

It is not possible to use a timer for PWM and the Servo Library at the same time. Since this library currently uses timer1, the PWM function (analogWrite()) is disabled for the pins connected to timer1 (for the STM8S103 this is pin PC3 and PC4 or digital pin 5 and 6).

Pins connected to timer2 (PA3, PD3, PD4, digital pin 2,12,13) are still usable for PWM output.

Possible improvements

A more sophisticated pseudo-OO API

Define a set of preprocessor macros that more OO-like definitions like this become possible:

Current syntax OO-like syntax Arduino syntax
myservo = Servo_attach(pin); myservo_attach(pin); myservo.attach(pin);
Servo_write(myservo, val); myservo_write(val); myservo_write(val);
val = Servo_read(myservo); val = myservo_read(); val = myservo_read();

Optimizing the handle_interrupts() function

SDCC compiles this function very inefficiently. The code size is around 500 Bytes, and it is executed as part of the CC interrupt routine. Expected CPU load for a full servo group of 12 servos is approx. 2%.

(Calculated for 16MHz CPU clock, 13 interrupts every 20ms = 650 interrupts/sec, approx. 500 clock cycles each)

Using more than one CC channel per timer

it might be possible to use all capture+compare (CC) channels of one timer at the same time, attaching one servo group to each CC-channel. This way it would be possible to serve up to 48 servos using the four CC-channels of timer TIM1. Monitoring the repetion period might become a little complex, as it must be ensured that all servos on all channels have finshed before.