Skip to content

Commit fd4c781

Browse files
committed
Merge branch 'servo' into hyperfirmata
2 parents bf534ec + 5624679 commit fd4c781

File tree

6 files changed

+653
-0
lines changed

6 files changed

+653
-0
lines changed

libraries/Servo/Servo.cpp

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
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+
#include <Arduino.h>
20+
#include <Servo.h>
21+
22+
// Converts microseconds to timer tick and vice versa
23+
#define usToTicks(_us) ((TMR_FREQ / 1000000L) * (_us))
24+
#define ticksToUs(_ticks) ((unsigned)_ticks / (TMR_FREQ / 1000000L))
25+
26+
#define TRIM_DURATION 5 // compensation ticks to trim adjust for digitalWrite delays
27+
28+
static servo_t servos[MAX_SERVOS]; // static array of servo structures
29+
30+
static uint8_t ServoCount = 0; // the total number of attached servos
31+
32+
// Index for the servo being pulsed for each timer (or -1 if refresh interval)
33+
static volatile int8_t currentServoIndex[_Nbr_16timers];
34+
#define REFRESH_INTERVAL_PERIOD (-1)
35+
36+
// Timer controlling this servo
37+
#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER))
38+
// Index of the servo on this timer
39+
#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER)
40+
// Servo index by timer and channel
41+
#define SERVO_INDEX(_timer, _channel) ((_timer * SERVOS_PER_TIMER) + _channel)
42+
// Servo structure by timer and channel
43+
#define SERVO(_timer, _channel) (servos[SERVO_INDEX(_timer,_channel)])
44+
45+
/************ static functions common to all instances ***********************/
46+
47+
#ifdef __cplusplus
48+
extern "C" {
49+
#endif
50+
51+
static __inline__ void Servo_Handler(timer16_Sequence_t timer, NRF_TIMER_Type *nrfTimer, uint32_t ccReg) __attribute__((always_inline));
52+
static __inline__ void Servo_Handler(timer16_Sequence_t timer, NRF_TIMER_Type *nrfTimer, uint32_t ccReg)
53+
{
54+
// Clear the interrupt event flag
55+
nrfTimer->EVENTS_COMPARE[ccReg] = 0;
56+
57+
if (currentServoIndex[timer] == REFRESH_INTERVAL_PERIOD) {
58+
// Refresh interval completed so reset the timer
59+
nrfTimer->TASKS_CLEAR = 1;
60+
nrfTimer->CC[ccReg] = 0;
61+
} else {
62+
if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount &&
63+
SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) {
64+
// It's an active channel so pulse it low
65+
NRF_GPIO->OUTCLR = digitalPinToBitMask(SERVO(timer, currentServoIndex[timer]).Pin.nbr);
66+
}
67+
}
68+
69+
// Select the next servo controlled by this timer
70+
currentServoIndex[timer]++;
71+
if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount &&
72+
currentServoIndex[timer] < SERVOS_PER_TIMER) {
73+
if (SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) {
74+
// It's an active channel so pulse it high
75+
NRF_GPIO->OUTSET = digitalPinToBitMask(SERVO(timer,currentServoIndex[timer]).Pin.nbr);
76+
}
77+
// Get the counter value
78+
unsigned int ticks = SERVO(timer, currentServoIndex[timer]).ticks;
79+
nrfTimer->CC[ccReg] += ticks + (ticks >> 6);
80+
} else {
81+
// Finished all channels so wait for the refresh period to expire before starting over
82+
// Allow a few ticks to ensure the next timer event is not missed
83+
if (nrfTimer->CC[ccReg] + 4 < usToTicks(REFRESH_INTERVAL)) {
84+
nrfTimer->CC[ccReg] = (uint32_t)usToTicks(REFRESH_INTERVAL) + (REFRESH_INTERVAL >> 5);
85+
} else {
86+
// Refresh interval has elapsed
87+
nrfTimer->CC[ccReg] += 4;
88+
}
89+
// Will get incremented at the end of the refresh period to start again at the first channel
90+
currentServoIndex[timer] = REFRESH_INTERVAL_PERIOD;
91+
}
92+
}
93+
94+
#if defined (_useTimer0)
95+
void TMR0_HANDLER(void) {
96+
Servo_Handler(_timer0, TMR0_POINTER, TMR0_CC_REG);
97+
}
98+
#endif
99+
#if defined (_useTimer1)
100+
void TMR1_HANDLER(void) {
101+
Servo_Handler(_timer1, TMR1_POINTER, TMR1_CC_REG);
102+
}
103+
#endif
104+
#if defined (_useTimer2)
105+
void TMR2_HANDLER(void) {
106+
Servo_Handler(_timer2, TMR2_POINTER, TMR2_CC_REG);
107+
}
108+
#endif
109+
#if defined (_useTimer3)
110+
void TMR3_HANDLER(void) {
111+
Servo_Handler(_timer3, TMR3_POINTER, TMR3_CC_REG);
112+
}
113+
#endif
114+
#if defined (_useTimer4)
115+
void TMR4_HANDLER(void) {
116+
Servo_Handler(_timer4, TMR4_POINTER, TMR4_CC_REG);
117+
}
118+
#endif
119+
120+
#ifdef __cplusplus
121+
}
122+
#endif
123+
124+
static void _initISR(NRF_TIMER_Type *nrfTimer, uint32_t ccReg, IRQn_Type timerIRQn)
125+
{
126+
__disable_irq();
127+
nrfTimer->TASKS_STOP = 1;
128+
129+
nrfTimer->MODE = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos;
130+
nrfTimer->BITMODE = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos;
131+
nrfTimer->PRESCALER = TMR_FREQ_REG_PRESCALER << TIMER_PRESCALER_PRESCALER_Pos;
132+
nrfTimer->SHORTS = TIMER_SHORTS_DISABLE_ALL;
133+
nrfTimer->TASKS_CLEAR = 1; // Clear task
134+
nrfTimer->EVENTS_COMPARE[ccReg] = 0; // Clear interrupt event flag
135+
nrfTimer->CC[ccReg] = (uint32_t)usToTicks(REFRESH_INTERVAL);
136+
137+
if (ccReg == TMR_CC_REG0) {
138+
nrfTimer->INTENSET = (TIMER_INTENSET_COMPARE0_Set << TIMER_INTENSET_COMPARE0_Pos);
139+
} else if (ccReg == TMR_CC_REG1) {
140+
nrfTimer->INTENSET = (TIMER_INTENSET_COMPARE1_Set << TIMER_INTENSET_COMPARE1_Pos);
141+
} else if (ccReg == TMR_CC_REG2) {
142+
nrfTimer->INTENSET = (TIMER_INTENSET_COMPARE2_Set << TIMER_INTENSET_COMPARE2_Pos);
143+
} else if (ccReg == TMR_CC_REG3) {
144+
nrfTimer->INTENSET = (TIMER_INTENSET_COMPARE3_Set << TIMER_INTENSET_COMPARE3_Pos);
145+
}
146+
NVIC_ClearPendingIRQ(timerIRQn);
147+
NVIC_SetPriority(timerIRQn, TMR_PRIORITY);
148+
NVIC_EnableIRQ(timerIRQn);
149+
150+
__enable_irq();
151+
nrfTimer->TASKS_START = 1;
152+
}
153+
154+
static void initISR(timer16_Sequence_t timer)
155+
{
156+
#if defined (_useTimer0)
157+
if (timer == _timer0) {
158+
_initISR(TMR0_POINTER, TMR0_CC_REG, TMR0_IRQN);
159+
}
160+
#endif
161+
#if defined (_useTimer1)
162+
if (timer == _timer1) {
163+
_initISR(TMR1_POINTER, TMR1_CC_REG, TMR1_IRQN);
164+
}
165+
#endif
166+
#if defined (_useTimer2)
167+
if (timer == _timer2) {
168+
_initISR(TMR2_POINTER, TMR2_CC_REG, TMR2_IRQN);
169+
}
170+
#endif
171+
#if defined (_useTimer3)
172+
if (timer == _timer3) {
173+
_initISR(TMR3_POINTER, TMR3_CC_REG, TMR3_IRQN);
174+
}
175+
#endif
176+
#if defined (_useTimer4)
177+
if (timer == _timer4) {
178+
_initISR(TMR4_POINTER, TMR4_CC_REG, TMR4_IRQN);
179+
}
180+
#endif
181+
}
182+
183+
static void finISR(timer16_Sequence_t timer)
184+
{
185+
#if defined (_useTimer0)
186+
if (timer == _timer0) {
187+
TMR0_POINTER->TASKS_STOP = 1;
188+
}
189+
#endif
190+
#if defined (_useTimer1)
191+
if (timer == _timer1) {
192+
TMR1_POINTER->TASKS_STOP = 1;
193+
}
194+
#endif
195+
#if defined (_useTimer2)
196+
if (timer == _timer2) {
197+
TMR2_POINTER->TASKS_STOP = 1;
198+
}
199+
#endif
200+
#if defined (_useTimer3)
201+
if (timer == _timer3) {
202+
TMR3_POINTER->TASKS_STOP = 1;
203+
}
204+
#endif
205+
#if defined (_useTimer4)
206+
if (timer == _timer4) {
207+
TMR4_POINTER->TASKS_STOP = 1;
208+
}
209+
#endif
210+
}
211+
212+
static boolean isTimerActive(timer16_Sequence_t timer)
213+
{
214+
// Returns true if any servo is active on this timer
215+
for (uint8_t channel = 0; channel < SERVOS_PER_TIMER; channel++) {
216+
if (SERVO(timer, channel).Pin.isActive == true) {
217+
return true;
218+
}
219+
}
220+
return false;
221+
}
222+
223+
/****************** end of static functions ******************************/
224+
225+
Servo::Servo()
226+
{
227+
if (ServoCount < MAX_SERVOS) {
228+
this->servoIndex = ServoCount++; // assign a servo index to this instance
229+
servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values
230+
} else {
231+
this->servoIndex = INVALID_SERVO; // too many servos
232+
}
233+
}
234+
235+
uint8_t Servo::attach(int pin)
236+
{
237+
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
238+
}
239+
240+
uint8_t Servo::attach(int pin, int min, int max)
241+
{
242+
if (this->servoIndex < MAX_SERVOS) {
243+
pinMode(pin, OUTPUT);
244+
servos[this->servoIndex].Pin.nbr = pin;
245+
this->min = min < MIN_PULSE_WIDTH ? MIN_PULSE_WIDTH : min;
246+
this->max = max > MAX_PULSE_WIDTH ? MAX_PULSE_WIDTH : max;
247+
timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(this->servoIndex);
248+
if (isTimerActive(timer) == false) {
249+
initISR(timer);
250+
}
251+
// This must be set after the check for isTimerActive
252+
servos[this->servoIndex].Pin.isActive = true;
253+
}
254+
return this->servoIndex;
255+
}
256+
257+
void Servo::detach()
258+
{
259+
servos[this->servoIndex].Pin.isActive = false;
260+
timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(this->servoIndex);
261+
if (isTimerActive(timer) == false) {
262+
finISR(timer);
263+
}
264+
}
265+
266+
void Servo::write(int value)
267+
{
268+
// Treat values less than MIN_PULSE_WIDTH as angles clamped to 0-180 degrees
269+
if (value < MIN_PULSE_WIDTH) {
270+
if (value < 0) {
271+
value = 0;
272+
} else if (value > 180) {
273+
value = 180;
274+
}
275+
value = map(value, 0, 180, this->min, this->max);
276+
}
277+
this->writeMicroseconds(value);
278+
}
279+
280+
void Servo::writeMicroseconds(int value)
281+
{
282+
// calculate and store the values for the given channel
283+
if (this->servoIndex < MAX_SERVOS) { // ensure channel is valid
284+
if (value < this->min) { // ensure pulse width is valid
285+
value = this->min;
286+
} else if (value > this->max) {
287+
value = this->max;
288+
}
289+
servos[this->servoIndex].ticks = usToTicks(value);
290+
}
291+
}
292+
293+
int Servo::read() // return the value as degrees
294+
{
295+
return map(this->readMicroseconds() + 1, this->min, this->max, 0, 180);
296+
}
297+
298+
int Servo::readMicroseconds()
299+
{
300+
return (this->servoIndex != INVALID_SERVO) ?
301+
ticksToUs(servos[this->servoIndex].ticks) : 0;
302+
}
303+
304+
bool Servo::attached()
305+
{
306+
return servos[this->servoIndex].Pin.isActive;
307+
}

0 commit comments

Comments
 (0)