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