Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and improve pulse width range #24

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=Servo
version=1.1.3
version=3.0.0
author=Michael Margolis, Arduino
maintainer=Arduino <[email protected]>
sentence=Allows Arduino/Genuino boards to control a variety of servo motors.
Expand Down
53 changes: 26 additions & 27 deletions src/Servo.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,40 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

/*
A servo is activated by creating an instance of the Servo class passing
/*
A servo is activated by creating an instance of the Servo class passing
the desired pin to the attach() method.
The servos are pulsed in the background using the value most recently
The servos are pulsed in the background using the value most recently
written using the write() method.

Note that analogWrite of PWM on pins associated with the timer are
Note that analogWrite of PWM on pins associated with the timer are
disabled when the first servo is attached.
Timers are seized as needed in groups of 12 servos - 24 servos use two
Timers are seized as needed in groups of 12 servos - 24 servos use two
timers, 48 servos will use four.
The sequence used to sieze timers is defined in timers.h
The sequence used to seize timers is defined in timers.h

The methods are:

Servo - Class for manipulating servo motors connected to Arduino pins.

attach(pin ) - Attaches a servo motor to an i/o pin.
attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
default min is 544, max is 2400


write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
writeMicroseconds() - Sets the servo pulse width in microseconds
read() - Gets the last written servo pulse width as an angle between 0 and 180.
writeMicroseconds() - Sets the servo pulse width in microseconds
read() - Gets the last written servo pulse width as an angle between 0 and 180.
readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
attached() - Returns true if there is a servo attached.
detach() - Stops an attached servos from pulsing its i/o pin.
attached() - Returns true if there is a servo attached.
detach() - Stops an attached servos from pulsing its i/o pin.
*/

#ifndef Servo_h
#define Servo_h

#include <inttypes.h>

/*
* Defines for 16 bit timers used with Servo library
/*
* Defines for 16 bit timers used with Servo library
*
* If _useTimerX is defined then TimerX is a 16 bit timer on the current board
* timer16_Sequence_t enumerates the sequence that the timers should be allocated
Expand All @@ -75,14 +74,14 @@
#error "This library only supports boards with an AVR, SAM, SAMD, NRF52 or STM32F4 processor."
#endif

#define Servo_VERSION 2 // software version of this library
#define Servo_VERSION 3 // 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 MIN_PULSE_WIDTH 1000 // the shortest pulse (in us) sent to a servo
#define MAX_PULSE_WIDTH 2000 // the longest pulse (in us) 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 REFRESH_INTERVAL 20000 // minimum time to refresh servos in microseconds

#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer
#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
Expand All @@ -91,8 +90,8 @@

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 ;
uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false
} ServoPin_t ;

typedef struct {
ServoPin_t Pin;
Expand All @@ -104,17 +103,17 @@ 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.
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
void write(int value); // if value is < the minimum pulse width it's 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
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
uint16_t min; // minimum pulse width in microseconds
uint16_t max; // maximum pulse width in microseconds
};

#endif
Expand Down
25 changes: 7 additions & 18 deletions src/avr/Servo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ uint8_t ServoCount = 0; // the total number
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel

#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo

/************ static functions common to all instances ***********************/

