/** * Marlin 3D Printer Firmware * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] * * Based on Sprinter and grbl. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** * servo.cpp - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 * Copyright (c) 2009 Michael Margolis. All right reserved. */ /** * A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method. * The servos are pulsed in the background using the value most recently written using the write() method * * Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached. * Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four. * * The methods are: * * Servo - Class for manipulating servo motors connected to Arduino pins. * * attach(pin) - Attach a servo motor to an i/o pin. * attach(pin, min, max) - Attach to a pin, setting min and max values in microseconds * Default min is 544, max is 2400 * * write() - Set the servo angle in degrees. (Invalid angles —over MIN_PULSE_WIDTH— are treated as µs.) * writeMicroseconds() - Set the servo pulse width in microseconds. * move(pin, angle) - Sequence of attach(pin), write(angle), safe_delay(servo_delay[servoIndex]). * With DEACTIVATE_SERVOS_AFTER_MOVE it detaches after servo_delay[servoIndex]. * read() - Get the last-written servo pulse width as an angle between 0 and 180. * readMicroseconds() - Get the last-written servo pulse width in microseconds. * attached() - Return true if a servo is attached. * detach() - Stop an attached servo from pulsing its i/o pin. */ #ifdef __AVR__ #include "../../inc/MarlinConfig.h" #if HAS_SERVOS #include #include "../shared/servo.h" #include "../shared/servo_private.h" static volatile int8_t Channel[_Nbr_16timers]; // counter for the servo being pulsed for each timer (or -1 if refresh interval) /************ static functions common to all instances ***********************/ static inline void handle_interrupts(const timer16_Sequence_t timer, volatile uint16_t* TCNTn, volatile uint16_t* OCRnA) { int8_t cho = Channel[timer]; // Handle the prior Channel[timer] first if (cho < 0) // Channel -1 indicates the refresh interval completed... *TCNTn = 0; // ...so reset the timer else if (SERVO_INDEX(timer, cho) < ServoCount) // prior channel handled? extDigitalWrite(SERVO(timer, cho).Pin.nbr, LOW); // pulse the prior channel LOW Channel[timer] = ++cho; // Handle the next channel (or 0) if (cho < SERVOS_PER_TIMER && SERVO_INDEX(timer, cho) < ServoCount) { *OCRnA = *TCNTn + SERVO(timer, cho).ticks; // set compare to current ticks plus duration if (SERVO(timer, cho).Pin.isActive) // activated? extDigitalWrite(SERVO(timer, cho).Pin.nbr, HIGH); // yes: pulse HIGH } else { // finished all channels so wait for the refresh period to expire before starting over const unsigned int cval = ((unsigned)*TCNTn) + 32 / (SERVO_TIMER_PRESCALER), // allow 32 cycles to ensure the next OCR1A not missed ival = (unsigned int)usToTicks(REFRESH_INTERVAL); // at least REFRESH_INTERVAL has elapsed *OCRnA = max(cval, ival); Channel[timer] = -1; // reset the timer counter to 0 on the next call } } #ifndef WIRING // Wiring pre-defines signal handlers so don't define any if compiling for the Wiring platform // Interrupt handlers for Arduino #ifdef _useTimer1 SIGNAL(TIMER1_COMPA_vect) { handle_interrupts(_timer1, &TCNT1, &OCR1A); } #endif #ifdef _useTimer3 SIGNAL(TIMER3_COMPA_vect) { handle_interrupts(_timer3, &TCNT3, &OCR3A); } #endif #ifdef _useTimer4 SIGNAL(TIMER4_COMPA_vect) { handle_interrupts(_timer4, &TCNT4, &OCR4A); } #endif #ifdef _useTimer5 SIGNAL(TIMER5_COMPA_vect) { handle_interrupts(_timer5, &TCNT5, &OCR5A); } #endif #else // WIRING // Interrupt handlers for Wiring #ifdef _useTimer1 void Timer1Service() { handle_interrupts(_timer1, &TCNT1, &OCR1A); } #endif #ifdef _useTimer3 void Timer3Service() { handle_interrupts(_timer3, &TCNT3, &OCR3A); } #endif #endif // WIRING /****************** end of static functions ******************************/ void initISR(const timer16_Sequence_t timer_index) { switch (timer_index) { default: break; #ifdef _useTimer1 case _timer1: TCCR1A = 0; // normal counting mode TCCR1B = _BV(CS11); // set prescaler of 8 TCNT1 = 0; // clear the timer count #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__) SBI(TIFR, OCF1A); // clear any pending interrupts; SBI(TIMSK, OCIE1A); // enable the output compare interrupt #else // here if not ATmega8 or ATmega128 SBI(TIFR1, OCF1A); // clear any pending interrupts; SBI(TIMSK1, OCIE1A); // enable the output compare interrupt #endif #ifdef WIRING timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service); #endif break; #endif #ifdef _useTimer3 case _timer3: TCCR3A = 0; // normal counting mode TCCR3B = _BV(CS31); // set prescaler of 8 TCNT3 = 0; // clear the timer count #ifdef __AVR_ATmega128__ SBI(TIFR, OCF3A); // clear any pending interrupts; SBI(ETIMSK, OCIE3A); // enable the output compare interrupt #else SBI(TIFR3, OCF3A); // clear any pending interrupts; SBI(TIMSK3, OCIE3A); // enable the output compare interrupt #endif #ifdef WIRING timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only #endif break; #endif #ifdef _useTimer4 case _timer4: TCCR4A = 0; // normal counting mode TCCR4B = _BV(CS41); // set prescaler of 8 TCNT4 = 0; // clear the timer count TIFR4 = _BV(OCF4A); // clear any pending interrupts; TIMSK4 = _BV(OCIE4A); // enable the output compare interrupt break; #endif #ifdef _useTimer5 case _timer5: TCCR5A = 0; // normal counting mode TCCR5B = _BV(CS51); // set prescaler of 8 TCNT5 = 0; // clear the timer count TIFR5 = _BV(OCF5A); // clear any pending interrupts; TIMSK5 = _BV(OCIE5A); // enable the output compare interrupt break; #endif } } void finISR(const timer16_Sequence_t timer_index) { // Disable use of the given timer #ifdef WIRING switch (timer_index) { default: break; case _timer1: CBI( #if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) TIMSK1 #else TIMSK #endif , OCIE1A // disable timer 1 output compare interrupt ); timerDetach(TIMER1OUTCOMPAREA_INT); break; case _timer3: CBI( #if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) TIMSK3 #else ETIMSK #endif , OCIE3A // disable the timer3 output compare A interrupt ); timerDetach(TIMER3OUTCOMPAREA_INT); break; } #else // !WIRING // For arduino - in future: call here to a currently undefined function to reset the timer UNUSED(timer_index); #endif } #endif // HAS_SERVOS #endif // __AVR__