Library for using the TLC5940 as a servo controller.
Revision 2:1d2251574d35, committed 2014-10-21
- Comitter:
- dudanian
- Date:
- Tue Oct 21 06:04:24 2014 +0000
- Parent:
- 1:ba4e0390f72e
- Child:
- 3:3b04a122e508
- Commit message:
- Big changes to the code. Removed Servo files and made an inner class inside TLC5940Servo.
Changed in this revision
--- a/Servo.cpp Tue Oct 21 02:32:57 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* mbed R/C Servo Library - * - * Copyright (c) 2007-2010 sford, cstyles - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "Servo.h" -#include "mbed.h" - -static float clamp(float value, float min, float max) { - if(value < min) { - return min; - } else if(value > max) { - return max; - } else { - return value; - } -} - -Servo::Servo() { - calibrate(); - write(0.5); -} - -// used source code, but converted pwm pulse to 12 bit value -// assuming 20ms period, divide to get percentage and multiply to get value -void Servo::write(float percent) { - float offset = _range * 2.0 * (percent - 0.5); - _pw = (int)((0.0015 + clamp(offset, -_range, _range))/0.02 * 4096.0); - _p = clamp(percent, 0.0, 1.0); -} - -/*void Servo::position(float degrees) { - /* not used for now - float offset = _range * (degrees / _degrees); - _pw = 0.0015 + clamp(offset, -_range, _range); // need to change - -}*/ - -void Servo::calibrate(float range, float degrees) { - _range = range; - _degrees = degrees; -} - -float Servo::read() { - return _p; -} - -int Servo::pulsewidth() { - return _pw; -} - -float& Servo::operator= (float percent) { - write(percent); - return _p; -}
--- a/Servo.h Tue Oct 21 02:32:57 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* mbed R/C Servo Library - * Copyright (c) 2007-2010 sford, cstyles - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef SERVO_H -#define SERVO_H - -#include "mbed.h" - -/** - * Servo control class, modified to be used with the TLC5940 PWM IC - * - */ -class Servo { - -public: - /** Create a servo object NOT connected to a specified PwmOut pin - */ - Servo(); - - /** Set the servo position, normalised to it's full range - * - * @param percent A normalised number 0.0-1.0 to represent the full range. - */ - void write(float percent); - - /** Read the servo motors current position - * - * @param returns A normalised number 0.0-1.0 representing the full range. - */ - float read(); - - /** Read the servo motors current pulse width converted to TLC5940 PWM value - * - * @param returns A number between 0 and 4096 representing a pulse width. - */ - int pulsewidth(); - - /** Allows calibration of the range and angles for a particular servo - * - * @param range Pulsewidth range from center (1.5ms) to maximum/minimum position in seconds - * @param degrees Angle from centre to maximum/minimum position in degrees - */ - void calibrate(float range = 0.0005, float degrees = 45.0); - - /** Shorthand for the write and read functions */ - float& operator= (float percent); - -protected: - int _pw; - float _range; - float _degrees; - float _p; -}; - -#endif
--- a/TLC5940Servo.cpp Tue Oct 21 02:32:57 2014 +0000 +++ b/TLC5940Servo.cpp Tue Oct 21 06:04:24 2014 +0000 @@ -1,71 +1,118 @@ #include "TLC5940Servo.h" -TLC5940Servo::TLC5940Servo(PinName MOSI, PinName SCLK, PinName XLAT, PinName BLANK, - PinName GSCLK, const int number) : number(number), spi(MOSI, NC, SCLK), gsclk(GSCLK), - blank(BLANK), xlat(XLAT), newData(false), need_xlat(false) -{ - // Configure SPI to 12 bits and SPI_SPEED +TLC5940Servo::TLC5940Servo(PinName MOSI, PinName SCLK, PinName XLAT, PinName BLANK, + PinName GSCLK, const int number) : number(number), spi(MOSI, NC, SCLK), gsclk(GSCLK), + blank(BLANK), xlat(XLAT), newData(false), need_xlat(false) { spi.format(12, 0); spi.frequency(SPI_SPEED); - - // Set output pin states + xlat = 0; blank = 1; - - // Call the reset function every 4096 PWM outputs + reset_ticker.attach_us(this, &TLC5940Servo::reset, (1000000.0/GSCLK_SPEED) * 4096.0); - - // Configure FastPWM output for GSCLK frequency at 50% duty cycle + gsclk.period_us(1000000.0/GSCLK_SPEED); gsclk.write(.5); - // Create a data buffer to store the current Servo states - dataBuffer = new int[16 * number](); + servos = new Servo[16 * number]; } -TLC5940Servo::~TLC5940Servo() -{ - // Delete the buffer - delete[] dataBuffer; +TLC5940Servo::~TLC5940Servo() { + delete[] servos; +} + +void TLC5940Servo::calibrate(int index, float range, float degrees) { + servos[index].calibrate(range, degrees); } - -int& TLC5940Servo::operator[](int index) -{ - // Return the start of the correct data chunk - newData = true; - return dataBuffer[index]; +void TLC5940Servo::calibrate(float range, float degrees) { + for (int i = 0; i < 16 * number; i++) + servos[i].calibrate(range, degrees); } -void TLC5940Servo::reset() -{ +TLC5940Servo::Servo& TLC5940Servo::operator[](int index) { + newData = true; + return servos[index]; +} + +// most complicated method, heavily commented +void TLC5940Servo::reset() { gsclk.write(0); // turn off gsclk blank = 1; // start reset - + // latch data if new data was written if (need_xlat) { xlat = 1; xlat = 0; - + need_xlat = false; } - + blank = 0; // turn off reset gsclk.write(.5); // restart gsclk if (newData) { - - // Send GS data backwards - this makes the GS_buffer[0] index correspond to OUT0 - for (int i = (16 * number) - 1; i >= 0; i--) - { + + // Send data backwards - this makes servos[0] index correspond to OUT0 + for (int i = (16 * number) - 1; i >= 0; i--) { // Get the lower 12 bits of the buffer and send - spi.write(dataBuffer[i] & 0xFFF); + spi.write(servos[i].pulsewidth() & 0xFFF); } - + // Latch after current GS data is done being displayed need_xlat = true; - + // No new data to send (we just sent it!) newData = false; } -} \ No newline at end of file +} + +/** + * TLC5940 Inner Servo class + * + * Helps to abstract some of the details away + */ +static float clamp(float value, float min, float max) { + if(value < min) { + return min; + } else if(value > max) { + return max; + } else { + return value; + } +} + +TLC5940Servo::Servo::Servo() { + calibrate(); + write(0.5); +} + +// used source code, but converted pwm pulse to 12 bit value +// assuming 20ms period, divide to get percentage and multiply to get value +void TLC5940Servo::Servo::write(float percent) { + float offset = _range * 2.0 * (percent - 0.5); + _pw = (int)(4095 - ((0.0015 + clamp(offset, -_range, _range))/0.02 * 4096.0)); + _p = clamp(percent, 0.0, 1.0); +} + +void TLC5940Servo::Servo::calibrate(float range, float degrees) { + _range = range; + _degrees = degrees; +} + +float TLC5940Servo::Servo::read() { + return _p; +} + +int TLC5940Servo::Servo::pulsewidth() { + return _pw; +} + +TLC5940Servo::Servo& TLC5940Servo::Servo::operator= (float percent) { + write(percent); + return *this; +} + +TLC5940Servo::Servo::operator float() { + return read(); +}
--- a/TLC5940Servo.h Tue Oct 21 02:32:57 2014 +0000 +++ b/TLC5940Servo.h Tue Oct 21 06:04:24 2014 +0000 @@ -2,7 +2,7 @@ #define TLC5940Servo_H #include "FastPWM.h" - + /** * SPI speed used by the mbed to communicate with the TLC5940 * The TLC5940 supports up to 30Mhz. This should be kept as high @@ -14,65 +14,81 @@ * The rate at which the GSCLK pin is pulsed * This also controls how often the reset function is called * The rate at which the reset function is called can be calculated by: (1/GSCLK_SPEED) * 4096 - * - * Since the Servo period is 20ms (50Hz), we set this clock to 50*4096 = 204,800Hz according to the eqn above - * Also, since this clock is so slow, there is no real limit to the number of TLC5940s you can chain + * + * Since the Servo period is 20ms (50Hz), we set this clock to 50*4096 = 204,800Hz according to the equation above + * Also, since this clock is so slow, there is almost no limit to the number of TLC5940s you can chain */ #define GSCLK_SPEED 204800 /** - * This class controls a TLC5940 PWM driver to control Servo motors. - * It supports sending grayscale PWM data calibrated for Servos, but it does not support error checking or writing the EEPROM. - * This class uses the FastPWM library by Erik Olieman to continuously pulse the GSLCK pin without CPU intervention. After - * 4096 pulses, the private member funciton reset is called by the ticker. It resets the display by pulsing the BLANK pin. If new - * data has been set to be sent by the function setNewData, it is sent here. + * This class controls a TLC5940 PWM driver to control Servo motors. It supports sending grayscale PWM data calibrated for Servos, + * but it does not support error checking or writing the EEPROM. After 4096 pulses, the private member funciton reset is called by + * the ticker. It resets the driver by pulsing the BLANK pin. If new data has been set to be sent by the function setNewData, it + * is sent here. + * + * This class is a heavily modified versoin of the TLC5940 library created by Spencer Davis. This class also uses the FastPWM + * library by Erik Olieman to continuously pulse the GSLCK pin without CPU intervention. */ -class TLC5940Servo -{ +class TLC5940Servo { public: + /* + * Servo inner class to abstract some of the details. Based off of the mbed official Servo class + */ + class Servo { + public: + Servo(); + void write(float percent); + void calibrate(float range=0.0005, float degrees=45.0); + float read(); + int pulsewidth(); + Servo& operator= (float percent); + operator float(); + private: + float _p; + float _range; + float _degrees; + int _pw; + }; /** - * Set up the TLC5940 - * - * @param MOSI - The MOSI pin of the SPI bus - * @param SCLK - The SCK pin of the SPI bus - * @param XLAT - The XLAT pin of the TLC5940(s) - * @param BLANK - The BLANK pin of the TLC5940(s) - * @param GSCLK - The GSCLK pin of the TLC5940(s) - * @param number - The number of TLC5940s (optional) + * Set up the TLC5940 + * + * @param MOSI - The MOSI pin of the SPI bus + * @param SCLK - The SCK pin of the SPI bus + * @param XLAT - The XLAT pin of the TLC5940(s) + * @param BLANK - The BLANK pin of the TLC5940(s) + * @param GSCLK - The GSCLK pin of the TLC5940(s) + * @param number - The number of TLC5940s (optional) */ TLC5940Servo(PinName MOSI, PinName SCLK, PinName XLAT, PinName BLANK, - PinName GSCLK, const int number = 1); - - // Destructor used to delete memory + PinName GSCLK, const int number = 1); + + /** + * Destructor used to delete memory + */ ~TLC5940Servo(); - /** Allows calibration of the range and angles for all servos - * - * @param range Pulsewidth range from center (1.5ms) to maximum/minimum position in seconds - * @param degrees Angle from centre to maximum/minimum position in degrees - */ - void calibrate(float range, float degrees); - - /** Allows calibration of the range and angles for a particular servo + /** + * Allows calibration of the range and angles for a particular servo * * @param range Pulsewidth range from center (1.5ms) to maximum/minimum position in seconds * @param degrees Angle from centre to maximum/minimum position in degrees */ void calibrate(int index, float range, float degrees); - - /** Allows calibration of the range and angles for a particular servo + + /** + * Allows calibration of the range and angles for all servos * * @param range Pulsewidth range from center (1.5ms) to maximum/minimum position in seconds * @param degrees Angle from centre to maximum/minimum position in degrees */ - void calibrate(float *range, float *degrees); - + void calibrate(float range, float degrees); + /** - * Set the next chunk of grayscale data to be sent - * @param data - Array of 16 bit shorts containing 16 12 bit grayscale data chunks per TLC5940 - * @note These must be in intervals of at least (1/GSCLK_SPEED) * 4096 to be sent + * Array index operator for reading and writing from servos + * + * @param index Index of Servo in array */ - int& operator[](int index); + Servo& operator[](int index); private: // SPI port - only MOSI and SCK are used @@ -98,7 +114,7 @@ volatile bool need_xlat; // Buffers to store data until it is sent - int* dataBuffer; + Servo* dataBuffer; // Function to reset the display and send the next chunks of data void reset();