Library for using the TLC5940 as a servo controller.

Dependencies:   FastPWM

Dependents:   TLC5940ServoTest

TLC5940Servo.cpp

Committer:
dudanian
Date:
2014-10-21
Revision:
5:56daa8c0697d
Parent:
2:1d2251574d35
Child:
6:b001282e967b

File content as of revision 5:56daa8c0697d:

#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) {
    spi.format(12, 0);
    spi.frequency(SPI_SPEED);

    xlat = 0;
    blank = 1;

    reset_ticker.attach_us(this, &TLC5940Servo::reset, (1000000.0/GSCLK_SPEED) * 4096.0);

    gsclk.period_us(1000000.0/GSCLK_SPEED);
    gsclk.write(.5);

    servos = new Servo[16 * number];
}

TLC5940Servo::~TLC5940Servo() {
    delete[] servos;
}

void TLC5940Servo::calibrate(int index, float range, float degrees) {
    servos[index].calibrate(range, degrees);
}

void TLC5940Servo::calibrate(float range, float degrees) {
    for (int i = 0; i < 16 * number; i++)
        servos[i].calibrate(range, degrees);
}

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 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(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;
    }
}

/**
 * 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);
}

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::position(float degrees) {
    float offset = _range * (degrees / _degrees);
    _pw = (int)(4095 - ((0.0015 + clamp(offset, -_range, _range))/0.02 * 4096.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& TLC5940Servo::Servo::operator= (TLC5940Servo::Servo& rhs) {
    write(rhs.read());
    return *this;
}

TLC5940Servo::Servo::operator float() {
    return read();
}