Published
Fork of TLC5940 by
Diff: TLC5955.cpp
- 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