1
1
/*
2
- Copyright (c) 2015 Michael C. Miller. All right reserved.
2
+ Copyright (c) 2015 Michael C. Miller. All right reserved.
3
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.
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
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.
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
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
- */
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
18
19
19
#if defined(ESP8266)
20
20
26
26
27
27
const uint32_t c_CycleCompensation = 4 ; // compensation us to trim adjust for digitalWrite delays
28
28
29
+ #define INVALID_PIN 63 // flag indicating never attached servo
30
+
29
31
struct ServoInfo {
30
- uint8_t pin : 6 ; // a pin number from 0 to 63
32
+ uint8_t pin : 6 ; // a pin number from 0 to 62, 63 reserved
31
33
uint8_t isActive : 1 ; // true if this channel is enabled, pin not pulsed if false
34
+ uint8_t isDetaching : 1 ; // true if this channel is being detached, maintains pulse integrity
32
35
};
33
36
34
37
struct ServoState {
@@ -76,29 +79,49 @@ static void Servo_Handler(T* timer)
76
79
if (servoIndex < s_servoCount && s_servos[servoIndex].info .isActive ) {
77
80
// pulse this channel low if activated
78
81
digitalWrite (s_servos[servoIndex].info .pin , LOW);
82
+
83
+ if (s_servos[servoIndex].info .isDetaching ) {
84
+ s_servos[servoIndex].info .isActive = false ;
85
+ s_servos[servoIndex].info .isDetaching = false ;
86
+ }
79
87
}
80
88
timer->nextChannel ();
81
89
}
82
90
83
91
servoIndex = SERVO_INDEX (timer->timerId (), timer->getCurrentChannel ());
84
92
85
- if (servoIndex < s_servoCount && timer->getCurrentChannel () < SERVOS_PER_TIMER) {
93
+ if (servoIndex < s_servoCount &&
94
+ timer->getCurrentChannel () < SERVOS_PER_TIMER) {
86
95
timer->SetPulseCompare (timer->usToTicks (s_servos[servoIndex].usPulse ) - c_CycleCompensation);
87
96
88
- if (s_servos[servoIndex].info .isActive ) { // check if activated
89
- digitalWrite (s_servos[servoIndex].info .pin , HIGH); // its an active channel so pulse it high
97
+ if (s_servos[servoIndex].info .isActive ) {
98
+ if (s_servos[servoIndex].info .isDetaching ) {
99
+ // it was active, reset state and leave low
100
+ s_servos[servoIndex].info .isActive = false ;
101
+ s_servos[servoIndex].info .isDetaching = false ;
102
+ }
103
+ else {
104
+ // its an active channel so pulse it high
105
+ digitalWrite (s_servos[servoIndex].info .pin , HIGH);
106
+ }
90
107
}
91
108
}
92
109
else {
93
- // finished all channels so wait for the refresh period to expire before starting over
94
- // allow a few ticks to ensure the next match is not missed
95
- uint32_t refreshCompare = timer->usToTicks (REFRESH_INTERVAL);
96
- if ((timer->GetCycleCount () + c_CycleCompensation * 2 ) < refreshCompare) {
97
- timer->SetCycleCompare (refreshCompare - c_CycleCompensation);
110
+ if (!isTimerActive (timer->timerId ())) {
111
+ // no active running channels on this timer, stop the ISR
112
+ finISR (timer->timerId ());
98
113
}
99
114
else {
100
- // at least REFRESH_INTERVAL has elapsed
101
- timer->SetCycleCompare (timer->GetCycleCount () + c_CycleCompensation * 2 );
115
+ // finished all channels so wait for the refresh period to expire before starting over
116
+ // allow a few ticks to ensure the next match is not missed
117
+ uint32_t refreshCompare = timer->usToTicks (REFRESH_INTERVAL);
118
+ if ((timer->GetCycleCount () + c_CycleCompensation * 2 ) < refreshCompare) {
119
+ timer->SetCycleCompare (refreshCompare - c_CycleCompensation);
120
+ }
121
+ else {
122
+ // at least REFRESH_INTERVAL has elapsed
123
+ timer->SetCycleCompare (timer->GetCycleCount () + c_CycleCompensation * 2 );
124
+ }
102
125
}
103
126
104
127
timer->setEndOfCycle ();
@@ -166,6 +189,10 @@ Servo::Servo()
166
189
// set default _minUs and _maxUs incase write() is called before attach()
167
190
_minUs = MIN_PULSE_WIDTH;
168
191
_maxUs = MAX_PULSE_WIDTH;
192
+
193
+ s_servos[_servoIndex].info .isActive = false ;
194
+ s_servos[_servoIndex].info .isDetaching = false ;
195
+ s_servos[_servoIndex].info .pin = INVALID_PIN;
169
196
}
170
197
else {
171
198
_servoIndex = INVALID_SERVO; // too many servos
@@ -182,9 +209,11 @@ uint8_t Servo::attach(int pin, int minUs, int maxUs)
182
209
ServoTimerSequence timerId;
183
210
184
211
if (_servoIndex < MAX_SERVOS) {
185
- pinMode (pin, OUTPUT); // set servo pin to output
186
- digitalWrite (pin, LOW);
187
- s_servos[_servoIndex].info .pin = pin;
212
+ if (s_servos[_servoIndex].info .pin == INVALID_PIN) {
213
+ pinMode (pin, OUTPUT); // set servo pin to output
214
+ digitalWrite (pin, LOW);
215
+ s_servos[_servoIndex].info .pin = pin;
216
+ }
188
217
189
218
// keep the min and max within 200-3000 us, these are extreme
190
219
// ranges and should support extreme servos while maintaining
@@ -197,6 +226,7 @@ uint8_t Servo::attach(int pin, int minUs, int maxUs)
197
226
if (!isTimerActive (timerId)) {
198
227
initISR (timerId);
199
228
}
229
+ s_servos[_servoIndex].info .isDetaching = false ;
200
230
s_servos[_servoIndex].info .isActive = true ; // this must be set after the check for isTimerActive
201
231
}
202
232
return _servoIndex;
@@ -206,10 +236,8 @@ void Servo::detach()
206
236
{
207
237
ServoTimerSequence timerId;
208
238
209
- s_servos[_servoIndex].info .isActive = false ;
210
- timerId = SERVO_INDEX_TO_TIMER (_servoIndex);
211
- if (!isTimerActive (timerId)) {
212
- finISR (timerId);
239
+ if (s_servos[_servoIndex].info .isActive ) {
240
+ s_servos[_servoIndex].info .isDetaching = true ;
213
241
}
214
242
}
215
243
0 commit comments