Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "teensy_tlc5940.h"
- ////////////////////////////////////////////////////////////////////////////////
- /// Internal Forward Declarations
- static inline void startGSCLK ();
- static inline void stopGSCLK ();
- static inline void resetGSCLK ();
- static inline void sleepGSCLK ();
- static inline void wakeGSCLK ();
- static inline void startBLANKTimer ();
- static inline void stopBLANKTimer ();
- static inline void sleepBLANKTimer ();
- static inline void wakeBLANKTimer ();
- static inline void startGSCLKandBLANKTimers ();
- #define SHIFT_LATCH_PIN 2
- #define SHIFT_CLK_PIN 7
- #define SHIFT_DATA_PIN 8
- #define SHIFT_OE_PIN 6
- ////////////////////////////////////////////////////////////////////////////////
- /// GSCLK Timer
- ///
- /// The grayscale clock is used to clock the PWM outputs of the TLC5940. These
- /// are 12-bit, so one PWM cycle takes 4096 GSCLK ticks. Blanking the output to
- /// latch in new data should only be done at the boundaries between PWM cycles
- /// for the smoothest looking output, so we use two counters:
- ///
- /// 1) GSCLK running at 2.0 MHz, will output a 50% duty cycle. The PWM cycle
- /// will then be ~488 Hz.
- ///
- /// 2) The BLANK timer will generate an interrupt every 4096 cycles of GSCLK.
- ///
- #define GSCLK_PRESCALE 0 // divide F_BUS by 1 = 48 MHz
- void initGSCLKTimer ()
- {// F_BUS = 48 MHz
- //
- // GSCLK will run at 2.0 MHz. Since the TLC5940 has 4096 GSCLK pulses per
- // PWM cycle, this means a PWM frequency of ~488 Hz.
- // Connect Teensy pin 9 to FTM0, Ch. 2 output:
- CORE_PIN9_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE;
- stopGSCLK(); // disable while performing setup
- FTM0_CNT = 0;
- FTM0_MOD = 23; // 48 MHz/24 = 2 MHz (period is FTM1_MOD + 1)
- // Edge-Aligned PWM, set output on FTM0, ch.2 match:
- FTM0_C2SC = 0x24; // MSnB:MSnA = 10, ELSnB:ELSnA = 01
- FTM0_C2V = 12; // 50% duty cycle
- FTM0_SC = GSCLK_PRESCALE;
- return;}
- static inline void startGSCLK ()
- {FTM0_CNT = 0;
- FTM0_SC = FTM_SC_CLKS(1) | GSCLK_PRESCALE;
- return;}
- static inline void stopGSCLK ()
- {FTM0_SC = 0;
- return;}
- static inline void resetGSCLK ()
- {FTM0_CNT = 0;
- return;}
- static inline void sleepGSCLK ()
- {stopGSCLK();
- SIM_SCGC6 &= ~SIM_SCGC6_FTM0;
- return;}
- static inline void wakeGSCLK ()
- {SIM_SCGC6 |= SIM_SCGC6_FTM0;
- return;}
- ////////////////////////////////////////////////////////////////////////////////
- /// BLANK Timer and output pin
- ///
- /// The BLANK signal should go high after 4096 GSCLK pulses.
- ///
- /// We'll use a Periodic Interrupt Timer for this. I'm hoping that it's
- /// decently in sync with the Flex Timers since they are both driven from
- /// the same peripheral bus clock.
- ///
- /// The PITs run directly off the peripheral bus clock. On a normally clocked
- /// Teensy 3, F_BUS is 48 MHz.
- ///
- void initBLANKTimer ()
- {SIM_SCGC6 |= SIM_SCGC6_PIT; // enable PIT peripheral at system level
- PIT_MCR = 1; // disable PIT while performing setup
- PIT_TCTRL3 = 0; // disable PIT #3 while performing setup
- PIT_LDVAL3 = 98304; // FTM1 period * 4096
- NVIC_ENABLE_IRQ(IRQ_PIT_CH3); // allow PIT #3 interrupts
- PIT_MCR = 0; // enable PIT
- return;}
- static inline void startBLANKTimer ()
- {PIT_TFLG3 = 1; // ensure that the interrupt is cleared
- PIT_TCTRL3 = 3; // enables PIT #3 and its interrupt
- return;}
- static inline void stopBLANKTimer ()
- {PIT_TCTRL3 = 0; // disables PIT #3 and its interrupt
- return;}
- static inline void sleepBLANKTimer ()
- {
- stopBLANKTimer();
- PIT_MCR = 1;
- SIM_SCGC6 &= ~SIM_SCGC6_PIT;
- return;}
- static inline void wakeBLANKTimer ()
- {
- PIT_MCR = 0;
- SIM_SCGC6 |= SIM_SCGC6_PIT;
- return;}
- // The following pin outputs the BLANK signal:
- #define BLANK_PIN 10
- void initBLANKPin ()
- {pinMode(BLANK_PIN, OUTPUT);
- digitalWrite(BLANK_PIN, HIGH); // start with BLANK active
- return;}
- static inline void setBLANK ()
- {CORE_PIN10_PORTSET = CORE_PIN10_BITMASK;
- return;}
- static inline void clearBLANK ()
- {CORE_PIN10_PORTCLEAR = CORE_PIN10_BITMASK;
- return;}
- ////////////////////////////////////////////////////////////////////////////////
- /// XLAT
- ///
- /// Causes new data sent to the TLC5940 to be latched in.
- ///
- #define XLAT_PIN 12
- volatile boolean tlc_needXLAT = false;
- volatile boolean tlc_needExtraSCLKPulseAfterGSData = false;
- void initXLAT ()
- {pinMode(XLAT_PIN, OUTPUT);
- digitalWrite(XLAT_PIN, LOW);
- return;}
- static inline void pulseXLAT ()
- {CORE_PIN12_PORTSET = CORE_PIN12_BITMASK;
- // Don't need a delay here. This code takes longer than the minimum hold
- // time needed for TLC5940 XLAT.
- CORE_PIN12_PORTCLEAR = CORE_PIN12_BITMASK;
- return;}
- ////////////////////////////////////////////////////////////////////////////////
- /// VPRG
- ///
- /// VRPG should be low to send grayscale data and high to send dot-correction
- /// data.
- ///
- #define VPRG_PIN 14
- void initVPRG ()
- {pinMode(VPRG_PIN, OUTPUT);
- digitalWrite(VPRG_PIN, LOW);
- return;}
- ////////////////////////////////////////////////////////////////////////////////
- /// XERR
- ///
- /// This pin receives data from the TLC5940 describing problems.
- ///
- /// We're not currently using this. I might not even connect it on the circuit
- /// board. Need to decide.
- ///
- //#define XERR_PIN 7
- //
- //void initXERR ()
- // {pinMode(XERR_PIN, INPUT);
- // return;}
- ////////////////////////////////////////////////////////////////////////////////
- /// TLC5940 SPI Communications
- ///
- /// SCLK = Teensy pin 13
- /// TLC5940 SIN = Teensy pin 11
- ///
- /// We'll use a baud rate prescaler (PBR) of 2 and a baud rate scaler (BR) of 2
- /// which results in 12 MHz data transfer rate. PBR 0 = prescaler 2; BR 0 =
- /// scaler 2.
- ///
- #define SPI_SR_TXCTR_MASK 0x0000F000
- #define SPI_FIFO_DEPTH 4
- void setupTLC5940_SPI ()
- {SIM_SCGC6 |= SIM_SCGC6_SPI0; // enable SPI peripheral at system level
- CORE_PIN11_CONFIG = PORT_PCR_MUX(2) | PORT_PCR_DSE;
- CORE_PIN13_CONFIG = PORT_PCR_MUX(2) | PORT_PCR_DSE;
- SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_MDIS | SPI_MCR_HALT; // stop while configuring
- SPI0_CTAR0 = SPI_CTAR_FMSZ(11) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(0); // 12-bit
- SPI0_CTAR1 = SPI_CTAR_FMSZ( 5) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(0); // 6-bit
- SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_DOZE | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF;
- return;}
- static inline void sleepSPI ()
- {SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_MDIS | SPI_MCR_HALT;
- SIM_SCGC6 &= ~SIM_SCGC6_SPI0;
- return;}
- static inline void wakeSPI ()
- {SIM_SCGC6 |= SIM_SCGC6_SPI0;
- SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_DOZE | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF;
- return;}
- void pulseSPI_SCLK ()
- // After latching in GS data, the TLC5940 requires another SCLK pulse before
- // more GS data can be sent. (It triggers the sending off status data on MISO.)
- // So after an XLAT, we need to manually pulse the SCLK once.
- {CORE_PIN13_CONFIG = PORT_PCR_MUX(1) | PORT_PCR_DSE;
- CORE_PIN13_PORTSET = CORE_PIN13_BITMASK;
- // Don't need a delay here. This code takes longer than the minimum hold
- // time needed for SCLK.
- CORE_PIN13_PORTCLEAR = CORE_PIN13_BITMASK;
- CORE_PIN13_CONFIG = PORT_PCR_MUX(2) | PORT_PCR_DSE;
- return;}
- static inline void waitForSPITransmitFinish ()
- {while (!(SPI0_SR & SPI_SR_EOQF));}
- static inline void waitForSPIFIFOSpace ()
- {while (((SPI0_SR & SPI_SR_TXCTR_MASK) >> 12) >= SPI_FIFO_DEPTH);}
- void sendSPIData12Bit (uint16_t* data, int n)
- {
- SPI0_SR = SPI_SR_TCF; // reset transfer complete flag
- for (int j = n - 1; j >= 0; j--)
- {waitForSPIFIFOSpace();
- if (j == 0)
- {SPI0_PUSHR = SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ | data[j];}
- else
- {SPI0_PUSHR = SPI_PUSHR_CTAS(0) | data[j];}}
- waitForSPITransmitFinish();
- SPI0_SR = SPI_SR_EOQF; // Re-enable SPI running state
- return;}
- void sendSPIData6Bit (uint8_t* data, int n)
- {SPI0_SR = SPI_SR_TCF; // reset transfer complete flag
- for (int j = n - 1; j >= 0; j--)
- {waitForSPIFIFOSpace();
- if (j == 0)
- {SPI0_PUSHR = SPI_PUSHR_CTAS(1) | SPI_PUSHR_EOQ | data[j];}
- else
- {SPI0_PUSHR = SPI_PUSHR_CTAS(1) | data[j];}}
- waitForSPITransmitFinish();
- SPI0_SR = SPI_SR_EOQF; // Re-enable SPI running state
- return;}
- ////////////////////////////////////////////////////////////////////////////////
- /// Grayscale Data
- ///
- /// tlc_GSData[] is an array of bytes, each one representing the grayscale
- /// value for one of the TLC5940's output channels. tlc_GSData[0] is the value
- /// for OUT0 of the first TLC, tlc_GSData[15] is the value for OUT15 of the
- /// first TLC5940, tlc_GSData[16] is for OUT0 of the second TLC5940, etc.
- ///
- /// Valid grayscale values range from 0 through 4095 (i.e. they are 12-bit).
- ///
- uint16_t tlc_GSData[NUM_TLCS * 16];
- boolean tlc_GSDataChangedSinceLastSend = true;
- void setGSData(int tlcOutput, uint16_t value)
- // Sets a value in the local tlc_GSData array which will be used as the
- // grayscale value for the specified TLC5940 output the next time sendGSData
- // is called. OUT0 of the first TLC is tlcOutput = 0, OUT0 of the next TLC
- // is tlcOutput = 16, and so on. 4095 is the maximum allowed grayscale value.
- {tlc_GSData[tlcOutput] = value;
- tlc_GSDataChangedSinceLastSend = true;
- return;}
- uint16_t getGSData(int tlcOutput)
- // Gets a value from the local tlc_GSData array.
- {return(tlc_GSData[tlcOutput]);}
- void setAllGSData(uint16_t value)
- // Sets all entries in the local tlc_GSData array to the specified value.
- {for (int j = 0; j < NUM_TLCS * 16; j++)
- {tlc_GSData[j] = value;}
- tlc_GSDataChangedSinceLastSend = true;
- return;}
- void sendGSData (byte leds)
- {
- if (tlc_GSDataChangedSinceLastSend)
- {// If we get here and the last XLAT request hasn't been handled yet, set
- // tlc_needXLAT back to false so we can send the latest data before latching
- // it in.
- tlc_needXLAT = false;
- digitalWrite(VPRG_PIN, LOW); // VPRG low says we're setting the GS register
- sendSPIData12Bit(tlc_GSData, NUM_TLCS * 16);
- //delay(1);
- digitalWrite(SHIFT_LATCH_PIN, LOW);
- shiftOut(SHIFT_DATA_PIN, SHIFT_CLK_PIN, LSBFIRST, leds);
- digitalWrite(SHIFT_LATCH_PIN, HIGH);
- tlc_GSDataChangedSinceLastSend = false;
- tlc_needXLAT = true;
- digitalWrite(SHIFT_OE_PIN,LOW);
- while(tlc_needXLAT);
- digitalWrite(SHIFT_OE_PIN,HIGH);
- }
- return;}
- extern boolean unlatchedGSData()
- // Returns true iff GSData was sent but has not yet been latched (which happens
- // during the BLANK interval.
- {return(tlc_needXLAT);}
- ////////////////////////////////////////////////////////////////////////////////
- /// Dot Correction
- ///
- /// tlc_DCData[] is an array of bytes, each one representing the dot-correction
- /// value for one of the TLC5940's output channels. tlc_DCData[0] is the value
- /// for OUT0 of the first TLC, tlc_DCData[15] is the value for OUT15 of the
- /// first TLC5940, tlc_DCData[16] is for OUT0 of the second TLC5940, etc.
- ///
- /// Valid dot-correction values range from 0 through 63 (i.e. they are 6-bit).
- ///
- uint8_t tlc_DCData[NUM_TLCS * 16] = INITIAL_DCDATA;
- void setDCData(int tlcOutput, uint8_t value)
- // Sets a value in the local tlc_DCData array which will be used as the
- // dot-correction value for the specified TLC5940 output the next time sendDCData
- // is called. OUT0 of the first TLC is tlcOutput = 0, OUT0 of the next TLC
- // is tlcOutput = 16, and so on. 63 is the maximum allowed value.
- {tlc_DCData[tlcOutput] = value;
- return;}
- uint8_t getDCData(int tlcOutput)
- // Gets a value from the local tlc_DCData array.
- {return(tlc_DCData[tlcOutput]);}
- void sendDCData ()
- // There are four 6-bit DC data values packed into every three bytes.
- {while (tlc_needXLAT); // wait for pending GSData to be latched in
- digitalWrite(VPRG_PIN, HIGH); // VPRG high says we're setting the DC register
- sendSPIData6Bit(tlc_DCData, NUM_TLCS * 16);
- // Sending DC data can be done without blanking the output. Just latch it
- // in and magic happens.
- pulseXLAT();
- tlc_needExtraSCLKPulseAfterGSData = true;
- return;}
- ////////////////////////////////////////////////////////////////////////////////
- /// TLC5940 overall
- void initTLC5940 ()
- {setAllGSData(0);
- initBLANKPin();
- initXLAT();
- initVPRG();
- // initXERR(); // *** only do this if I actually connect the pin on the PCB
- setupTLC5940_SPI();
- sendDCData();
- initGSCLKTimer();
- initBLANKTimer();
- clearBLANK();
- startGSCLKandBLANKTimers();
- sendGSData(0);
- return;}
- static inline void startGSCLKandBLANKTimers ()
- {startBLANKTimer();
- startGSCLK();
- return;}
- void sleepTLC5940 ()
- {
- sleepGSCLK();
- stopGSCLK();
- sleepBLANKTimer();
- stopBLANKTimer();
- sleepSPI();
- digitalWrite(XLAT_PIN, LOW);
- digitalWrite(VPRG_PIN, LOW);
- clearBLANK();
- return;}
- void wakeTLC5940 ()
- {initBLANKPin();
- initXLAT();
- initVPRG();
- wakeSPI();
- clearBLANK();
- wakeGSCLK();
- wakeBLANKTimer();
- startBLANKTimer();
- //startGSCLKandBLANKTimers();
- return;}
- void pit3_isr ()
- // BLANK interrupt
- {setBLANK();
- stopGSCLK();
- stopBLANKTimer();
- if (tlc_needXLAT)
- {
- tlc_needXLAT = false;
- pulseXLAT();
- if (tlc_needExtraSCLKPulseAfterGSData)
- {
- pulseSPI_SCLK();
- tlc_needExtraSCLKPulseAfterGSData = false;
- }
- }
- clearBLANK();
- startGSCLKandBLANKTimers();
- return;}
- ////////////////////////////////////////////////////////////////////////////////
- /// End of Code
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement