Skip to content

Commit af7c83a

Browse files
committed
Moving libraries out of arduino platform / core directory and to top-level.
0 parents  commit af7c83a

File tree

5 files changed

+435
-0
lines changed

5 files changed

+435
-0
lines changed

Servo.cpp

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
/*
2+
Servo.cpp - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
3+
Copyright (c) 2009 Michael Margolis. All right reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
/*
21+
22+
A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
23+
The servos are pulsed in the background using the value most recently written using the write() method
24+
25+
Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached.
26+
Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four.
27+
28+
The methods are:
29+
30+
Servo - Class for manipulating servo motors connected to Arduino pins.
31+
32+
attach(pin ) - Attaches a servo motor to an i/o pin.
33+
attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
34+
default min is 544, max is 2400
35+
36+
write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
37+
writeMicroseconds() - Sets the servo pulse width in microseconds
38+
read() - Gets the last written servo pulse width as an angle between 0 and 180.
39+
readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
40+
attached() - Returns true if there is a servo attached.
41+
detach() - Stops an attached servos from pulsing its i/o pin.
42+
43+
*/
44+
45+
#include <avr/interrupt.h>
46+
#include <WProgram.h>
47+
48+
49+
#include "Servo.h"
50+
51+
#define TICKS_PER_uS (clockCyclesPerMicrosecond() / 8) // number of timer ticks per microsecond with prescale of 8
52+
53+
#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer
54+
#define TRIM_DURATION (SERVOS_PER_TIMER/2) // compensation ticks to trim adjust for digitalWrite delays
55+
56+
#define NBR_TIMERS (MAX_SERVOS / SERVOS_PER_TIMER)
57+
58+
static servo_t servos[MAX_SERVOS]; // static array of servo structures
59+
static volatile int8_t Channel[NBR_TIMERS]; // counter for the servo being pulsed for each timer (or -1 if refresh interval)
60+
#if defined(__AVR_ATmega1280__)
61+
typedef enum { _timer5, _timer1, _timer3, _timer4 } servoTimer_t; // this is the sequence for timer utilization on mega
62+
#else
63+
typedef enum { _timer1 } servoTimer_t; // this is the sequence for timer utilization on other controllers
64+
#endif
65+
66+
uint8_t ServoCount = 0; // the total number of attached servos
67+
68+
// convenience macros
69+
#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((servoTimer_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
70+
#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
71+
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
72+
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel
73+
74+
#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
75+
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo
76+
77+
/************ static functions common to all instances ***********************/
78+
79+
static inline void handle_interrupts(servoTimer_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
80+
{
81+
if( Channel[timer] < 0 )
82+
*TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
83+
else{
84+
if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true )
85+
digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated
86+
}
87+
88+
Channel[timer]++; // increment to the next channel
89+
if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
90+
*OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks;
91+
if(SERVO(timer,Channel[timer]).Pin.isActive == true) // check if activated
92+
digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high
93+
}
94+
else {
95+
// finished all channels so wait for the refresh period to expire before starting over
96+
if( (unsigned)*TCNTn < (((unsigned int)REFRESH_INTERVAL * TICKS_PER_uS) + 4) ) // allow a few ticks to ensure the next OCR1A not missed
97+
*OCRnA = (unsigned int)REFRESH_INTERVAL * TICKS_PER_uS;
98+
else
99+
*OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed
100+
Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
101+
}
102+
}
103+
104+
SIGNAL (TIMER1_COMPA_vect)
105+
{
106+
handle_interrupts(_timer1, &TCNT1, &OCR1A);
107+
}
108+
109+
#if defined(__AVR_ATmega1280__)
110+
SIGNAL (TIMER3_COMPA_vect)
111+
{
112+
handle_interrupts(_timer3, &TCNT3, &OCR3A);
113+
}
114+
SIGNAL (TIMER4_COMPA_vect)
115+
{
116+
handle_interrupts(_timer4, &TCNT4, &OCR4A);
117+
}
118+
SIGNAL (TIMER5_COMPA_vect)
119+
{
120+
handle_interrupts(_timer5, &TCNT5, &OCR5A);
121+
}
122+
#endif
123+
124+
static void initISR(servoTimer_t timer)
125+
{
126+
if(timer == _timer1) {
127+
TCCR1A = 0; // normal counting mode
128+
TCCR1B = _BV(CS11); // set prescaler of 8
129+
TCNT1 = 0; // clear the timer count
130+
#if defined(__AVR_ATmega8__)
131+
TIFR |= _BV(OCF1A); // clear any pending interrupts;
132+
TIMSK |= _BV(OCIE1A) ; // enable the output compare interrupt
133+
#else
134+
TIFR1 |= _BV(OCF1A); // clear any pending interrupts;
135+
TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt
136+
#endif
137+
}
138+
#if defined(__AVR_ATmega1280__)
139+
else if(timer == _timer3) {
140+
TCCR3A = 0; // normal counting mode
141+
TCCR3B = _BV(CS31); // set prescaler of 8
142+
TCNT3 = 0; // clear the timer count
143+
TIFR3 = _BV(OCF3A); // clear any pending interrupts;
144+
TIMSK3 = _BV(OCIE3A) ; // enable the output compare interrupt
145+
}
146+
else if(timer == _timer4) {
147+
TCCR4A = 0; // normal counting mode
148+
TCCR4B = _BV(CS41); // set prescaler of 8
149+
TCNT4 = 0; // clear the timer count
150+
TIFR4 = _BV(OCF4A); // clear any pending interrupts;
151+
TIMSK4 = _BV(OCIE4A) ; // enable the output compare interrupt
152+
}
153+
else if(timer == _timer5) {
154+
TCCR5A = 0; // normal counting mode
155+
TCCR5B = _BV(CS51); // set prescaler of 8
156+
TCNT5 = 0; // clear the timer count
157+
TIFR5 = _BV(OCF5A); // clear any pending interrupts;
158+
TIMSK5 = _BV(OCIE5A) ; // enable the output compare interrupt
159+
}
160+
#endif
161+
}
162+
163+
static boolean isTimerActive(servoTimer_t timer)
164+
{
165+
// returns true if any servo is active on this timer
166+
for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {
167+
if(SERVO(timer,channel).Pin.isActive == true)
168+
return true;
169+
}
170+
return false;
171+
}
172+
173+
174+
/****************** end of static functions ******************************/
175+
176+
Servo::Servo()
177+
{
178+
if( ServoCount < MAX_SERVOS) {
179+
this->servoIndex = ServoCount++; // assign a servo index to this instance
180+
servos[this->servoIndex].ticks = DEFAULT_PULSE_WIDTH * TICKS_PER_uS; // store default values
181+
}
182+
else
183+
this->servoIndex = INVALID_SERVO ; // too many servos
184+
}
185+
186+
uint8_t Servo::attach(int pin)
187+
{
188+
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
189+
}
190+
191+
uint8_t Servo::attach(int pin, int min, int max)
192+
{
193+
if(this->servoIndex < MAX_SERVOS ) {
194+
pinMode( pin, OUTPUT) ; // set servo pin to output
195+
servos[this->servoIndex].Pin.nbr = pin;
196+
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
197+
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
198+
this->max = (MAX_PULSE_WIDTH - max)/4;
199+
// initialize the timer if it has not already been initialized
200+
servoTimer_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
201+
if(isTimerActive(timer) == false)
202+
initISR(timer);
203+
servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive
204+
}
205+
return this->servoIndex ;
206+
}
207+
208+
void Servo::detach()
209+
{
210+
servos[this->servoIndex].Pin.isActive = false;
211+
212+
#ifdef FREE_TIMERS
213+
if(isTimerActive(SERVO_INDEX_TO_TIMER(servoIndex)) == false) {
214+
;// call to unimplimented function in wiring.c to re-init timer (set timer back to PWM mode) TODO?
215+
}
216+
#endif
217+
}
218+
219+
void Servo::write(int value)
220+
{
221+
if(value < MIN_PULSE_WIDTH)
222+
{ // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
223+
if(value < 0) value = 0;
224+
if(value > 180) value = 180;
225+
value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
226+
}
227+
this->writeMicroseconds(value);
228+
}
229+
230+
void Servo::writeMicroseconds(int value)
231+
{
232+
// calculate and store the values for the given channel
233+
byte channel = this->servoIndex;
234+
if( (channel >= 0) && (channel < MAX_SERVOS) ) // ensure channel is valid
235+
{
236+
if( value < SERVO_MIN() ) // ensure pulse width is valid
237+
value = SERVO_MIN();
238+
else if( value > SERVO_MAX() )
239+
value = SERVO_MAX();
240+
241+
value = (value-TRIM_DURATION) * TICKS_PER_uS; // convert to ticks after compensating for interrupt overhead
242+
uint8_t oldSREG = SREG;
243+
cli();
244+
servos[channel].ticks = value;
245+
SREG = oldSREG;
246+
}
247+
}
248+
249+
int Servo::read() // return the value as degrees
250+
{
251+
return map( this->readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
252+
}
253+
254+
int Servo::readMicroseconds()
255+
{
256+
unsigned int pulsewidth;
257+
if( this->servoIndex != INVALID_SERVO )
258+
pulsewidth = (servos[this->servoIndex].ticks / TICKS_PER_uS) + TRIM_DURATION ;
259+
else
260+
pulsewidth = 0;
261+
262+
return pulsewidth;
263+
}
264+
265+
bool Servo::attached()
266+
{
267+
return servos[this->servoIndex].Pin.isActive ;
268+
}

Servo.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
3+
Copyright (c) 2009 Michael Margolis. All right reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
/*
21+
22+
A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
23+
The servos are pulsed in the background using the value most recently written using the write() method
24+
25+
Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached.
26+
Timers are siezed as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four.
27+
28+
The methods are:
29+
30+
Servo - Class for manipulating servo motors connected to Arduino pins.
31+
32+
attach(pin ) - Attaches a servo motor to an i/o pin.
33+
attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
34+
default min is 544, max is 2400
35+
36+
write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
37+
writeMicroseconds() - Sets the servo pulse width in microseconds
38+
read() - Gets the last written servo pulse width as an angle between 0 and 180.
39+
readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
40+
attached() - Returns true if there is a servo attached.
41+
detach() - Stops an attached servos from pulsing its i/o pin.
42+
*/
43+
44+
#ifndef Servo_h
45+
#define Servo_h
46+
47+
#include <inttypes.h>
48+
49+
#define Servo_VERSION 2 // software version of this library
50+
51+
#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
52+
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
53+
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
54+
#define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds
55+
56+
#if defined(__AVR_ATmega1280__)
57+
#define MAX_SERVOS 48 // the maximum number of servos (valid range is from 1 to 48)
58+
#else
59+
#define MAX_SERVOS 12 // this library supports up to 12 on a standard Arduino
60+
#endif
61+
62+
#define INVALID_SERVO 255 // flag indicating an invalid servo index
63+
64+
typedef struct {
65+
uint8_t nbr :6 ; // a pin number from 0 to 63
66+
uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false
67+
} ServoPin_t ;
68+
69+
typedef struct {
70+
ServoPin_t Pin;
71+
unsigned int ticks;
72+
} servo_t;
73+
74+
class Servo
75+
{
76+
public:
77+
Servo();
78+
uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
79+
uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes.
80+
void detach();
81+
void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds
82+
void writeMicroseconds(int value); // Write pulse width in microseconds
83+
int read(); // returns current pulse width as an angle between 0 and 180 degrees
84+
int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
85+
bool attached(); // return true if this servo is attached, otherwise false
86+
private:
87+
uint8_t servoIndex; // index into the channel data for this servo
88+
int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH
89+
int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
90+
};
91+
92+
#endif

examples/Knob/Knob.pde

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Controlling a servo position using a potentiometer (variable resistor)
2+
// by Michal Rinott <http://people.interaction-ivrea.it/m.rinott>
3+
4+
#include <Servo.h>
5+
6+
Servo myservo; // create servo object to control a servo
7+
8+
int potpin = 0; // analog pin used to connect the potentiometer
9+
int val; // variable to read the value from the analog pin
10+
11+
void setup()
12+
{
13+
myservo.attach(9); // attaches the servo on pin 9 to the servo object
14+
}
15+
16+
void loop()
17+
{
18+
val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023)
19+
val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (value between 0 and 180)
20+
myservo.write(val); // sets the servo position according to the scaled value
21+
delay(15); // waits for the servo to get there
22+
}

0 commit comments

Comments
 (0)