Skip to content

Commit 1f8c964

Browse files
Claudio Indellicaticmaglie
Claudio Indellicati
authored andcommitted
Servo library for the SAMD architecture.
1 parent 8c60054 commit 1f8c964

File tree

4 files changed

+373
-3
lines changed

4 files changed

+373
-3
lines changed

libraries/Servo/library.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name=Servo
2-
version=1.0.3
2+
version=1.1.0
33
author=Michael Margolis, Arduino
44
maintainer=Arduino <info@arduino.cc>
55
sentence=Allows Arduino boards to control a variety of servo motors. For all Arduino boards.
66
paragraph=This library can control a great number of servos.<br />It makes careful use of timers: the library can control 12 servos using only 1 timer.<br />On the Arduino Due you can control up to 60 servos.<br />
77
category=Device Control
88
url=http://www.arduino.cc/en/Reference/Servo
9-
architectures=avr,sam
9+
architectures=avr,sam,samd

libraries/Servo/src/Servo.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,10 @@
6363
#include "avr/ServoTimers.h"
6464
#elif defined(ARDUINO_ARCH_SAM)
6565
#include "sam/ServoTimers.h"
66+
#elif defined(ARDUINO_ARCH_SAMD)
67+
#include "samd/ServoTimers.h"
6668
#else
67-
#error "This library only supports boards with an AVR or SAM processor."
69+
#error "This library only supports boards with an AVR, SAM or SAMD processor."
6870
#endif
6971

7072
#define Servo_VERSION 2 // software version of this library