static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
Expand Down Expand Up @@ -240,9 +237,8 @@ uint8_t Servo::attach(int pin, int min, int max)
if(this->servoIndex < MAX_SERVOS ) {
pinMode( pin, OUTPUT) ; // set servo pin to output
servos[this->servoIndex].Pin.nbr = pin;
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
this->max = (MAX_PULSE_WIDTH - max)/4;
this->min = min;
this->max = max;
// initialize the timer if it has not already been initialized
timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
if(isTimerActive(timer) == false)
Expand All @@ -263,11 +259,9 @@ void Servo::detach()

void Servo::write(int value)
{
if(value < MIN_PULSE_WIDTH)
{ // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
if(value < 0) value = 0;
if(value > 180) value = 180;
value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
if(value < this->min)
{ // treat values less than the minimum pulse width as angles in degrees (valid values in microseconds are handled as microseconds)
value = map(value, 0, 180, this->min, this->max);
}
this->writeMicroseconds(value);
}
Expand All @@ -278,11 +272,7 @@ void Servo::writeMicroseconds(int value)
byte channel = this->servoIndex;
if( (channel < MAX_SERVOS) ) // ensure channel is valid
{
if( value < SERVO_MIN() ) // ensure pulse width is valid
value = SERVO_MIN();
else if( value > SERVO_MAX() )
value = SERVO_MAX();

value = constrain(value, this->min, this->max); // ensure pulse width is valid
value = value - TRIM_DURATION;
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead - 12 Aug 2009

Expand All @@ -295,7 +285,7 @@ void Servo::writeMicroseconds(int value)

int Servo::read() // return the value as degrees
{
return map( this->readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
return map(this->readMicroseconds()+1, this->min, this->max, 0, 180);
}

int Servo::readMicroseconds()
Expand All @@ -315,4 +305,3 @@ bool Servo::attached()
}

#endif // ARDUINO_ARCH_AVR

29 changes: 8 additions & 21 deletions src/megaavr/Servo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ static volatile int8_t currentServoIndex[_Nbr_16timers]; // index for the serv
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel

#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo

void ServoHandler(int timer)
{
if (currentServoIndex[timer] < 0) {
Expand Down Expand Up @@ -131,9 +128,8 @@ uint8_t Servo::attach(int pin, int min, int max)
if (this->servoIndex < MAX_SERVOS) {
pinMode(pin, OUTPUT); // set servo pin to output
servos[this->servoIndex].Pin.nbr = pin;
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
this->max = (MAX_PULSE_WIDTH - max)/4;
this->min = min;
this->max = max;
// initialize the timer if it has not already been initialized
timer = SERVO_INDEX_TO_TIMER(servoIndex);
if (isTimerActive(timer) == false) {
Expand All @@ -157,15 +153,10 @@ void Servo::detach()

void Servo::write(int value)
{
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
if (value < MIN_PULSE_WIDTH)
// treat values less than the minimum pulse width as angles in degrees (valid values in microseconds are handled as microseconds)
if (value < this->min)
{
if (value < 0)
value = 0;
else if (value > 180)
value = 180;

value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
value = map(value, 0, 180, this->min, this->max);
}
writeMicroseconds(value);
}
Expand All @@ -176,11 +167,7 @@ void Servo::writeMicroseconds(int value)
byte channel = this->servoIndex;
if( (channel < MAX_SERVOS) ) // ensure channel is valid
{
if (value < SERVO_MIN()) // ensure pulse width is valid
value = SERVO_MIN();
else if (value > SERVO_MAX())
value = SERVO_MAX();

value = constrain(value, this->min, this->max); // ensure pulse width is valid
value = value - TRIM_DURATION;
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead
servos[channel].ticks = value;
Expand All @@ -189,7 +176,7 @@ void Servo::writeMicroseconds(int value)

int Servo::read() // return the value as degrees
{
return map(readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
return map(readMicroseconds()+1, this->min, this->max, 0, 180);
}

int Servo::readMicroseconds()
Expand All @@ -208,4 +195,4 @@ bool Servo::attached()
return servos[this->servoIndex].Pin.isActive;
}

#endif
#endif
19 changes: 9 additions & 10 deletions src/nrf52/Servo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,14 @@ Servo::Servo()
{
if (ServoCount < MAX_SERVOS) {
this->servoIndex = ServoCount++; // assign a servo index to this instance
} else {
} else {
this->servoIndex = INVALID_SERVO; // too many servos
}

}

uint8_t Servo::attach(int pin)
{

return this->attach(pin, 0, 2500);
}

Expand All @@ -57,11 +56,11 @@ uint8_t Servo::attach(int pin, int min, int max)

if(min < servo_min) min = servo_min;
if (max > servo_max) max = servo_max;
this->min = min;
this->max = max;
this->min = min;
this->max = max;

servos[this->servoIndex].Pin.isActive = true;

}
return this->servoIndex;
}
Expand All @@ -73,13 +72,13 @@ void Servo::detach()


void Servo::write(int value)
{
{
if (value < 0)
value = 0;
else if (value > 180)
value = 180;
value = map(value, 0, 180, MIN_PULSE, MAX_PULSE);

writeMicroseconds(value);
}

Expand Down Expand Up @@ -117,7 +116,7 @@ int Servo::read() // return the value as degrees
}

int Servo::readMicroseconds()
{
{
uint8_t channel, instance;
uint8_t pin=servos[this->servoIndex].Pin.nbr;
instance=(g_APinDescription[pin].ulPWMChannel & 0xF0)/16;
Expand All @@ -131,4 +130,4 @@ bool Servo::attached()
return servos[this->servoIndex].Pin.isActive;
}

#endif // ARDUINO_ARCH_NRF52
#endif // ARDUINO_ARCH_NRF52
28 changes: 7 additions & 21 deletions src/sam/Servo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ static volatile int8_t Channel[_Nbr_16timers ]; // counter for the s
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel

#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo

/************ static functions common to all instances ***********************/

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -202,9 +199,8 @@ uint8_t Servo::attach(int pin, int min, int max)
if (this->servoIndex < MAX_SERVOS) {
pinMode(pin, OUTPUT); // set servo pin to output
servos[this->servoIndex].Pin.nbr = pin;
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
this->max = (MAX_PULSE_WIDTH - max)/4;
this->min = min;
this->max = max;
// initialize the timer if it has not already been initialized
timer = SERVO_INDEX_TO_TIMER(servoIndex);
if (isTimerActive(timer) == false) {
Expand All @@ -228,15 +224,10 @@ void Servo::detach()

void Servo::write(int value)
{
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
if (value < MIN_PULSE_WIDTH)
// treat values less than the minimum pulse width as angles in degrees (valid values in microseconds are handled as microseconds)
if (value < this->min)
{
if (value < 0)
value = 0;
else if (value > 180)
value = 180;

value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
value = map(value, 0, 180, this->min, this->max);
}
writeMicroseconds(value);
}
Expand All @@ -247,11 +238,7 @@ void Servo::writeMicroseconds(int value)
byte channel = this->servoIndex;
if( (channel < MAX_SERVOS) ) // ensure channel is valid
{
if (value < SERVO_MIN()) // ensure pulse width is valid
value = SERVO_MIN();
else if (value > SERVO_MAX())
value = SERVO_MAX();

value = constrain(value, this->min, this->max); // ensure pulse width is valid
value = value - TRIM_DURATION;
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead
servos[channel].ticks = value;
Expand All @@ -260,7 +247,7 @@ void Servo::writeMicroseconds(int value)

int Servo::read() // return the value as degrees
{
return map(readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
return map(readMicroseconds()+1, this->min, this->max, 0, 180);
}

int Servo::readMicroseconds()
Expand All @@ -280,4 +267,3 @@ bool Servo::attached()
}

#endif // ARDUINO_ARCH_SAM

Loading