/* Copyright (c) 2009 by Alex Leone This file is part of the Arduino TLC5940 Library. The Arduino TLC5940 Library 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. The Arduino TLC5940 Library 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 The Arduino TLC5940 Library. If not, see . */ #ifndef TLC_SERVOS_H #define TLC_SERVOS_H /** \file TLC servo functions. */ #include "Tlc5940.h" #ifndef SERVO_MAX_ANGLE /** The maximum angle of the servo. */ #define SERVO_MAX_ANGLE 180 #endif #ifndef SERVO_MIN_WIDTH /** The 1ms pulse width for zero degrees (0 - 4095). */ #define SERVO_MIN_WIDTH 204 #endif #ifndef SERVO_MAX_WIDTH /** The 2ms pulse width for 180 degrees (0 - 4095). */ #define SERVO_MAX_WIDTH 410 #endif #ifndef SERVO_TIMER1_TOP /** The top value for XLAT and BLANK pulses. This is with the div8 prescale, so \f$\displaystyle f_{PWM} = \frac{f_{osc}}{2 * 8 * SERVO\_TIMER1\_TOP} \f$ The default is 20000, which corresponds to 50Hz. */ #define SERVO_TIMER1_TOP 20000 #endif #ifndef SERVO_TIMER2_TOP /** The top value for GSCLK pulses. Related to SERVO_TIMER1_TOP by \f$\displaystyle SERVO\_TIMER2\_TOP = \frac{2 * 8 * SERVO\_TIMER1\_TOP}{4096} - 1 \f$ The default is 77. */ #define SERVO_TIMER2_TOP 77 #endif void tlc_initServos(uint8_t initAngle = 0); void tlc_setServo(TLC_CHANNEL_TYPE channel, uint8_t angle); uint8_t tlc_getServo(TLC_CHANNEL_TYPE channel); uint16_t tlc_angleToVal(uint8_t angle); uint8_t tlc_valToAngle(uint16_t value); /** \addtogroup ExtendedFunctions \code #include "tlc_servos.h" \endcode - void tlc_initServos(uint8_t initAngle = 0) - initializes the tlc for servos. - void tlc_setServo(TLC_CHANNEL_TYPE channel, uint8_t angle) - sets a servo to an angle - uint8_t tlc_getServo(TLC_CHANNEL_TYPE channel) - gets the currently set servo angle */ /* @{ */ /** Initializes the tlc. \param initAngle the initial angle to set all servos to (0 - SERVO_MAX_ANGLE). */ void tlc_initServos(uint8_t initAngle) { Tlc.init(tlc_angleToVal(initAngle)); #if defined(__AVR__) TCCR1B &= ~(_BV(CS12) | _BV(CS11) | _BV(CS10)); // stop timer1 ICR1 = SERVO_TIMER1_TOP; TCNT1 = 0; #ifdef TLC_ATMEGA_8_H uint8_t oldTCCR2 = TCCR2; TCCR2 = 0; TCNT2 = 0; OCR2 = SERVO_TIMER2_TOP / 2; TCCR2 = oldTCCR2; #elif defined(TLC_TIMER3_GSCLK) // TODO: timer3 implementation needed... // pull requests would be most welcome! ;-) #else uint8_t oldTCCR2B = TCCR2B; TCCR2B = 0; TCNT2 = 0; OCR2A = SERVO_TIMER2_TOP; TCCR2B = oldTCCR2B; #endif TCCR1B |= _BV(CS11); // start timer1 with div 8 prescale #elif defined(__arm__) && defined(TEENSYDUINO) //clear_XLAT_interrupt(); uint32_t sc __attribute__ ((unused)) = FTM1_SC; FTM1_SC = 0; // stop timer CMT_MSC = 0; CMT_CGH1 = TLC_TIMER_TEENSY3_SERVO_CGH1; CMT_CGL1 = TLC_TIMER_TEENSY3_SERVO_CGL1; CMT_MSC = 0x01; // GSCLK target is 204800 Hz // TODO: this reconfiguration of FTM1 crashes... why? // pull requests would be most welcome! ;-) FTM1_CNT = 0; FTM1_MOD = TLC_TIMER_TEENSY3_SERVO_MOD; FTM1_C0V = TLC_TIMER_TEENSY3_SERVO_MOD - TLC_TIMER_TEENSY3_SERVO_CV; FTM1_C1V = TLC_TIMER_TEENSY3_SERVO_MOD - TLC_TIMER_TEENSY3_SERVO_CV - 1; FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_CPWMS | TLC_TIMER_TEENSY3_SERVO_PS; #endif } /** Sets a servo on channel to angle. \param channel which channel to set \param angle (0 - SERVO_MAX_ANGLE) */ void tlc_setServo(TLC_CHANNEL_TYPE channel, uint8_t angle) { Tlc.set(channel, tlc_angleToVal(angle)); } /** Gets the current angle that channel is set to. \param channel which channel to get */ uint8_t tlc_getServo(TLC_CHANNEL_TYPE channel) { return tlc_valToAngle(Tlc.get(channel)); } /** Converts and angle (0 - SERVO_MAX_ANGLE) to the inverted tlc channel value (4095 - 0). */ uint16_t tlc_angleToVal(uint8_t angle) { return 4095 - SERVO_MIN_WIDTH - ( ((uint16_t)(angle) * (uint16_t)(SERVO_MAX_WIDTH - SERVO_MIN_WIDTH)) / SERVO_MAX_ANGLE); } /** Converts an inverted tlc channel value (4095 - 0) into an angle (0 - SERVO_MAX_ANGLE). */ uint8_t tlc_valToAngle(uint16_t value) { return SERVO_MAX_ANGLE * (4095 - SERVO_MIN_WIDTH - value) / (SERVO_MAX_WIDTH - SERVO_MIN_WIDTH); } /* @} */ #endif