ServoEasing
LightweightServo.hpp
Go to the documentation of this file.
1 /*
2  * LightweightServo.hpp
3  *
4  * Lightweight Servo implementation timer hardware and no interrupts or other overhead.
5  * Supported pins:
6  * ATmega328: pin 44 = OC5C/PL5, 45 = OC5B/PL4 and 46 = OC5A/PL3 using only timer5 hardware
7  * ATmega2560 pin 9 = OC1A and 10 = OC1B on 328 using only timer1 hardware
8  * Provides auto initialization.
9  * 300 bytes code size / 4 bytes RAM including auto initialization compared to 700 / 48 bytes for Arduino Servo library.
10  * 8 bytes for each call to setLightweightServoPulse...
11  *
12  * Copyright (C) 2019-2024 Armin Joachimsmeyer
13  * armin.joachimsmeyer@gmail.com
14  *
15  * This file is part of ServoEasing https://github.com/ArminJo/ServoEasing.
16  *
17  * ServoEasing is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
25  * See the GNU General Public License for more details.
26 
27  * You should have received a copy of the GNU General Public License
28  * along with this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
29  *
30  */
31 
32 #ifndef _LIGHTWEIGHT_SERVO_HPP
33 #define _LIGHTWEIGHT_SERVO_HPP
34 
35 #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || defined (__AVR_ATmega328PB__) || defined(__AVR_ATmega2560__)
36 #include "LightweightServo.h"
37 
38 // After all includes
39 #if defined(DEBUG)
40 #define LOCAL_DEBUG
41 #else
42 //#define LOCAL_DEBUG // This enables debug output only for this file
43 #endif
44 
45 /*
46  * Variables to enable adjustment for different servo types
47  * 544 and 2400 are values compatible with standard arduino values
48  * 4 bytes RAM compared to 48 bytes for standard Arduino library
49  */
50 int sMicrosecondsForServo0Degree = 544;
51 int sMicrosecondsForServo180Degree = 2400;
52 
53 #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || defined (__AVR_ATmega328PB__)
54 /*
55  * Use 16 bit timer1 for generating 2 servo signals entirely by hardware without any interrupts.
56  * Use FastPWM mode and generate pulse at start of the 20 ms period
57  * The 2 servo signals are tied to pin 9 and 10 of an 328.
58  * Attention - both pins are set to OUTPUT here!
59  * 32 bytes code size
60  * Supports pin 9 = OC1A + 10 = OC1B
61  */
62 void initLightweightServoPin9And10() {
63  initLightweightServoPins();
64 }
65 
66 /*
67  * Use 16 bit timer1 for generating 2 servo signals entirely by hardware without any interrupts.
68  * Use FastPWM mode and generate pulse at start of the 20 ms period
69  * The 2 servo signals are tied to pin 9 and 10 of an ATMega328.
70  * Attention - the selected pin is set to OUTPUT here!
71  * 54 bytes code size
72  */
73 void initLightweightServoPin9_10(bool aUsePin9, bool aUsePin10) {
74 
75  uint8_t tNewTCCR1A = TCCR1A & (_BV(COM1A1) | _BV(COM1B1)); // keep existing COM1A1 and COM1B1 settings
76  tNewTCCR1A |= _BV(WGM11); // FastPWM Mode mode TOP (20 ms) determined by ICR1
77 
78  if (aUsePin9) {
79  DDRB |= _BV(DDB1); // set OC1A = PortB1 -> PIN 9 to output direction
80  tNewTCCR1A |= _BV(COM1A1); // non-inverting Compare Output mode OC1A
81  OCR1A = UINT16_MAX; // Set counter > ICR1 here, to avoid output signal generation.
82  }
83  if (aUsePin10) {
84  DDRB |= _BV(DDB2); // set OC1B = PortB2 -> PIN 10 to output direction
85  tNewTCCR1A |= _BV(COM1B1); // non-inverting Compare Output mode OC1B
86  OCR1B = UINT16_MAX; // Set counter > ICR1 here, to avoid output signal generation.
87  }
88  TCCR1A = tNewTCCR1A;
89  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11); // set prescaler to 8, FastPWM Mode mode bits WGM13 + WGM12
90  ICR1 = ISR_COUNT_FOR_20_MILLIS; // set period to 20 ms
91 }
92 
93 /*
94  * Disables Pin 10!
95  */
96 void initLightweightServoPin9() {
97  DDRB |= _BV(DDB1); // set OC1A = PortB1 -> PIN 9 to output direction
98  TCCR1A = _BV(WGM11) | _BV(COM1A1); // FastPWM Mode mode TOP (20 ms) determined by ICR1, non-inverting Compare Output mode OC1A
99  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11); // set prescaler to 8, FastPWM Mode mode bits WGM13 + WGM12
100  ICR1 = ISR_COUNT_FOR_20_MILLIS; // set period to 20 ms
101  OCR1A = UINT16_MAX; // Set counter > ICR1 here, to avoid output signal generation.
102 }
103 /*
104  * Disables Pin 9!
105  */
106 void initLightweightServoPin10() {
107  DDRB |= _BV(DDB2); // set OC1B = PortB2 -> PIN 10 to output direction
108  TCCR1A = _BV(WGM11) | _BV(COM1B1); // FastPWM Mode mode TOP (20 ms) determined by ICR1, non-inverting Compare Output mode OC1B
109  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11); // set prescaler to 8, FastPWM Mode mode bits WGM13 + WGM12
110  ICR1 = ISR_COUNT_FOR_20_MILLIS; // set period to 20 ms
111  OCR1B = UINT16_MAX; // Set counter > ICR1 here, to avoid output signal generation.
112 }
113 
114 void deinitLightweightServoPin9_10(bool aUsePin9, bool aUsePin10) {
115  if (aUsePin9) {
116  DDRB &= ~(_BV(DDB1)); // set OC1A = PortB1 -> PIN 9 to input direction
117  TCCR1A &= ~(_BV(COM1A1)); // disable non-inverting Compare Output mode OC1A
118  }
119  if (aUsePin10) {
120  DDRB &= ~(_BV(DDB2)); // set OC1B = PortB2 -> PIN 10 to input direction
121  TCCR1A &= ~(_BV(COM1B1)); // disable non-inverting Compare Output mode OC1B
122  }
123 }
124 
125 /*
126  * @param aDegree - If value is below 180 then assume degree, otherwise assume microseconds
127  * @param aUpdateFast - If true, enable starting a new output pulse if more than 5 ms since last one, some servo might react faster in this mode.
128  * @param aUsePin9 - If false, then Pin10 is used
129  * 236 / 186(without auto init) bytes code size
130  */
131 int writeLightweightServo(int aDegree, bool aUsePin9, bool aUpdateFast) {
132  if (aDegree <= 180) {
133  aDegree = DegreeToMicrosecondsLightweightServo(aDegree);
134  }
135  writeMicrosecondsLightweightServo(aDegree, aUsePin9, aUpdateFast);
136  return aDegree;
137 }
138 
139 void writeMicrosecondsLightweightServo(int aMicroseconds, bool aUsePin9, bool aUpdateFast) {
140 #if !defined(DISABLE_SERVO_TIMER_AUTO_INITIALIZE)
141  // auto initialize
142  if ((TCCR1B != (_BV(WGM13) | _BV(WGM12) | _BV(CS11))) || (aUsePin9 && ((TCCR1A & ~_BV(COM1B1)) != (_BV(COM1A1) | _BV(WGM11))))
143  || (!aUsePin9 && ((TCCR1A & ~_BV(COM1A1)) != (_BV(COM1B1) | _BV(WGM11))))) {
144  initLightweightServoPin9_10(aUsePin9, !aUsePin9);
145  }
146 #endif
147  // since the resolution is 1/2 of microsecond for 16 MHz CPU clock and prescaler of 8
148 #if (F_CPU == 16000000L)
149  aMicroseconds *= 2;
150 #elif (F_CPU < 8000000L) // for 8 MHz resolution is exactly 1 microsecond :-)
151  aMicroseconds /= (8000000L / F_CPU);
152 #endif
153  if (aUpdateFast) {
154  uint16_t tTimerCount = TCNT1;
155  if (tTimerCount > ISR_COUNT_FOR_2_5_MILLIS) {
156  // more than 2.5 ms since last pulse -> start a new one
157  TCNT1 = ICR1 - 1;
158  }
159  }
160  if (aUsePin9) {
161  OCR1A = aMicroseconds;
162  } else {
163  OCR1B = aMicroseconds;
164  }
165 }
166 
167 /*
168  * Pin 9 / Channel A. If value is below 180 then assume degree, otherwise assume microseconds
169  */
170 void write9(int aDegree, bool aUpdateFast) {
171  writeLightweightServo(aDegree, true, aUpdateFast);
172 }
173 
174 void writeMicroseconds9(int aMicroseconds, bool aUpdateFast) {
175  writeMicrosecondsLightweightServo(aMicroseconds, true, aUpdateFast);
176 }
177 
178 /*
179  * Without auto initialize!
180  */
181 void writeMicroseconds9Direct(int aMicroseconds) {
182 #if (F_CPU == 16000000L)
183  aMicroseconds *= 2;
184 #elif (F_CPU < 8000000L) // for 8 MHz resolution is exactly 1 microsecond :-)
185  aMicroseconds /= (8000000L / F_CPU);
186 #endif
187  OCR1A = aMicroseconds;
188 }
189 
190 /*
191  * Pin 10 / Channel B
192  */
193 void write10(int aDegree, bool aUpdateFast) {
194  writeLightweightServo(aDegree, false, aUpdateFast);
195 }
196 
197 void writeMicroseconds10(int aMicroseconds, bool aUpdateFast) {
198  writeMicrosecondsLightweightServo(aMicroseconds, false, aUpdateFast);
199 }
200 
201 /*
202  * Without auto initialize!
203  */
204 void writeMicroseconds10Direct(int aMicroseconds) {
205 #if (F_CPU == 16000000L)
206  aMicroseconds *= 2;
207 #elif (F_CPU < 8000000L) // for 8 MHz resolution is exactly 1 microsecond :-)
208  aMicroseconds /= (8000000L / F_CPU);
209 #endif
210  OCR1B = aMicroseconds;
211 }
212 #endif
213 
214 /*
215  * Use 16 bit timer1 for generating 2 servo signals entirely by hardware without any interrupts.
216  * Use FastPWM mode and generate pulse at start of the 20 ms period
217  * Attention - both pins are set to OUTPUT here!
218  * 32 bytes code size
219  * Assume, that PRR1 PRTIM5 bit is low, which enables timer 5
220  * Supported pins:
221  * ATmega328: pin 44 = OC5C/PL5, 45 = OC5B/PL4 and 46 = OC5A/PL3 using only timer5 hardware
222  * ATmega2560 pin 9 = OC1A and 10 = OC1B on 328 using only timer1 hardware
223  */
224 void initLightweightServoPins() {
225 #if defined(__AVR_ATmega2560__)
226  /*
227  * Periods below 20 ms gives problems with long signals i.e. the positioning is not possible
228  */
229  DDRL |= _BV(DDL3) | _BV(DDL4) | _BV(DDL5); // Required! Set pins pin 44 = OC5C/PL5, 45 = OC5B/PL4 + 46 = OC5A/PL3 to output direction
230  TCCR5A = _BV(COM5A1) | _BV(COM5B1) | _BV(COM5C1) | _BV(WGM51); // FastPWM Mode mode TOP (20 ms) determined by ICR1 - non-inverting Compare Output mode OC1A+OC1B
231  TCCR5B = _BV(WGM53) | _BV(WGM52) | _BV(CS51); // set prescaler to 8, FastPWM mode mode bits WGM53 + WGM52 - other available prescaler are 1 and 64 :-(
232  ICR5 = ISR_COUNT_FOR_20_MILLIS; // set period to 20 ms
233 #else
234  DDRB |= _BV(DDB1) | _BV(DDB2); // Required! Set pins OC1A = PortB1 -> PIN 9 and OC1B = PortB2 -> PIN 10 to output direction
235  TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11); // FastPWM Mode mode TOP (20 ms) determined by ICR1 - non-inverting Compare Output mode OC1A+OC1B
236  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11); // set prescaler to 8, FastPWM mode mode bits WGM13 + WGM12 - other available prescaler are 1 and 64 :-(
237  ICR1 = ISR_COUNT_FOR_20_MILLIS; // set period to 20 ms
238 #endif
239  // do not set counter here, since with counter = 0 (default) no output signal is generated.
240 }
241 
242 /*
243  * Use 16 bit timer for generating 2 servo signals entirely by hardware without any interrupts.
244  * Use FastPWM mode and generate pulse at start of the 20 ms period
245  * Attention - the selected pin is set to OUTPUT here!
246  * 54 bytes code size
247  * Set pin to output (required) and enable non-inverting Compare Output mode
248  * Set output compare > ICRx, to avoid output signal generation.
249  */
250 void checkAndInitLightweightServoPin(uint8_t aPin) {
251  bool tPinWasNotInitialized = false;
252  // Mask all other bits to zero!
253 #if defined(__AVR_ATmega2560__)
254  uint8_t tNewTCCR5A = TCCR5A & (_BV(COM5A1) | _BV(COM5B1) | _BV(COM5C1) | _BV(WGM51));
255  if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_A_PIN && !(TCCR5A & (_BV(COM5A1)))) {
256  OCR5A = UINT16_MAX;
257  DDRL |= (_BV(DDL3));
258  tNewTCCR5A |= (_BV(COM5A1)) | _BV(WGM51); // FastPWM Mode mode TOP (20 ms) determined by ICR
259  tPinWasNotInitialized = true;
260  } else if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_B_PIN && !(TCCR5A & (_BV(COM5B1)))) {
261  OCR5B = UINT16_MAX;
262  DDRL |= (_BV(DDL4));
263  tNewTCCR5A |= (_BV(COM5B1)) | _BV(WGM51);
264  tPinWasNotInitialized = true;
265  } else if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_C_PIN && !(TCCR5A & (_BV(COM5C1)))) {
266  OCR5C = UINT16_MAX;
267  DDRL |= (_BV(DDL5));
268  tNewTCCR5A |= (_BV(COM5C1)) | _BV(WGM51);
269  tPinWasNotInitialized = true;
270  }
271 #else
272  uint8_t tNewTCCR1A = TCCR1A & (_BV(COM1A1) | _BV(COM1B1)); // keep existing COM1A1 and COM1B1 settings
273  if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_A_PIN && !(TCCR1A & (_BV(COM1A1)))) {
274  OCR1A = UINT16_MAX;
275  DDRB |= _BV(DDB1); // set OC1A = PortB1 -> PIN 9 to output direction
276  tNewTCCR1A |= (_BV(COM1A1)) | _BV(WGM11); // FastPWM Mode mode TOP (20 ms) determined by ICR1
277  tPinWasNotInitialized = true;
278  } else if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_B_PIN && !(TCCR1A & (_BV(COM1B1)))) {
279  OCR1B = UINT16_MAX;
280  DDRB |= _BV(DDB2); // set OC1B = PortB2 -> PIN 10 to output direction
281  tNewTCCR1A |= (_BV(COM1B1)) | _BV(WGM11);
282  tPinWasNotInitialized = true;
283  }
284 #endif
285 
286  if (tPinWasNotInitialized) {
287 #if defined(LOCAL_DEBUG)
288  Serial.print(F("Auto initialize pin "));
289  Serial.print(aPin);
290  Serial.print(F(" TCCR5A=0x"));
291  Serial.println(tNewTCCR5A,HEX);
292 #endif
293  /*
294  * Initialize timer with constant values
295  */
296 #if defined(__AVR_ATmega2560__)
297  TCCR5A = tNewTCCR5A;
298  TCCR5B = _BV(WGM53) | _BV(WGM52) | _BV(CS51); // set prescaler to 8, FastPWM mode mode bits WGM53 + WGM52 - other available prescaler are 1 and 64 :-(
299  /*
300  * Periods below 20 ms gives problems with long signals i.e. the positioning is not possible
301  */
302  ICR5 = ISR_COUNT_FOR_20_MILLIS; // set period to 20 ms
303  // do not set counter here, since with counter = 0 (default) no output signal is generated.
304 #else
305  TCCR1A = tNewTCCR1A;
306  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11); // set prescaler to 8, FastPWM Mode mode bits WGM13 + WGM12
307  ICR1 = ISR_COUNT_FOR_20_MILLIS; // set period to 20 ms
308 #endif
309  }
310 }
311 
312 /*
313  * Set pin to input and disable non-inverting Compare Output mode
314  * Init state is reflected by COMXX1 register bit
315  */
316 void deinitLightweightServoPin(uint8_t aPin) {
317 #if defined(__AVR_ATmega2560__)
318  if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_A_PIN) {
319  DDRL &= ~(_BV(DDL3));
320  TCCR5A &= ~(_BV(COM5A1));
321  } else if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_B_PIN) {
322  DDRL &= ~(_BV(DDL4));
323  TCCR5A &= ~(_BV(COM5B1));
324  } else {
325  DDRL &= ~(_BV(DDL5));
326  TCCR5A &= ~(_BV(COM5C1));
327  }
328 #else
329  if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_A_PIN) {
330  DDRB &= ~(_BV(DDB1)); // set OC1A = PortB1 -> PIN 9 to input direction
331  TCCR1A &= ~(_BV(COM1A1)); // disable non-inverting Compare Output mode OC1A
332  } else if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_B_PIN) {
333  DDRB &= ~(_BV(DDB2)); // set OC1B = PortB2 -> PIN 10 to input direction
334  TCCR1A &= ~(_BV(COM1B1)); // disable non-inverting Compare Output mode OC1B
335  }
336 #endif
337 }
338 
339 /*
340  * @param aDegree - If value is below 180 then assume degree, otherwise assume microseconds
341  * @param aUpdateFast - If true, enable starting a new output pulse if more than 5 ms since last one, some servo might react faster in this mode.
342  * @param aUsePin9 - If false, then Pin10 is used
343  * 236 / 186(without auto init) bytes code size
344  */
345 int writeLightweightServoPin(int aDegree, uint8_t aPin, bool aUpdateFast) {
346  if (aDegree <= 180) {
347  aDegree = DegreeToMicrosecondsLightweightServo(aDegree);
348  }
349  writeMicrosecondsLightweightServoPin(aDegree, aPin, aUpdateFast);
350  return aDegree;
351 }
352 
353 void writeMicrosecondsLightweightServoPin(int aMicroseconds, uint8_t aPin, bool aUpdateFast, bool aDoAutoInit) {
354 #if defined(LOCAL_DEBUG)
355  Serial.print(F(" Micros=")); // trailing space required if called by _writeMicrosecondsOrUnits()
356  Serial.print(aMicroseconds);
357  Serial.print(F(" pin="));
358  Serial.println(aPin);
359 #endif
360 #if !defined(DISABLE_SERVO_TIMER_AUTO_INITIALIZE)
361  if (aDoAutoInit) {
362  checkAndInitLightweightServoPin(aPin);
363  }
364 #endif
365 // since the resolution is 1/2 of microsecond for 16 MHz CPU clock and prescaler of 8
366 #if (F_CPU == 16000000L)
367  aMicroseconds *= 2;
368 #elif (F_CPU < 8000000L) // for 8 MHz resolution is exactly 1 microsecond :-)
369 aMicroseconds /= (8000000L / F_CPU);
370 #endif
371 #if defined(__AVR_ATmega2560__)
372 if (aUpdateFast) {
373  uint16_t tTimerCount = TCNT5;
374  if (tTimerCount > ISR_COUNT_FOR_2_5_MILLIS) {
375  // more than 2.5 ms since last pulse -> start a new one
376  TCNT5 = ICR5 - 1;
377  }
378 }
379 if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_A_PIN) {
380  OCR5A = aMicroseconds;
381 } else if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_B_PIN) {
382  OCR5B = aMicroseconds;
383 } else {
384  OCR5C = aMicroseconds;
385 }
386 #else
387  if (aUpdateFast) {
388  uint16_t tTimerCount = TCNT1;
389  if (tTimerCount > ISR_COUNT_FOR_2_5_MILLIS) {
390  // more than 2.5 ms since last pulse -> start a new one
391  TCNT1 = ICR1 - 1;
392  }
393  }
394  if (aPin == LIGHTWEIGHT_SERVO_CHANNEL_A_PIN) {
395  OCR1A = aMicroseconds;
396  } else {
397  OCR1B = aMicroseconds;
398  }
399 #endif
400 }
401 
402 /*
403  * Sets the period of the servo pulses. Reasonable values are 2500 to 20000 microseconds.
404  * No parameter checking is done here!
405  */
406 void setLightweightServoRefreshRate(unsigned int aRefreshPeriodMicroseconds) {
407 #if defined(__AVR_ATmega2560__)
408  ICR5 = aRefreshPeriodMicroseconds * 2;
409 #else
410  ICR1 = aRefreshPeriodMicroseconds * 2;
411 #endif
412 }
413 /*
414  * Set the mapping pulse width values for 0 and 180 degree
415  */
416 void setLightweightServoPulseMicrosFor0And180Degree(int aMicrosecondsForServo0Degree, int aMicrosecondsForServo180Degree) {
417  sMicrosecondsForServo0Degree = aMicrosecondsForServo0Degree;
418  sMicrosecondsForServo180Degree = aMicrosecondsForServo180Degree;
419 }
420 
421 /*
422  * Conversion functions
423  */
424 int DegreeToMicrosecondsLightweightServo(int aDegree) {
425  return (map(aDegree, 0, 180, sMicrosecondsForServo0Degree, sMicrosecondsForServo180Degree));
426 }
427 
428 int MicrosecondsToDegreeLightweightServo(int aMicroseconds) {
429  return map(aMicroseconds, sMicrosecondsForServo0Degree, sMicrosecondsForServo180Degree, 0, 180);
430 }
431 
432 /*
433  * LightweightServo class functions
434  */
435 uint8_t LightweightServo::attach(int aPin) {
436  LightweightServoPin = aPin;
437  checkAndInitLightweightServoPin(aPin);
438  return aPin;
439 }
440 
441 /*
442  * do not use parameters aMicrosecondsForServo0Degree and aMicrosecondsForServo180Degree
443  */
444 uint8_t LightweightServo::attach(int aPin, int aMicrosecondsForServo0Degree, int aMicrosecondsForServo180Degree) {
445  LightweightServoPin = aPin;
446  MicrosecondsForServo0Degree = aMicrosecondsForServo0Degree;
447  MicrosecondsForServo180Degree = aMicrosecondsForServo180Degree;
448  checkAndInitLightweightServoPin(aPin);
449  return aPin;
450 }
451 
452 void LightweightServo::detach() {
453  deinitLightweightServoPin(LightweightServoPin);
454 }
455 
456 void LightweightServo::write(int aTargetDegreeOrMicrosecond) {
457  if (aTargetDegreeOrMicrosecond <= 180) {
458  aTargetDegreeOrMicrosecond = (map(aTargetDegreeOrMicrosecond, 0, 180, MicrosecondsForServo0Degree,
459  MicrosecondsForServo180Degree));
460  }
461  // The last false parameter requires 8 byte more than DISABLE_SERVO_TIMER_AUTO_INITIALIZE, but saves around 60 bytes anyway
462  writeMicrosecondsLightweightServoPin(aTargetDegreeOrMicrosecond, LightweightServoPin, false, false);
463 }
464 
465 void LightweightServo::writeMicroseconds(int aTargetMicrosecond) {
466  // The last false parameter requires 8 byte more than DISABLE_SERVO_TIMER_AUTO_INITIALIZE, but saves around 60 bytes anyway
467  writeMicrosecondsLightweightServoPin(aTargetMicrosecond, LightweightServoPin, false, false);
468 }
469 
470 #if defined(LOCAL_DEBUG)
471 #undef LOCAL_DEBUG
472 #endif
473 #endif // _LIGHTWEIGHT_SERVO_HPP
474 #endif // defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega2560__)
LightweightServo.h