Published

Dependents:   DuckLights

Fork of TLC5940 by Spencer Davis

Revision:
4:ab6b451bbf40
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TLC5955.cpp	Sat Jun 09 23:22:13 2018 +0000
@@ -0,0 +1,297 @@
+#include "TLC5955.h"
+#include "math.h"
+
+extern void init_clock(PinName clkPin);
+
+DigitalOut led2(LED2);
+
+// gamma correction array compensates for non-linearities
+// https://learn.adafruit.com/led-tricks-gamma-correction/the-longer-fix
+uint16_t redGamma[256];
+uint16_t greenGamma[256];
+uint16_t blueGamma[256];
+
+#define GAMMA_FACTOR 2.8
+#define GAMMA_MAX_IN 255
+#define GAMMA_RED_MAX_OUT 0xFFFF
+#define GAMMA_BLUE_MAX_OUT 0x4000
+#define GAMMA_GREEN_MAX_OUT 0x8000
+
+void buildGammaTable(float gammaFactor, uint16_t maxInput, uint16_t maxOutput, uint16_t* gamma) {
+    for(int i=0; i<=maxInput; i++) {
+        gamma[i] = (uint16_t) (pow((float)i / (float)maxInput, gammaFactor) * maxOutput + 0.5);
+        //printf("Max=%x, i=%d, val=%x\n\r", maxOutput, i, gamma[i]);
+    }
+}
+  
+void rebuildGammaTables(uint8_t amplitude) {
+    buildGammaTable (GAMMA_FACTOR, GAMMA_MAX_IN, (uint16_t)((((long) GAMMA_RED_MAX_OUT)*amplitude)/0xFFl), redGamma);
+    buildGammaTable (GAMMA_FACTOR, GAMMA_MAX_IN, (uint16_t)((((long) GAMMA_GREEN_MAX_OUT)*amplitude)/0xFFl), greenGamma);
+    buildGammaTable (GAMMA_FACTOR, GAMMA_MAX_IN, (uint16_t)((((long) GAMMA_BLUE_MAX_OUT)*amplitude)/0xFFl), blueGamma);
+}  
+
+TLC5955::TLC5955(PinName SCLK, PinName MOSI, PinName GSCLK,
+                 PinName XLAT, const int number) : number(number),
+                                                                                spi(MOSI, NC, SCLK),
+                                                                                gsclk(GSCLK),                                                                            
+                                                                                xlat(XLAT),                                                                                
+                                                                                newGSData(false),
+                                                                                newControlData(false),
+                                                                                need_xlat(false)
+{   
+    
+    rebuildGammaTables(0xFF); // oxFF is full amplitude
+    
+    for (int i=0; i<(SHORTS_PER_CHANNEL * CHANNELS_PER_IC * NUMBER_OF_ICS); i++) {
+        internalData[i] = 0xFFFF; // invert pwm outputs because transistor will get pulled low when on
+    }
+    for (int i=0; i<(SHORTS_PER_CHANNEL * CHANNELS_PER_IC * NUMBER_OF_ICS)+1; i++) {
+        gsBuffer[i] = 0xFFFF;
+    }
+    
+    
+    // Configure SPI to 16 bits and SPI_SPEED
+    spi.format(8, 0);
+    spi.frequency(SPI_SPEED);
+    
+    
+    
+    
+    // Set lat pin state
+    xlat = 0;
+    
+    // 62.5Hz should be fast enough
+    reset_ticker.attach_us(this, &TLC5955::reset, 16000);
+
+    // Outputs 8Mhz on pin 
+    init_clock(GSCLK);    
+}
+// https://github.com/FastLED/FastLED/wiki/FastLED-Color-Correction
+// Red LEDs are the dimmest, so no scaling
+// Blue LEDs are a bit brighter, so scale down by 0.9 (0xE6)
+// Green LED are by far the brightest, so scale down by 0.6 (0x99)
+void TLC5955::setChannel(int channelNum, unsigned short red, unsigned short green, unsigned short blue) {
+    if (red > 0xFF) red = 0xFF;
+    if (green > 0xFF) green = 0xFF;
+    if (blue > 0xFF) blue = 0xFF;
+    
+    if (channelNum < CHANNELS_PER_IC * NUMBER_OF_ICS) {        
+        // chip color values leave in b, r, g order, but that should map to r, g, b order
+
+        if (channelNum > 31) { 
+            channelNum -= 32;
+        } else if (channelNum < 16) {
+            channelNum += 32;        
+        }
+        
+        // mappings are slightly different for first eight and last eight of each board (red and green get inverted)
+        if (channelNum % 16 < 8) {
+            internalData[channelNum*SHORTS_PER_CHANNEL + 1] = (0xFFFF - GAMMA_BLUE_MAX_OUT) + (GAMMA_BLUE_MAX_OUT - blueGamma[blue]);  
+            internalData[channelNum*SHORTS_PER_CHANNEL + 0] = (0xFFFF - GAMMA_RED_MAX_OUT) + (GAMMA_RED_MAX_OUT - redGamma[red]); 
+            internalData[channelNum*SHORTS_PER_CHANNEL + 2] = (0xFFFF-GAMMA_GREEN_MAX_OUT) + (GAMMA_GREEN_MAX_OUT - greenGamma[green]); 
+        } else {
+            internalData[channelNum*SHORTS_PER_CHANNEL + 2] = (0xFFFF - GAMMA_BLUE_MAX_OUT) + (GAMMA_BLUE_MAX_OUT - blueGamma[blue]);  
+            internalData[channelNum*SHORTS_PER_CHANNEL + 1] = (0xFFFF - GAMMA_GREEN_MAX_OUT) +(GAMMA_GREEN_MAX_OUT - greenGamma[green]); 
+            internalData[channelNum*SHORTS_PER_CHANNEL + 0] = (0xFFFF - GAMMA_RED_MAX_OUT) + (GAMMA_RED_MAX_OUT - redGamma[red]); 
+        }
+    }
+}
+
+void TLC5955::latchData() {
+    newGSData = true;
+}
+
+void TLC5955::setNewControlData(unsigned short _globalBrightnessRed, unsigned short _globalBrightnessGreen, unsigned short _globalBrightnessBlue,
+                                led_power_t _maximumCurrentRed, led_power_t _maximumCurrentGreen, led_power_t _maximumCurrentBlue, 
+                                unsigned short* _dotCorrect) {
+    globalBrightnessRed = _globalBrightnessRed;
+    globalBrightnessGreen = _globalBrightnessGreen;
+    globalBrightnessBlue = _globalBrightnessBlue;
+    maximumCurrentRed = _maximumCurrentRed;
+    maximumCurrentGreen = _maximumCurrentGreen; 
+    maximumCurrentBlue = _maximumCurrentBlue;
+    dotCorrect = _dotCorrect;
+
+    // Tell reset function that new DC data has been given
+    newControlData = true;
+}
+
+// clock out the data over spi such that the MSB is the control bit (so 769 bits total)
+void TLC5955::clockOutData() {
+    // clock out buffer, where the first word contains 
+    // unused leading bits that will get clocked past the registers. 
+    
+    for (int i=0; i< (SHORTS_PER_CHANNEL * CHANNELS_PER_IC * NUMBER_OF_ICS) + 1; i++) {
+        spi.write(gsBuffer[i]>>8);
+        spi.write(gsBuffer[i]&0xFF);
+        //printf ("%d:%x:%x\n\r",i, gsBuffer[i]>>8, gsBuffer[i]&0xFF);
+    }
+}
+
+// Need a way to assemble bits in random offsets, since the 769 bit sequence doesn't divide evenly. 
+// So depending on how many TLC5955s are daisy chained together, there are an arbitrary number of extra bits.
+// Function that tracks next available bit location, and packs everything in is needed.
+
+
+inline void TLC5955::clearBit (unsigned short* value, int bitOffset) {
+    *value = *value & ~(1<<bitOffset);
+}
+
+inline void TLC5955::setBit (unsigned short* value, int bitOffset) {
+    *value = *value | (1<<bitOffset);
+}
+
+
+inline void TLC5955::packBit(unsigned int aBit) {
+    // current bit location divided by 16 to get the short
+    int shortOffset = currentBitLocation / 16;
+    
+    // current bit location mod 8 to get the bit to mask
+    int bitOffset = currentBitLocation % 16;
+    
+    if (aBit == 0) {
+        // clear
+        clearBit (&(gsBuffer[shortOffset]), 15 - bitOffset);
+    } else {
+        // set
+        setBit (&(gsBuffer[shortOffset]), 15 - bitOffset);
+    }
+    currentBitLocation++;    
+} 
+
+void TLC5955::packByte (unsigned int aByte) {
+    // call packBit for 8 bits, starting at the current bit location
+    for (int i = 7; i >= 0;i--) {  // MSB gets packed first
+        packBit( aByte & (1<<i));
+    }
+        
+}
+
+void TLC5955::packShort (unsigned int aShort) {
+    packByte( aShort >> 8); // MSB first
+    packByte( aShort & 0xFF);
+}
+
+void TLC5955::reset()
+{   
+    // Do we have new control data to send?
+    if (newControlData)
+    {
+       // printf ("Control:\n\r");
+
+        currentBitLocation = 0;
+        for (int i=0; i < 16 - NUMBER_OF_ICS;i++) {
+            packBit(0); // stuff leading byte
+        }
+        for (int icNum=0; icNum < NUMBER_OF_ICS; icNum++) {
+            // TODO: For daisy-chaining, this will need to consider the number of boards when calculating how many clocked out bits to offset
+            //packShort(1); // set LSB to one, will clock into LSB spot when spi serializes it to trigger control data load when latched
+            packBit(1); // set LSB to one
+            packByte(0x96); // 8 bit command decoder must be set to 0x96
+            
+            // 390 bits skipped, so could be anything
+            for (int i=0;i<389;i++) {
+                packBit(0);
+            }
+            
+            // FC 5 bits
+            packBit (0); // LED short circuit detection voltage - don't care for this application
+            packBit (0); // Use conventional PWM for this application; it's compatible with daisy chaining
+            packBit (0); // auto data refresh disabled
+            packBit (0); // display timing reset disabled
+            packBit (1); // auto display repeat mode ENABLED
+            
+            // BC, 21 bits
+            packBit (globalBrightnessRed & (1<<6));
+            packBit (globalBrightnessRed & (1<<5));
+            packBit (globalBrightnessRed & (1<<4));
+            packBit (globalBrightnessRed & (1<<3));
+            packBit (globalBrightnessRed & (1<<2));
+            packBit (globalBrightnessRed & (1<<1));
+            packBit (globalBrightnessRed & (1<<0));
+    
+            packBit (globalBrightnessGreen & (1<<6));
+            packBit (globalBrightnessGreen & (1<<5));
+            packBit (globalBrightnessGreen & (1<<4));
+            packBit (globalBrightnessGreen & (1<<3));
+            packBit (globalBrightnessGreen & (1<<2));
+            packBit (globalBrightnessGreen & (1<<1));
+            packBit (globalBrightnessGreen & (1<<0));
+    
+            packBit (globalBrightnessBlue & (1<<6));
+            packBit (globalBrightnessBlue & (1<<5));
+            packBit (globalBrightnessBlue & (1<<4));
+            packBit (globalBrightnessBlue & (1<<3));
+            packBit (globalBrightnessBlue & (1<<2));
+            packBit (globalBrightnessBlue & (1<<1));
+            packBit (globalBrightnessBlue & (1<<0));
+            
+            // MC, 9 bits
+            packBit (maximumCurrentBlue & (1<<2));
+            packBit (maximumCurrentBlue & (1<<1));
+            packBit (maximumCurrentBlue & (1<<0));
+    
+            packBit (maximumCurrentGreen & (1<<2));
+            packBit (maximumCurrentGreen & (1<<1));
+            packBit (maximumCurrentGreen & (1<<0));
+    
+            packBit (maximumCurrentRed & (1<<2));
+            packBit (maximumCurrentRed & (1<<1));
+            packBit (maximumCurrentRed & (1<<0));
+    
+            // DC 336 bits
+            // dot correct for each channel, starting with channel 48
+            for (int i=0; i<48; i++) {
+                packBit (dotCorrect[i] & (1<<6));
+                packBit (dotCorrect[i] & (1<<5));
+                packBit (dotCorrect[i] & (1<<4));
+                packBit (dotCorrect[i] & (1<<3));
+                packBit (dotCorrect[i] & (1<<2));
+                packBit (dotCorrect[i] & (1<<1));
+                packBit (dotCorrect[i] & (1<<0));        
+            }
+        }
+        clockOutData();
+        
+        // Latch
+        xlat = 0;
+        xlat = 1;
+        wait_us(10);
+        xlat = 0;
+        
+        
+        // No new data to send (we just sent it!)
+        newControlData = false;
+        
+    } else if (newGSData) {  // Do we have new GS data to send?
+        //printf ("Data:\n\r");
+ 
+        led2 = !led2;
+        
+        currentBitLocation = 0;
+        for (int i=0; i < 16 - NUMBER_OF_ICS;i++) {
+            packBit(1); // stuff leading byte
+        }
+        for (int icNum=0; icNum < NUMBER_OF_ICS; icNum++) {
+            // TODO: For daisy-chaining, this will need to consider the number of boards when calculating how many clocked out bits to offset
+            //packShort(0); // set LSB to zero, will clock into LSB spot when spi serializes it to trigger control data load when latched
+            packBit(0); // set LSB to zero
+            
+            // Send GS data backwards - this makes the GS_buffer[0] index correspond to OUT0 
+            for (int i = (SHORTS_PER_CHANNEL * CHANNELS_PER_IC) - 1; i >= 0; i--)
+            {
+                packShort(internalData[i + (icNum*SHORTS_PER_CHANNEL * CHANNELS_PER_IC)]);
+            }
+        }        
+        clockOutData();
+        
+        // Latch
+        xlat = 0;
+        xlat = 1;
+        wait_us(10);
+        xlat = 0;
+        
+        // No new data to send (we just sent it!)
+        newGSData = false;
+    }
+}
\ No newline at end of file