eBoard ๐Ÿ‰  โ‘ โ‘งโ‘จ
Written for SIA 2017/2018
eagle_Servo.h
Go to the documentation of this file.
1 #ifndef EAGLE_EBOARD_HELPLIB_SERVO
2  #define EAGLE_EBOARD_HELPLIB_SERVO
3 
4 
13 //=====================================================================================================================================================
14 // Macro Definitions
15 //=====================================================================================================================================================
16 
17  //import servo-class
18  //shameless copy paste :D
20  #define _useTimer1
21  typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t;
22  #define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
23  #define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
24  #define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
25  #define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds
26 
27  #define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer
28  #define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
29 
30  #define INVALID_SERVO 255 // flag indicating an invalid servo index
31 
32 //=====================================================================================================================================================
33 // ServoPin_t
34 //=====================================================================================================================================================
35 
40  typedef struct {
42  uint8_t nbr :6 ; // a pin number from 0 to 63
44  uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false
45  } ServoPin_t;
46 
47 //=====================================================================================================================================================
48 // Servo_t
49 //=====================================================================================================================================================
50 
55  typedef struct {
57  ServoPin_t Pin;
59  volatile unsigned int ticks;
60  } servo_t;
61 
62 //=====================================================================================================================================================
63 // Servo
64 //=====================================================================================================================================================
65 
66  //-------------------------------------------------------------------------------------------------------------------------------------------------
67  // class
68  //-------------------------------------------------------------------------------------------------------------------------------------------------
69 
71 
83  class Servo {
84  public:
86  Servo();
92  uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
100  uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes.
102  void detach();
107  void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds
112  void writeMicroseconds(int value); // Write pulse width in microseconds
117  inline int read(); // returns current pulse width as an angle between 0 and 180 degrees
122  int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
127  inline bool attached(); // return true if this servo is attached, otherwise false
128  private:
130  uint8_t servoIndex; // index into the channel data for this servo
132  int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH
134  int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
135  };
137 
138  //-------------------------------------------------------------------------------------------------------------------------------------------------
139  // definitions
140  //-------------------------------------------------------------------------------------------------------------------------------------------------
141 
142  #define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to tick (assumes prescale of 8) // 12 Aug 2009
143  #define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds
144 
145 
146  #define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays // 12 August 2009
147 
148  //#define NBR_TIMERS (MAX_SERVOS / SERVOS_PER_TIMER)
149 
150  static servo_t servos[MAX_SERVOS]; // static array of servo structures
151  static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval)
152 
153  uint8_t ServoCount = 0; // the total number of attached servos
154 
155  // convenience macros
156  #define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
157  #define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
158  #define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
159  #define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel
160 
161  #define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
162  #define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo
163 
164  static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
165  {
166  if( Channel[timer] < 0 )
167  *TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
168  else{
169  if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true )
170  digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated
171  }
172 
173  Channel[timer]++; // increment to the next channel
174  if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
175  *OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks;
176  if(SERVO(timer,Channel[timer]).Pin.isActive == true) // check if activated
177  digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high
178  }
179  else {
180  // finished all channels so wait for the refresh period to expire before starting over
181  if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL) ) // allow a few ticks to ensure the next OCR1A not missed
182  *OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);
183  else
184  *OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed
185  Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
186  }
187  }
188 
189  #ifndef WIRING // Wiring pre-defines signal handlers so don't define any if compiling for the Wiring platform
190  // Interrupt handlers for Arduino
191  #if defined(_useTimer1)
192  SIGNAL (TIMER1_COMPA_vect)
193  {
194  handle_interrupts(_timer1, &TCNT1, &OCR1A);
195  }
196  #endif
197  #elif defined WIRING
198  // Interrupt handlers for Wiring
199  #if defined(_useTimer1)
200  void Timer1Service()
201  {
202  handle_interrupts(_timer1, &TCNT1, &OCR1A);
203  }
204  #endif
205  #endif
206 
207  inline int Servo::read() // return the value as degrees
208  {
209  return map( this->readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
210  }
211 
212 
213 Servo::Servo() {
214  if( ServoCount < MAX_SERVOS) {
215  this->servoIndex = ServoCount++; // assign a servo index to this instance
216  servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values - 12 Aug 2009
217  }
218  else
219  this->servoIndex = INVALID_SERVO ; // too many servos
220  }
221 
222  uint8_t Servo::attach(int pin) {
223  return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
224  }
225 
226  uint8_t Servo::attach(int pin, int min, int max) {
227  if(this->servoIndex < MAX_SERVOS ) {
228  pinMode( pin, OUTPUT) ; // set servo pin to output
229  servos[this->servoIndex].Pin.nbr = pin;
230  // todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
231  this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
232  this->max = (MAX_PULSE_WIDTH - max)/4;
233  // initialize the timer if it has not already been initialized
234  timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
235  if(isTimerActive(timer) == false)
236  initISR(timer);
237  servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive
238  }
239  return this->servoIndex ;
240  }
241 
242  void Servo::detach() {
243  servos[this->servoIndex].Pin.isActive = false;
244  timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
245  if(isTimerActive(timer) == false) {
246  finISR(timer);
247  }
248  }
249 
250  void Servo::write(int value) {
251  if(value < MIN_PULSE_WIDTH)
252  { // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
253  if(value < 0) value = 0;
254  if(value > 180) value = 180;
255  value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
256  }
257  this->writeMicroseconds(value);
258  }
259 
260  void Servo::writeMicroseconds(int value) {
261  // calculate and store the values for the given channel
262  byte channel = this->servoIndex;
263  if( (channel < MAX_SERVOS) ) // ensure channel is valid
264  {
265  if( value < SERVO_MIN() ) // ensure pulse width is valid
266  value = SERVO_MIN();
267  else if( value > SERVO_MAX() )
268  value = SERVO_MAX();
269 
270  value = value - TRIM_DURATION;
271  value = usToTicks(value); // convert to ticks after compensating for interrupt overhead - 12 Aug 2009
272 
273  uint8_t oldSREG = SREG;
274  cli();
275  servos[channel].ticks = value;
276  SREG = oldSREG;
277  }
278  }
279 
280 
282  unsigned int pulsewidth;
283  if( this->servoIndex != INVALID_SERVO )
284  pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION ; // 12 aug 2009
285  else
286  pulsewidth = 0;
287 
288  return pulsewidth;
289  }
290 
291  inline bool Servo::attached() {
292  return servos[this->servoIndex].Pin.isActive ;
293  }
294 
295  //========= TIMER
296 
297  static void initISR(timer16_Sequence_t timer) {
298  if(timer == _timer1) {
299  TCCR1A = 0; // normal counting mode
300  TCCR1B = _BV(CS11); // set prescaler of 8
301  TCNT1 = 0; // clear the timer count
302  TIFR1 |= _BV(OCF1A); // clear any pending interrupts;
303  TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt
304  #if defined(WIRING)
305  timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service);
306  #endif
307  }
308  }
309 
310  static void finISR(timer16_Sequence_t timer) {
311  #if defined WIRING // Wiring
312  if(timer == _timer1) {
313  TIMSK &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt
314  timerDetach(TIMER1OUTCOMPAREA_INT);
315  }
316  else if(timer == _timer3) {
317  ETIMSK &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt
318  timerDetach(TIMER3OUTCOMPAREA_INT);
319  }
320  #else
321  //For arduino - in future: call here to a currently undefined function to reset the timer
322  (void) timer; // squash "unused parameter 'timer' [-Wunused-parameter]" warning
323  #endif
324  }
325 
326  static bool isTimerActive(timer16_Sequence_t timer) {
327  // returns true if any servo is active on this timer
328  for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {
329  if(SERVO(timer,channel).Pin.isActive == true)
330  return true;
331  }
332  return false;
333  }
334 
335 
336 
337 
339 #endif
void write(int value)
sets angle or pulse width of the Servo
bool attached()
check attached status
int read()
reads the current pulse width as angle
int8_t max
maximum is this value times 4 added to MAX_PULSE_WIDTH
Definition: eagle_Servo.h:134
void detach()
detaches the connected pin
Servo()
the constructor
uint8_t attach(int pin)
this will tell the Servo-instance which pin the data cable is connected to
void writeMicroseconds(int value)
sets pulse width of the Servo
uint8_t servoIndex
the channel index this servo is using
Definition: eagle_Servo.h:130
int readMicroseconds()
reads the current pulse width
int8_t min
minimum is this value times 4 added to MIN_PULSE_WIDTH
Definition: eagle_Servo.h:132
[NANO] The standard Arduino Servo Class
Definition: eagle_Servo.h:83