Published
Fork of TLC5940 by
Revision 4:ab6b451bbf40, committed 2018-06-09
- Comitter:
- roysandberg
- Date:
- Sat Jun 09 23:22:13 2018 +0000
- Parent:
- 3:e5ed5650eb15
- Commit message:
- Published
Changed in this revision
diff -r e5ed5650eb15 -r ab6b451bbf40 FastPWM.lib --- a/FastPWM.lib Sun May 26 05:04:29 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://mbed.org/users/Sissors/code/FastPWM/#3094d3806cfc
diff -r e5ed5650eb15 -r ab6b451bbf40 TLC5940.cpp --- a/TLC5940.cpp Sun May 26 05:04:29 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,118 +0,0 @@ -#include "TLC5940.h" - -TLC5940::TLC5940(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, - PinName XLAT, PinName DCPRG, PinName VPRG, const int number) : number(number), - spi(MOSI, NC, SCLK), - gsclk(GSCLK), - blank(BLANK), - xlat(XLAT), - dcprg(DCPRG), - vprg(VPRG), - newGSData(false), - newDCData(false), - need_xlat(false) -{ - // Configure SPI to 12 bits and SPI_SPEED - spi.format(12, 0); - spi.frequency(SPI_SPEED); - - // Set output pin states - dcprg = 0; - vprg = 0; - xlat = 0; - blank = 1; - - // Call the reset function every 4096 PWM outputs - reset_ticker.attach_us(this, &TLC5940::reset, (1000000.0/GSCLK_SPEED) * 4096.0); - - // Configure FastPWM output for GSCLK frequency at 50% duty cycle - gsclk.period_us(1000000.0/(GSCLK_SPEED * 1.05)); - gsclk.write(.5); -} - -void TLC5940::setNewGSData(unsigned short* data) -{ - gsBuffer = data; - - // Tell reset function that new GS data has been given - newGSData = true; -} - -void TLC5940::setNewDCData(unsigned char* data) -{ - dcBuffer = data; - - // Tell reset function that new DC data has been given - newDCData = true; -} - -void TLC5940::reset() -{ - gsclk.write(0); - // Turn off LEDs - blank = 1; - - // Virtual function that allows the next data chunk to be set after every GSCLK cycle - // Useful for setting the next frame when multiplexing (e.g. LED matrices) - setNextData(); - - // Latch in data from previous cycle if needed - if (need_xlat) - { - // Latch - xlat = 1; - xlat = 0; - - // Don't need to latch again - need_xlat = false; - } - - // Reset the screen so that it is updating while data is being sent - blank = 0; - gsclk.write(.5); - - // Do we have new DC data to send? - if (newDCData) - { - // Set TLC5940 to accpet DC data - vprg = 1; - - // Get DC data from registers instead of EEPROM (since we are sending data to the registers now) - dcprg = 1; - - // Send DC data backwards - this makes the DC_buffer[0] index correspond to OUT0 - for (int i = (16 * number) - 1; i >= 0; i--) - { - // Assemble a 12 bit packet from two 6 bit chunks - spi.write(((dcBuffer[i] & 0x3F) << 6) | (dcBuffer[i-1] & 0x3F)); - i--; - } - - // Latch - xlat = 1; - xlat = 0; - - // No new data to send (we just sent it!) - newDCData = false; - } - - // Do we have new GS data to send? - if (newGSData) - { - // Set TLC5940 to accept GS data - vprg = 0; - - // Send GS data backwards - this makes the GS_buffer[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(gsBuffer[i] & 0xFFF); - } - - // Latch after current GS data is done being displayed - need_xlat = true; - - // No new data to send (we just sent it!) - newGSData = false; - } -} \ No newline at end of file
diff -r e5ed5650eb15 -r ab6b451bbf40 TLC5940.h --- a/TLC5940.h Sun May 26 05:04:29 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,242 +0,0 @@ -#ifndef TLC5940_H -#define TLC5940_H - -#include "FastPWM.h" - /* - - ASCII art cat(why not?): - /\_/\ - ____/ o o \ - /~____ =ø= / - (______)__m_m) - - */ - -/** - * SPI speed used by the mbed to communicate with the TLC5940 - * The TLC5940 supports up to 30Mhz. This should be kept as high - * as possible to ensure that data has time to be sent each reset cycle. - */ -#define SPI_SPEED 30000000 - -/** - * The rate at which the GSCLK pin is pulsed - * This also controls how often the reset function is called - * The rate at which the reset function is called can be calculated by: (1/GSCLK_SPEED) * 4096 - * The maximum reliable rate is around ~32Mhz. I reccomend keeping this as low as possible because - * a higher rate will use more CPU. Also, this must be low enough to give time for sending new data - * before the completion of a GSCLK cycle (4096 pulses). If you are daisy chaining multiple TLC5940s, - * divide 32Mhz by the number of chips to get a good maximum rate. - */ -#define GSCLK_SPEED 2500000 - -/** - * This class controls a TLC5940 PWM driver IC. - * It supports sending dot correction and grayscale data. However, it does not support error checking or writing the EEPROM. - * This class uses the FastPWM library by Erik Olieman to continuously pulse the GSLCK pin without CPU intervention. After - * 4096 pulses, the private member funciton reset is called by the ticker. It resets the display by pulsing the BLANK pin. If new - * data has been set to be sent by the functions setNewGSData or setNewDCData, it is sent here. The definition GSCLK_SPEED in TLC5940.h - * controls how often this function is called. A higher GSCLK_SPEED will increase the rate at which the screen is updated but also increase - * CPU time spent in that function. The default value is 1Mhz. The rate at which the reset function is called can be calculated by: - * (1/GSCLK_SPEED) * 4096. - * - * Using the TLC5940 class to control an LED: - * @code - * #include "mbed.h" - * #include "TLC5940.h" - * - * // Create the TLC5940 instance - * TLC5940 tlc(p7, p5, p21, p9, p10, p11, p12, 1); - * - * int main() - * { - * // Create a buffer to store the data to be sent - * unsigned short GSData[16] = { 0x0000 }; - * - * // Enable the first LED - * GSData[0] = 0xFFF; - * - * // Set the new data - * tlc.setNewGSData(GSData); - * - * while(1) - * { - * - * } - * } - * @endcode - */ -class TLC5940 -{ -public: - /** - * Set up the TLC5940 - * @param SCLK - The SCK pin of the SPI bus - * @param MOSI - The MOSI pin of the SPI bus - * @param GSCLK - The GSCLK pin of the TLC5940(s) - * @param BLANK - The BLANK pin of the TLC5940(s) - * @param XLAT - The XLAT pin of the TLC5940(s) - * @param DCPRG - The DCPRG pin of the TLC5940(s) - * @param VPRG - The VPRG pin of the TLC5940(s) - * @param number - The number of TLC5940s (if you are daisy chaining) - */ - TLC5940(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, - PinName XLAT, PinName DCPRG, PinName VPRG, const int number = 1); - - /** - * Set the next chunk of grayscale data to be sent - * @param data - Array of 16 bit shorts containing 16 12 bit grayscale data chunks per TLC5940 - * @note These must be in intervals of at least (1/GSCLK_SPEED) * 4096 to be sent - */ - void setNewGSData(unsigned short* data); - - /** - * Set the next chunk of dot correction data to be sent - * @param data - Array of 8 bit chars containing 16 6 bit dot correction data chunks per TLC5940 - * @note These must be in intervals of at least (1/GSCLK_SPEED) * 4096 to be sent. Also, this function is optional. If you do not - * use it, then the TLC5940 will use the EEPROM, which (by default) conatins the data 0x3F. - */ - void setNewDCData(unsigned char* data); - -protected: - /** - * Set the next chunk of grayscale data to be sent while in the current reset cycle - * @note This is useful to send the next set of data right after the first is finished being displayed. - * The primary purpose for this is multiplexing, although it could be used for anything else. - */ - virtual void setNextData() {} - - - // Number of TLC5940s in series - const int number; - -private: - // SPI port - only MOSI and SCK are used - SPI spi; - - // PWM output using the FastPWM library by Erik Olieman - FastPWM gsclk; - - // Digital out pins used for the TLC5940 - DigitalOut blank; - DigitalOut xlat; - DigitalOut dcprg; - DigitalOut vprg; - - // Call a reset function to manage sending data and GSCLK updating - Ticker reset_ticker; - - // Has new GS/DC data been loaded? - volatile bool newGSData; - volatile bool newDCData; - - // Do we need to send an XLAT pulse? (Was GS data clocked in last reset?) - volatile bool need_xlat; - - // Buffers to store data until it is sent - unsigned short* gsBuffer; - unsigned char* dcBuffer; - - // Function to reset the display and send the next chunks of data - void reset(); -}; - - -/** - * This class allows a TLC5940 to be multiplexed. - * It inherits the TLC5940 class and uses it to control the TLC5940 driver(s). It does not support sending dot corection data. - * This class sets the new grayscale data every iteration of the GSCLK reset loop. It then updates the current row using the - * user defined function SetRows. The framerate you will recieve using this function can be calculate by: 1 / (((1/GSCLK_SPEED) * 4096) * rows). - * I reccomend maintaining a framerate above 30fps. However, keep in mind that as your framerate increases, so does your CPU usage. - * - * Using the TLC5940Mux class to control an 8x8 LED matrix: - * @code - * #include "mbed.h" - * #include "TLC5940.h" - * - * // Bus connecting to the rows of the LED matrix through PNP transistors - * BusOut rows(p22, p23, p24, p25, p26, p27, p28, p29); - * - * // Function to update the rows using the BusOut class - * void SetRows(int nextRow) - * { - * // I am using PNP transistors, so inversion is necessary - * rows = ~(1 << nextRow); - * } - * - * // Create the TLC5940Mux instance - * TLC5940Mux tlc(p7, p5, p21, p9, p10, p11, p12, 1, 8, &SetRows); - * - * int main() - * { - * tlc[0][0] = 0xFFF; // Turn on the top left LED - * while(1) - * { - * - * } - * } - * @endcode - */ -class TLC5940Mux : private TLC5940 -{ -public: - /** - * Set up the TLC5940 - * @param SCLK - The SCK pin of the SPI bus - * @param MOSI - The MOSI pin of the SPI bus - * @param GSCLK - The GSCLK pin of the TLC5940(s) - * @param BLANK - The BLANK pin of the TLC5940(s) - * @param XLAT - The XLAT pin of the TLC5940(s) - * @param DCPRG - The DCPRG pin of the TLC5940(s) - * @param VPRG - The VPRG pin of the TLC5940(s) - * @param number - The number of TLC5940s (if you are daisy chaining) - * @param rows - The number of rows you are multiplexing - * @param SetRows - The function pointer to your function that sets the current row. - * @note The SetRows function allows you to set exactly how you want your rows - * to be updated. The TLC5940Mux class calls this function with an argument of int that contains the number of the row to - * be turned on. If the TLC5940Mux class needs the first row to be turned on, the int will be 0. - */ - TLC5940Mux(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, - PinName XLAT, PinName DCPRG, PinName VPRG, const int number, - const int rows, void (*SetRows)(int)); - - // Destructor used to delete memory - ~TLC5940Mux(); - - /** - * Set the contents of the buffer that contains the multiplexed data - * @param data - The data to set to the buffer containing 16 12 bit grayscale data chunks per TLC5940 - * @returns The data provided - */ - unsigned short* operator=(unsigned short* data); - - /** - * Get a pointer to one of the rows of the multiplexed data - * @param index - The row that you would like the contents of - * @returns A pointer to the data containing the requested row containing 16 12 bit grayscale data chunks per TLC5940 - * @note This operator can also be used to change or get the value of an individual LED. - * For example: - * @code - * TLC5940Mux[0][0] = 0xFFF; - * @endcode - */ - unsigned short* operator[](int index); - -private: - // Virtual function overriden from TLC5940 class - virtual void setNextData(); - - // Number of rows - const int rows; - - // Function to set the current row - void (*SetRows)(int); - - // The current row - volatile int currentIndex; - - // Buffer containing data to be sent during each frame - unsigned short* dataBuffer; -}; - -#endif \ No newline at end of file
diff -r e5ed5650eb15 -r ab6b451bbf40 TLC5940Mux.cpp --- a/TLC5940Mux.cpp Sun May 26 05:04:29 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -#include "mbed.h" -#include "TLC5940.h" - - -TLC5940Mux::TLC5940Mux(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, - PinName XLAT, PinName DCPRG, PinName VPRG, const int number, - const int rows, void (*SetRows)(int)) : TLC5940(SCLK, MOSI, GSCLK, BLANK, XLAT, DCPRG, VPRG, number), - rows(rows), - SetRows(SetRows), - currentIndex(0) - -{ - // Create a data buffer to store the current LED states - dataBuffer = new unsigned short[rows * 16 * number]; - - // Zero the buffer - memset(dataBuffer, 0x00, rows * 16 * number * 2); -} - -TLC5940Mux::~TLC5940Mux() -{ - // Delete the buffer - delete[] dataBuffer; -} - -unsigned short* TLC5940Mux::operator=(unsigned short* data) -{ - // Copy the memory from data to the data buffer - memcpy(dataBuffer, data, rows * 16 * number * 2); - - return data; -} - -unsigned short* TLC5940Mux::operator[](int index) -{ - // Return the start of the correct data chunk - return dataBuffer + (index * 16 * number); -} - -void TLC5940Mux::setNextData() -{ - // Move into the dataBuffer and return a pointer corresponding to the start of the correct data block - setNewGSData(dataBuffer + (currentIndex * 16 * number)); - - // Since the data we sent on the previous reset was just latched in, - // we must enable the row for the previous index so it is displayed in the right position - // The ternary is used in case index is zero (we can't have an index of -1) - SetRows((currentIndex ? (currentIndex - 1) : (rows - 1))); - - // Go to next index - if (currentIndex == (rows - 1)) - currentIndex = 0; - else - currentIndex++; -} \ No newline at end of file
diff -r e5ed5650eb15 -r ab6b451bbf40 TLC5955.cpp --- /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
diff -r e5ed5650eb15 -r ab6b451bbf40 TLC5955.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TLC5955.h Sat Jun 09 23:22:13 2018 +0000 @@ -0,0 +1,241 @@ +#ifndef TLC5955_H +#define TLC5955_H + +//#include "FastPWM.h" +#include "mbed.h" + +#define SHORTS_PER_CHANNEL 3 +#define CHANNELS_PER_IC 16 +#define NUMBER_OF_ICS 3 + +#define TRUE 1 +#define FALSE 0 + +// slow this down 10x for testing +//#define SEQUENCER_RATE 0.5f +//#define SEQUENCER_RATE 0.025f +#define SEQUENCER_RATE 0.030f + +/** + * SPI speed used by the mbed to communicate with the TLC5955 + * The TLC5955 supports up to 30Mhz. This should be kept as high + * as possible to ensure that data has time to be sent each reset cycle. + */ + // 4Mhz is fastest supported rate on nRF51 +#define SPI_SPEED 4000000 + +void rebuildGammaTables(uint8_t amplitude); + +/** + * This class controls a TLC5955 PWM driver IC. + * It supports sending dot correction and grayscale data. However, it does not support error checking or writing the EEPROM. + * After + * 4096 pulses, the private member funciton reset is called by the ticker. It resets the display by pulsing the BLANK pin. If new + * data has been set to be sent by the functions setNewGSData or setNewDCData, it is sent here. The definition GSCLK_SPEED in TLC5955.h + * controls how often this function is called. A higher GSCLK_SPEED will increase the rate at which the screen is updated but also increase + * CPU time spent in that function. The default value is 1Mhz. The rate at which the reset function is called can be calculated by: + * (1/GSCLK_SPEED) * 4096. + */ +class TLC5955 +{ +public: + + typedef enum + { + I_3_2_MA = 0, + I_8_0_MA = 1, + I_11_2_MA = 2, + I_15_9_MA = 3, + I_19_1_MA = 4, + I_23_9_MA = 5, + I_27_1_MA = 6, + I_31_9_MA = 7 + } led_power_t; + + /** + * Set up the TLC5955 + * @param SCLK - The SCK pin of the SPI bus + * @param MOSI - The MOSI pin of the SPI bus + * @param GSCLK - The GSCLK pin of the TLC5955(s) + * @param BLANK - The BLANK pin of the TLC5955(s) <- REMOVE + * @param XLAT - The XLAT pin of the TLC5955(s) + * @param DCPRG - The DCPRG pin of the TLC5955(s) <- REMOVE + * @param VPRG - The VPRG pin of the TLC5955(s) <- REMOVE + * @param number - The number of TLC5955s (if you are daisy chaining) + */ + TLC5955(PinName SCLK, PinName MOSI, PinName GSCLK, + PinName XLAT, const int number = 1); + + void setChannel(int channelNum, unsigned short red, unsigned short green, unsigned short blue); + void latchData(); + + + /** + * Set the next chunk of grayscale data to be sent + * @param data - Array of 16 bit shorts containing 16 12 bit grayscale data chunks per TLC5955 + * @note These must be in intervals of at least (1/GSCLK_SPEED) * 4096 to be sent + */ + void setNewGSData(unsigned short* data); + + /** + * Set the next chunk of dot correction data to be sent + * @param data - Array of 8 bit chars containing 16 6 bit dot correction data chunks per TLC5955 + * @note These must be in intervals of at least (1/GSCLK_SPEED) * 4096 to be sent. Also, this function is optional. If you do not + * use it, then the TLC5955 will use the EEPROM, which (by default) conatins the data 0x3F. + */ +void 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); + void clockOutData(); + void clearBit (unsigned short* value, int bitOffset); + void setBit (unsigned short* value, int bitOffset); + void packBit(unsigned int aBit); + void packByte (unsigned int aByte); + void packShort (unsigned int aShort); +protected: + /** + * Set the next chunk of grayscale data to be sent while in the current reset cycle + * @note This is useful to send the next set of data right after the first is finished being displayed. + * The primary purpose for this is multiplexing, although it could be used for anything else. + */ + virtual void setNextData() {} + + + // Number of TLC5955s in series + const int number; + +private: + + // SPI port - only MOSI and SCK are used + SPI spi; + + // PWM output + DigitalOut gsclk; + + // Digital out pins used for the TLC5955 + DigitalOut xlat; + + // Call a reset function to manage sending data and GSCLK updating + Ticker reset_ticker; + + int currentBitLocation; + + // Has new GS/Control data been loaded? + volatile bool newGSData; + volatile bool newControlData; + + // Do we need to send an XLAT pulse? (Was GS data clocked in last reset?) + volatile bool need_xlat; + + // Buffer to store data until it is sent + unsigned short gsBuffer[(SHORTS_PER_CHANNEL * CHANNELS_PER_IC * NUMBER_OF_ICS) + 1]; // one extra word to fit the MSB control bit. Extra bits will be clocked out. + + unsigned short internalData[SHORTS_PER_CHANNEL * CHANNELS_PER_IC * NUMBER_OF_ICS]; // internal data storage for use by per-channel setting function + unsigned short globalBrightnessRed; + unsigned short globalBrightnessGreen; + unsigned short globalBrightnessBlue; + unsigned short maximumCurrentRed; + unsigned short maximumCurrentGreen; + unsigned short maximumCurrentBlue; + unsigned short* dotCorrect; + + // Function to reset the display and send the next chunks of data + void reset(); +}; + + +/** + * This class allows a TLC5955 to be multiplexed. + * It inherits the TLC5955 class and uses it to control the TLC5955 driver(s). It does not support sending dot corection data. + * This class sets the new grayscale data every iteration of the GSCLK reset loop. It then updates the current row using the + * user defined function SetRows. The framerate you will recieve using this function can be calculate by: 1 / (((1/GSCLK_SPEED) * 4096) * rows). + * I reccomend maintaining a framerate above 30fps. However, keep in mind that as your framerate increases, so does your CPU usage. + * + * Using the TLC5955Mux class to control an 8x8 LED matrix: + * @code + * #include "mbed.h" + * #include "TLC5955.h" + * + * // Bus connecting to the rows of the LED matrix through PNP transistors + * BusOut rows(p22, p23, p24, p25, p26, p27, p28, p29); + * + * // Function to update the rows using the BusOut class + * void SetRows(int nextRow) + * { + * // I am using PNP transistors, so inversion is necessary + * rows = ~(1 << nextRow); + * } + * + * // Create the TLC5955Mux instance + * TLC5955Mux tlc(p7, p5, p21, p9, p10, p11, p12, 1, 8, &SetRows); + * + * int main() + * { + * tlc[0][0] = 0xFFF; // Turn on the top left LED + * while(1) + * { + * + * } + * } + * @endcode + */ +class TLC5955Mux : private TLC5955 +{ +public: + /** + * Set up the TLC5955 + * @param SCLK - The SCK pin of the SPI bus + * @param MOSI - The MOSI pin of the SPI bus + * @param GSCLK - The GSCLK pin of the TLC5955(s) + * @param BLANK - The BLANK pin of the TLC5955(s) + * @param XLAT - The XLAT pin of the TLC5955(s) + * @param DCPRG - The DCPRG pin of the TLC5955(s) + * @param VPRG - The VPRG pin of the TLC5955(s) + * @param number - The number of TLC5955s (if you are daisy chaining) + * @param rows - The number of rows you are multiplexing + * @param SetRows - The function pointer to your function that sets the current row. + * @note The SetRows function allows you to set exactly how you want your rows + * to be updated. The TLC5955Mux class calls this function with an argument of int that contains the number of the row to + * be turned on. If the TLC5955Mux class needs the first row to be turned on, the int will be 0. + */ + TLC5955Mux(PinName SCLK, PinName MOSI, PinName GSCLK, + PinName XLAT, const int number, + const int rows, void (*SetRows)(int)); + + // Destructor used to delete memory + ~TLC5955Mux(); + + /** + * Set the contents of the buffer that contains the multiplexed data + * @param data - The data to set to the buffer containing 16 12 bit grayscale data chunks per TLC5955 + * @returns The data provided + */ + unsigned short* operator=(unsigned short* data); + + /** + * Get a pointer to one of the rows of the multiplexed data + * @param index - The row that you would like the contents of + * @returns A pointer to the data containing the requested row containing 16 12 bit grayscale data chunks per TLC5955 + * @note This operator can also be used to change or get the value of an individual LED. + * For example: + * @code + * TLC5955Mux[0][0] = 0xFFF; + * @endcode + */ + unsigned short* operator[](int index); + +private: + // Virtual function overriden from TLC5955 class + virtual void setNextData(); + + // Number of rows + const int rows; + + // Function to set the current row + void (*SetRows)(int); + + // The current row + volatile int currentIndex; +}; + +#endif \ No newline at end of file