From 2fc1571df28b246e9ba90a4165c28f0e58901814 Mon Sep 17 00:00:00 2001 From: ensoniq Date: Thu, 17 Nov 2016 15:24:16 +0800 Subject: [PATCH] Updated library to support twice as many servos With the use of the OCRxB (Output Compare Register B), and not resetting the Timer Counter after each refresh cycle, we can have another set of servos pulsed by sharing the one Timer Counter --- library.properties | 2 +- src/avr/Servo.cpp | 50 +++++++++++++++++++++++++++++++++++++------ src/avr/ServoTimers.h | 3 ++- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/library.properties b/library.properties index f27ae5d..74caa4e 100644 --- a/library.properties +++ b/library.properties @@ -3,7 +3,7 @@ version=1.1.2 author=Michael Margolis, Arduino maintainer=Arduino sentence=Allows Arduino/Genuino boards to control a variety of servo motors. -paragraph=This library can control a great number of servos.
It makes careful use of timers: the library can control 12 servos using only 1 timer.
On the Arduino Due you can control up to 60 servos.
+paragraph=This library can control a great number of servos.
It makes careful use of timers: the library can control 24 servos using only 1 timer.
On the Arduino Due you can control up to 60 servos.
category=Device Control url=http://www.arduino.cc/en/Reference/Servo architectures=avr,sam,samd diff --git a/src/avr/Servo.cpp b/src/avr/Servo.cpp index 7fdd9d5..278cb47 100644 --- a/src/avr/Servo.cpp +++ b/src/avr/Servo.cpp @@ -34,6 +34,7 @@ static servo_t servos[MAX_SERVOS]; // static array of servo structures static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval) +static volatile uint16_t StartCount[_Nbr_16timers ]; // holder for last time servo update cycle was started for each timer uint8_t ServoCount = 0; // the total number of attached servos @@ -49,10 +50,10 @@ uint8_t ServoCount = 0; // the total number /************ static functions common to all instances ***********************/ -static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA) +static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnx) { if( Channel[timer] < 0 ) - *TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer + StartCount[timer] = *TCNTn; // channel set to -1 indicated that refresh interval completed so record the timer else{ if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true ) digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated @@ -60,16 +61,16 @@ static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t Channel[timer]++; // increment to the next channel if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) { - *OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks; + *OCRnx = *TCNTn + SERVO(timer,Channel[timer]).ticks; if(SERVO(timer,Channel[timer]).Pin.isActive == true) // check if activated digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high } else { // finished all channels so wait for the refresh period to expire before starting over - if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL) ) // allow a few ticks to ensure the next OCR1A not missed - *OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL); + if( ((unsigned)*TCNTn) + 4 - StartCount[timer] < usToTicks(REFRESH_INTERVAL) ) // allow a few ticks to ensure the next OCR1A not missed + *OCRnx = (unsigned int)StartCount[timer] + usToTicks(REFRESH_INTERVAL); else - *OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed + *OCRnx = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel } } @@ -83,6 +84,13 @@ SIGNAL (TIMER1_COMPA_vect) } #endif +#if defined(_useTimer1B) // Extra timer definition +SIGNAL (TIMER1_COMPB_vect) +{ + handle_interrupts(_timer1B, &TCNT1, &OCR1B); +} +#endif + #if defined(_useTimer3) SIGNAL (TIMER3_COMPA_vect) { @@ -112,6 +120,12 @@ void Timer1Service() handle_interrupts(_timer1, &TCNT1, &OCR1A); } #endif +#if defined(_useTimer1B) +void Timer1BService() +{ + handle_interrupts(_timer1B, &TCNT1, &OCR1B); +} +#endif #if defined(_useTimer3) void Timer3Service() { @@ -142,6 +156,22 @@ static void initISR(timer16_Sequence_t timer) } #endif +#if defined (_useTimer1B) + if(timer == _timer1B) { +#if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__) + TIFR |= _BV(OCF1B); // clear any pending interrupts; + TIMSK |= _BV(OCIE1B) ; // enable the output compare interrupt +#else + // here if not ATmega8 or ATmega128 + TIFR1 |= _BV(OCF1B); // clear any pending interrupts; + TIMSK1 |= _BV(OCIE1B) ; // enable the output compare interrupt +#endif +#if defined(WIRING) + timerAttach(TIMER1OUTCOMPAREB_INT, Timer1BService); +#endif + } +#endif + #if defined (_useTimer3) if(timer == _timer3) { TCCR3A = 0; // normal counting mode @@ -193,6 +223,14 @@ static void finISR(timer16_Sequence_t timer) #endif timerDetach(TIMER1OUTCOMPAREA_INT); } + else if(timer == _timer1B) { + #if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__) + TIMSK1 &= ~_BV(OCIE1B) ; // disable timer 1 output compare interrupt + #else + TIMSK &= ~_BV(OCIE1B) ; // disable timer 1 output compare interrupt + #endif + timerDetach(TIMER1OUTCOMPAREB_INT); + } else if(timer == _timer3) { #if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__) TIMSK3 &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt diff --git a/src/avr/ServoTimers.h b/src/avr/ServoTimers.h index 9794c8e..dcba247 100644 --- a/src/avr/ServoTimers.h +++ b/src/avr/ServoTimers.h @@ -54,6 +54,7 @@ typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t; #else // everything else #define _useTimer1 -typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t; +#define _useTimer1B +typedef enum { _timer1, _timer1B, _Nbr_16timers } timer16_Sequence_t; #endif