Published
Fork of TLC5940 by
TLC5955.cpp
- Committer:
- roysandberg
- Date:
- 2018-06-09
- Revision:
- 4:ab6b451bbf40
File content as of revision 4:ab6b451bbf40:
#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; } }