/****************************************************************************** * The MIT License * * Copyright (c) 2010, LeafLabs, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. *****************************************************************************/ #ifndef _SERVO_H_ #define _SERVO_H_ #include #include "wirish.h" /* hack for IDE compile */ /* Note on Arduino compatibility: In the Arduino implementation, PWM is done "by hand" in the sense that timer channels are hijacked in groups and an ISR is set which toggles Servo::attach()ed pins using digitalWrite(). While this scheme allows any pin to drive a servo, it chews up cycles and complicates the programmer's notion of when a particular timer channel will be in use. This implementation only allows Servo instances to Servo::attach() to pins that already have a timer channel associated with them, and just uses pwmWrite() to drive the wave. This introduces an incompatibility: while the Arduino implementation of attach() returns the affected channel on success and 0 on failure, this one returns true on success and false on failure. RC Servos expect a pulse every 20ms. Since periods are set for entire timers, rather than individual channels, attach()ing a Servo to a pin can interfere with other pins associated with the same timer. As always, the pin mapping mega table is your friend. */ // Pin number of unattached pins #define NOT_ATTACHED (-1) // Maximum angle in degrees you can write(), exclusive. Value chosen // for Arduino compatibility. #define SERVO_MAX_WRITE_ANGLE (200) // Default min (0 deg)/max(180 deg) pulse widths, in microseconds. // Value chosen for Arduino compatibility. #define SERVO_DEFAULT_MIN_PW (544) #define SERVO_DEFAULT_MAX_PW (2400) class Servo { public: Servo(); /* Pin has to have a timer channel associated with it already; * sets pinMode to PWM and returns true iff successful (failure * when pin doesn't support PWM). doesn't detach any ISRs * associated with timer channel. */ bool attach(uint8_t pin); /* Like attach(int), but with (inclusive) min (0 degree) and max * (180 degree) pulse widths, in microseconds. */ bool attach(uint8_t pin, uint16_t min, uint16_t max); /* Return pin number if currently attach()ed to a pin, NOT_ATTACHED otherwise. */ int attached() const { return pin; } /* Stop driving the wave by disabling the output compare interrupt. Returns true if this call did anything. */ bool detach(); /* If value < MAX_WRITE_ANGLE, treated as an angle in degrees. Otherwise, it's treated as a pulse width. */ void write(unsigned int value); /* If outside of [min, max] determined by attach(), it is clamped to lie in that range. */ void writeMicroseconds(uint16_t pulseWidth); /* Return servo target angle, in degrees. This will lie between 0 and 180. */ int read() const; /* Returns the current pulse width, in microseconds. This will lie within the [min, max] range. */ uint16_t readMicroseconds() const; private: int8_t pin; HardwareTimer *timer; int channel; uint16_t min; uint16_t max; }; #endif /* _SERVO_H_ */