libraries/Servo/src/samd/Servo.cpp

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
/*
2+
Copyright (c) 2015 Arduino LLC. All right reserved.
3+
4+
This library is free software; you can redistribute it and/or
5+
modify it under the terms of the GNU Lesser General Public
6+
License as published by the Free Software Foundation; either
7+
version 2.1 of the License, or (at your option) any later version.
8+
9+
This library is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
Lesser General Public License for more details.
13+
14+
You should have received a copy of the GNU Lesser General Public
15+
License along with this library; if not, write to the Free Software
16+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*/
18+
19+
#if defined(ARDUINO_ARCH_SAMD)
20+
21+
#include <Arduino.h>
22+
#include <Servo.h>
23+
24+
#define usToTicks(_us) ((clockCyclesPerMicrosecond() * _us) / 16) // converts microseconds to tick
25+
#define ticksToUs(_ticks) (((unsigned) _ticks * 16) / clockCyclesPerMicrosecond()) // converts from ticks back to microseconds
26+
27+
#define TRIM_DURATION 5 // compensation ticks to trim adjust for digitalWrite delays
28+
29+
static servo_t servos[MAX_SERVOS]; // static array of servo structures
30+
31+
uint8_t ServoCount = 0; // the total number of attached servos
32+
33+
static volatile int8_t currentServoIndex[_Nbr_16timers]; // index for the servo being pulsed for each timer (or -1 if refresh interval)
34+
35+
// convenience macros
36+
#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
37+
#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
38+
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
39+
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel
40+
41+
#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
42+
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo
43+
44+
#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY);
45+
46+
/************ static functions common to all instances ***********************/
47+
48+
void Servo_Handler(timer16_Sequence_t timer, Tc *pTc, uint8_t channel, uint8_t intFlag);
49+
#if defined (_useTimer1)
50+
void HANDLER_FOR_TIMER1(void) {
51+
Servo_Handler(_timer1, TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, INTFLAG_BIT_FOR_TIMER_1);
52+
}
53+
#endif
54+
#if defined (_useTimer2)
55+
void HANDLER_FOR_TIMER2(void) {
56+
Servo_Handler(_timer2, TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, INTFLAG_BIT_FOR_TIMER_2);
57+
}
58+
#endif
59+
60+
void Servo_Handler(timer16_Sequence_t timer, Tc *tc, uint8_t channel, uint8_t intFlag)
61+
{
62+
if (currentServoIndex[timer] < 0) {
63+
tc->COUNT16.COUNT.reg = (uint16_t) 0;
64+
WAIT_TC16_REGS_SYNC(tc)
65+
} else {
66+
if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) {
67+
digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, LOW); // pulse this channel low if activated
68+
}
69+
}
70+
71+
// Select the next servo controlled by this timer
72+
currentServoIndex[timer]++;
73+
74+
if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && currentServoIndex[timer] < SERVOS_PER_TIMER) {
75+
if (SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) { // check if activated
76+
digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, HIGH); // it's an active channel so pulse it high
77+
}
78+
79+
// Get the counter value
80+
uint16_t tcCounterValue = tc->COUNT16.COUNT.reg;
81+
WAIT_TC16_REGS_SYNC(tc)
82+
83+
tc->COUNT16.CC[channel].reg = (uint16_t) (tcCounterValue + SERVO(timer, currentServoIndex[timer]).ticks);
84+
WAIT_TC16_REGS_SYNC(tc)
85+
}
86+
else {
87+
// finished all channels so wait for the refresh period to expire before starting over
88+
89+
// Get the counter value
90+
uint16_t tcCounterValue = tc->COUNT16.COUNT.reg;
91+
WAIT_TC16_REGS_SYNC(tc)
92+
93+
if (tcCounterValue + 4UL < usToTicks(REFRESH_INTERVAL)) { // allow a few ticks to ensure the next OCR1A not missed
94+
tc->COUNT16.CC[channel].reg = (uint16_t) usToTicks(REFRESH_INTERVAL);
95+
}
96+
else {
97+
tc->COUNT16.CC[channel].reg = (uint16_t) (tcCounterValue + 4UL); // at least REFRESH_INTERVAL has elapsed
98+
}
99+
WAIT_TC16_REGS_SYNC(tc)
100+
101+
currentServoIndex[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
102+
}
103+
104+
// Clear the interrupt
105+
tc->COUNT16.INTFLAG.reg = intFlag;
106+
}
107+
108+
static inline void resetTC (Tc* TCx)
109+
{
110+
// Disable TCx
111+
TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
112+
WAIT_TC16_REGS_SYNC(TCx)
113+
114+
// Reset TCx
115+
TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
116+
WAIT_TC16_REGS_SYNC(TCx)
117+
while (TCx->COUNT16.CTRLA.bit.SWRST);
118+
}
119+
120+
static void _initISR(Tc *tc, uint8_t channel, uint32_t id, IRQn_Type irqn, uint8_t gcmForTimer, uint8_t intEnableBit)
121+
{
122+
// Enable GCLK for timer 1 (timer counter input clock)
123+
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(gcmForTimer));
124+
while (GCLK->STATUS.bit.SYNCBUSY);
125+
126+
// Reset the timer
127+
// TODO this is not the right thing to do if more than one channel per timer is used by the Servo library
128+
resetTC(tc);
129+
130+
// Set timer counter mode to 16 bits
131+
tc->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
132+
133+
// Set timer counter mode as normal PWM
134+
tc->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_NPWM;
135+
136+
// Set the prescaler factor to GCLK_TC/16. At nominal 48MHz GCLK_TC this is 3000 ticks per millisecond
137+
tc->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV16;
138+
139+
// Count up
140+
tc->COUNT16.CTRLBCLR.bit.DIR = 1;
141+
WAIT_TC16_REGS_SYNC(tc)
142+
143+
// First interrupt request after 1 ms
144+
tc->COUNT16.CC[channel].reg = (uint16_t) usToTicks(1000UL);
145+
WAIT_TC16_REGS_SYNC(tc)
146+
147+
// Configure interrupt request
148+
// TODO this should be changed if more than one channel per timer is used by the Servo library
149+
NVIC_DisableIRQ(irqn);
150+
NVIC_ClearPendingIRQ(irqn);
151+
NVIC_SetPriority(irqn, 0);
152+
NVIC_EnableIRQ(irqn);
153+
154+
// Enable the match channel interrupt request
155+
tc->COUNT16.INTENSET.reg = intEnableBit;
156+
157+
// Enable the timer and start it
158+
tc->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
159+
WAIT_TC16_REGS_SYNC(tc)
160+
}
161+
162+
static void initISR(timer16_Sequence_t timer)
163+
{
164+
#if defined (_useTimer1)
165+
if (timer == _timer1)
166+
_initISR(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, ID_TC_FOR_TIMER1, IRQn_FOR_TIMER1, GCM_FOR_TIMER_1, INTENSET_BIT_FOR_TIMER_1);
167+
#endif
168+
#if defined (_useTimer2)
169+
if (timer == _timer2)
170+
_initISR(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, ID_TC_FOR_TIMER2, IRQn_FOR_TIMER2, GCM_FOR_TIMER_2, INTENSET_BIT_FOR_TIMER_2);
171+
#endif
172+
}
173+
174+
static void finISR(timer16_Sequence_t timer)
175+
{
176+
#if defined (_useTimer1)
177+
// Disable the match channel interrupt request
178+
TC_FOR_TIMER1->COUNT16.INTENCLR.reg = INTENCLR_BIT_FOR_TIMER_1;
179+
#endif
180+
#if defined (_useTimer2)
181+
// Disable the match channel interrupt request
182+
TC_FOR_TIMER2->COUNT16.INTENCLR.reg = INTENCLR_BIT_FOR_TIMER_2;
183+
#endif
184+
}
185+
186+
static boolean isTimerActive(timer16_Sequence_t timer)
187+
{
188+
// returns true if any servo is active on this timer
189+
for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {
190+
if(SERVO(timer,channel).Pin.isActive == true)
191+
return true;
192+
}
193+
return false;
194+
}
195+
196+
/****************** end of static functions ******************************/
197+
198+
Servo::Servo()
199+
{
200+
if (ServoCount < MAX_SERVOS) {
201+
this->servoIndex = ServoCount++; // assign a servo index to this instance
202+
servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values
203+
} else {
204+
this->servoIndex = INVALID_SERVO; // too many servos
205+
}
206+
}
207+
208+
uint8_t Servo::attach(int pin)
209+
{
210+
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
211+
}
212+
213+
uint8_t Servo::attach(int pin, int min, int max)
214+
{
215+
timer16_Sequence_t timer;
216+
217+
if (this->servoIndex < MAX_SERVOS) {
218+
pinMode(pin, OUTPUT); // set servo pin to output
219+
servos[this->servoIndex].Pin.nbr = pin;
220+
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
221+
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
222+
this->max = (MAX_PULSE_WIDTH - max)/4;
223+
// initialize the timer if it has not already been initialized
224+
timer = SERVO_INDEX_TO_TIMER(servoIndex);
225+
if (isTimerActive(timer) == false) {
226+
initISR(timer);
227+
}
228+
servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive
229+
}
230+
return this->servoIndex;
231+
}
232+
233+
void Servo::detach()
234+
{
235+
timer16_Sequence_t timer;
236+
237+
servos[this->servoIndex].Pin.isActive = false;
238+
timer = SERVO_INDEX_TO_TIMER(servoIndex);
239+
if(isTimerActive(timer) == false) {
240+
finISR(timer);
241+
}
242+
}
243+
244+
void Servo::write(int value)
245+
{
246+
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
247+
if (value < MIN_PULSE_WIDTH)
248+
{
249+
if (value < 0)
250+
value = 0;
251+
else if (value > 180)
252+
value = 180;
253+
254+
value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
255+
}
256+
writeMicroseconds(value);
257+
}
258+
259+
void Servo::writeMicroseconds(int value)
260+
{
261+
// calculate and store the values for the given channel
262+
byte channel = this->servoIndex;
263+
if( (channel < MAX_SERVOS) ) // ensure channel is valid
264+
{
265+
if (value < SERVO_MIN()) // ensure pulse width is valid
266+
value = SERVO_MIN();
267+
else if (value > SERVO_MAX())
268+
value = SERVO_MAX();
269+
270+
value = value - TRIM_DURATION;
271+
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead
272+
servos[channel].ticks = value;
273+
}
274+
}
275+
276+
int Servo::read() // return the value as degrees
277+
{
278+
return map(readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
279+
}
280+
281+
int Servo::readMicroseconds()
282+
{
283+
unsigned int pulsewidth;
284+
if (this->servoIndex != INVALID_SERVO)
285+
pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION;
286+
else
287+
pulsewidth = 0;
288+
289+
return pulsewidth;
290+
}
291+
292+
bool Servo::attached()
293+
{
294+
return servos[this->servoIndex].Pin.isActive;
295+
}
296+
297+
#endif // ARDUINO_ARCH_SAMD
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
Copyright (c) 2015 Arduino LLC. All right reserved.
3+
4+
This library is free software; you can redistribute it and/or
5+
modify it under the terms of the GNU Lesser General Public
6+
License as published by the Free Software Foundation; either
7+
version 2.1 of the License, or (at your option) any later version.
8+
9+
This library is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
Lesser General Public License for more details.
13+
14+
You should have received a copy of the GNU Lesser General Public
15+
License along with this library; if not, write to the Free Software
16+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*/
18+
19+
/*
20+
* Defines for 16 bit timers used with Servo library
21+
*
22+
* If _useTimerX is defined then TimerX is a 16 bit timer on the current board
23+
* timer16_Sequence_t enumerates the sequence that the timers should be allocated
24+
* _Nbr_16timers indicates how many 16 bit timers are available.
25+
*/
26+
27+
#ifndef __SERVO_TIMERS_H__
28+
#define __SERVO_TIMERS_H__
29+
30+
/**
31+
* SAMD Only definitions
32+
* ---------------------
33+
*/
34+
35+
// For SAMD:
36+
#define _useTimer1
37+
//#define _useTimer2 // <- TODO do not activate until the code in Servo.cpp has been changed in order
38+
// to manage more than one channel per timer on the SAMD architecture
39+
40+
#if defined (_useTimer1)
41+
#define TC_FOR_TIMER1 TC4
42+
#define CHANNEL_FOR_TIMER1 0
43+
#define INTENSET_BIT_FOR_TIMER_1 TC_INTENSET_MC0
44+
#define INTENCLR_BIT_FOR_TIMER_1 TC_INTENCLR_MC0
45+
#define INTFLAG_BIT_FOR_TIMER_1 TC_INTFLAG_MC0
46+
#define ID_TC_FOR_TIMER1 ID_TC4
47+
#define IRQn_FOR_TIMER1 TC4_IRQn
48+
#define HANDLER_FOR_TIMER1 TC4_Handler
49+
#define GCM_FOR_TIMER_1 GCM_TC4_TC5
50+
#endif
51+
#if defined (_useTimer2)
52+
#define TC_FOR_TIMER2 TC4
53+
#define CHANNEL_FOR_TIMER2 1
54+
#define INTENSET_BIT_FOR_TIMER_2 TC_INTENSET_MC1
55+
#define INTENCLR_BIT_FOR_TIMER_2 TC_INTENCLR_MC1
56+
#define ID_TC_FOR_TIMER2 ID_TC4
57+
#define IRQn_FOR_TIMER2 TC4_IRQn
58+
#define HANDLER_FOR_TIMER2 TC4_Handler
59+
#define GCM_FOR_TIMER_2 GCM_TC4_TC5
60+
#endif
61+
62+
typedef enum {
63+
#if defined (_useTimer1)
64+
_timer1,
65+
#endif
66+
#if defined (_useTimer2)
67+
_timer2,
68+
#endif
69+
_Nbr_16timers } timer16_Sequence_t;
70+
71+
#endif // __SERVO_TIMERS_H__

0 commit comments

Comments
 (0)