Library for using the TLC5940 as a servo controller.

Dependencies:   FastPWM

Dependents:   TLC5940ServoTest

Revision:
2:1d2251574d35
Parent:
0:9fc434ff7a03
Child:
5:56daa8c0697d
--- 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();
+}