Skip to content

Commit e847a73

Browse files
committed
Initial version of NRF5 servo library
1 parent 6622bd1 commit e847a73

File tree

6 files changed

+642
-0
lines changed

6 files changed

+642
-0
lines changed

libraries/Servo/Servo.cpp

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

0 commit comments

Comments
 (0)