TLC5940 test program. Uses the Pinscape TLC5940 interface class in isolation to test the outputs directly. Configured for Pinscape Expansion Boards by default but can be changed to any other pin configuration.

Dependencies:   FastPWM mbed

Committer:
mjr
Date:
Fri Jul 08 23:57:01 2016 +0000
Revision:
1:f66778df0d8d
Parent:
0:3ca2fd0d22ba
Set mode to 0 by default;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 0:3ca2fd0d22ba 1 // Pinscape Controller TLC5940 interface
mjr 0:3ca2fd0d22ba 2 //
mjr 0:3ca2fd0d22ba 3 // Based on Spencer Davis's mbed TLC5940 library. Adapted for the
mjr 0:3ca2fd0d22ba 4 // KL25Z and simplified (removes dot correction and status input
mjr 0:3ca2fd0d22ba 5 // support).
mjr 0:3ca2fd0d22ba 6
mjr 0:3ca2fd0d22ba 7
mjr 0:3ca2fd0d22ba 8 #ifndef TLC5940_H
mjr 0:3ca2fd0d22ba 9 #define TLC5940_H
mjr 0:3ca2fd0d22ba 10
mjr 0:3ca2fd0d22ba 11 #include "FastPWM.h"
mjr 0:3ca2fd0d22ba 12
mjr 0:3ca2fd0d22ba 13 // --------------------------------------------------------------------------
mjr 0:3ca2fd0d22ba 14 // Data Transmission Mode.
mjr 0:3ca2fd0d22ba 15 //
mjr 0:3ca2fd0d22ba 16 // NOTE! This section contains a possible workaround to try if you're
mjr 0:3ca2fd0d22ba 17 // having data signal stability problems with your TLC5940 chips. If
mjr 0:3ca2fd0d22ba 18 // things are working properly, you can ignore this part.
mjr 0:3ca2fd0d22ba 19 //
mjr 0:3ca2fd0d22ba 20 // The software has two options for sending data updates to the chips:
mjr 0:3ca2fd0d22ba 21 //
mjr 0:3ca2fd0d22ba 22 // Mode 0: Send data *during* the grayscale cycle. This is the default,
mjr 0:3ca2fd0d22ba 23 // and it's the standard method the chips are designed for. In this mode,
mjr 0:3ca2fd0d22ba 24 // we start sending an update just after then blanking interval that starts
mjr 0:3ca2fd0d22ba 25 // a new grayscale cycle. The timing is arranged so that the update is
mjr 0:3ca2fd0d22ba 26 // completed well before the end of the grayscale cycle. At the next
mjr 0:3ca2fd0d22ba 27 // blanking interval, we latch the new data, so the new brightness levels
mjr 0:3ca2fd0d22ba 28 // will be shown starting on the next cycle.
mjr 0:3ca2fd0d22ba 29 //
mjr 0:3ca2fd0d22ba 30 // Mode 1: Send data *between* grayscale cycles. In this mode, we send
mjr 0:3ca2fd0d22ba 31 // each complete update during a blanking period, then latch the update
mjr 0:3ca2fd0d22ba 32 // and start the next grayscale cycle. This isn't the way the chips were
mjr 0:3ca2fd0d22ba 33 // intended to be used, but it works. The disadvantage is that it requires
mjr 0:3ca2fd0d22ba 34 // the blanking interval to be extended long enough for the full data
mjr 0:3ca2fd0d22ba 35 // update (192 bits * the number of chips in the chain). Since the
mjr 0:3ca2fd0d22ba 36 // outputs are turned off throughout the blanking period, this reduces
mjr 0:3ca2fd0d22ba 37 // the overall brightness/intensity of the outputs by reducing the duty
mjr 0:3ca2fd0d22ba 38 // cycle. The TLC5940 chips can't achieve 100% duty cycle to begin with,
mjr 0:3ca2fd0d22ba 39 // since they require a brief minimum time in the blanking interval
mjr 0:3ca2fd0d22ba 40 // between grayscale cycles; however, the minimum is so short that the
mjr 0:3ca2fd0d22ba 41 // duty cycle is close to 100%. With the full data transmission stuffed
mjr 0:3ca2fd0d22ba 42 // into the blanking interval, we reduce the duty cycle further below
mjr 0:3ca2fd0d22ba 43 // 100%. With four chips in the chain, a 28 MHz data clock, and a
mjr 0:3ca2fd0d22ba 44 // 500 kHz grayscale clock, the reduction is about 0.3%.
mjr 0:3ca2fd0d22ba 45 //
mjr 0:3ca2fd0d22ba 46 // Mode 0 is the method documented in the manufacturer's data sheet.
mjr 0:3ca2fd0d22ba 47 // It works well empirically with the Pinscape expansion boards.
mjr 0:3ca2fd0d22ba 48 //
mjr 0:3ca2fd0d22ba 49 // So what's the point of Mode 1? In early testing, with a breadboard
mjr 0:3ca2fd0d22ba 50 // setup, I saw some problems with data signal stability, which manifested
mjr 0:3ca2fd0d22ba 51 // as sporadic flickering in the outputs. Switching to Mode 1 improved
mjr 0:3ca2fd0d22ba 52 // the signal stability considerably. I'm therefore leaving this code
mjr 0:3ca2fd0d22ba 53 // available as an option in case anyone runs into similar signal problems
mjr 0:3ca2fd0d22ba 54 // and wants to try the alternative mode as a workaround.
mjr 0:3ca2fd0d22ba 55 //
mjr 0:3ca2fd0d22ba 56 #define DATA_UPDATE_INSIDE_BLANKING 0
mjr 0:3ca2fd0d22ba 57
mjr 0:3ca2fd0d22ba 58 #include "mbed.h"
mjr 0:3ca2fd0d22ba 59
mjr 0:3ca2fd0d22ba 60
mjr 0:3ca2fd0d22ba 61 // --------------------------------------------------------------------------
mjr 0:3ca2fd0d22ba 62 // Some notes on the data transmission design
mjr 0:3ca2fd0d22ba 63 //
mjr 0:3ca2fd0d22ba 64 // I spent a while working on using DMA to send the data, thinking that
mjr 0:3ca2fd0d22ba 65 // this would reduce the CPU load. But I couldn't get this working
mjr 0:3ca2fd0d22ba 66 // reliably; there was some kind of timing interaction or race condition
mjr 0:3ca2fd0d22ba 67 // that caused crashes when initiating the DMA transfer from within the
mjr 0:3ca2fd0d22ba 68 // blanking interrupt. I spent quite a while trying to debug it and
mjr 0:3ca2fd0d22ba 69 // couldn't figure out what was going on. There are some complications
mjr 0:3ca2fd0d22ba 70 // involved in using DMA with SPI that are documented in the KL25Z
mjr 0:3ca2fd0d22ba 71 // reference manual, and I was following those carefully, but I suspect
mjr 0:3ca2fd0d22ba 72 // that the problem was somehow related to that, because it seemed to
mjr 0:3ca2fd0d22ba 73 // be sporadic and timing-related, and I couldn't find any software race
mjr 0:3ca2fd0d22ba 74 // conditions or concurrency issues that could explain it.
mjr 0:3ca2fd0d22ba 75 //
mjr 0:3ca2fd0d22ba 76 // I finally decided that I wasn't going to crack that and started looking
mjr 0:3ca2fd0d22ba 77 // for alternatives, so out of curiosity, I measured the time needed for a
mjr 0:3ca2fd0d22ba 78 // synchronous (CPU-driven) SPI send, to see how it would fit into various
mjr 0:3ca2fd0d22ba 79 // places in the code. This turned out to be faster than I expected: with
mjr 0:3ca2fd0d22ba 80 // SPI at 28MHz, the measured time for a synchronous send is about 72us for
mjr 0:3ca2fd0d22ba 81 // 4 chips worth of GS data (192 bits), which I expect to be the typical
mjr 0:3ca2fd0d22ba 82 // Expansion Board setup. For an 8-chip setup, which will probably be
mjr 0:3ca2fd0d22ba 83 // about the maximum workable setup, the time would be 144us. We only have
mjr 0:3ca2fd0d22ba 84 // to send the data once per grayscale cycle, and each cycle is 11.7ms with
mjr 0:3ca2fd0d22ba 85 // the grayscale clock at 350kHz (4096 steps per cycle divided by 350,000
mjr 0:3ca2fd0d22ba 86 // steps per second = 11.7ms per cycle), so this is only 1% overhead. The
mjr 0:3ca2fd0d22ba 87 // main loop spends most of its time polling anyway, so we have plenty of
mjr 0:3ca2fd0d22ba 88 // cycles to reallocate from idle polling to the sending the data.
mjr 0:3ca2fd0d22ba 89 //
mjr 0:3ca2fd0d22ba 90 // The easiest place to do the send is in the blanking interval ISR, but
mjr 0:3ca2fd0d22ba 91 // I wanted to keep this out of the ISR. It's only ~100us, but even so,
mjr 0:3ca2fd0d22ba 92 // it's critical to minimize time in ISRs so that we don't miss other
mjr 0:3ca2fd0d22ba 93 // interrupts. So instead, I set it up so that the ISR coordinates with
mjr 0:3ca2fd0d22ba 94 // the main loop via a flag:
mjr 0:3ca2fd0d22ba 95 //
mjr 0:3ca2fd0d22ba 96 // - In the blanking interrupt, set a flag ("cts" = clear to send),
mjr 0:3ca2fd0d22ba 97 // and arm a timeout that fires 2/3 through the next blanking cycle
mjr 0:3ca2fd0d22ba 98 //
mjr 0:3ca2fd0d22ba 99 // - In the main loop, poll "cts" each time through the loop. When
mjr 0:3ca2fd0d22ba 100 // cts is true, send the data synchronously and clear the flag.
mjr 0:3ca2fd0d22ba 101 // Do nothing when cts is false.
mjr 0:3ca2fd0d22ba 102 //
mjr 0:3ca2fd0d22ba 103 // The main loop runs on about a 1.5ms cycle, and 2/3 of the grayscale
mjr 0:3ca2fd0d22ba 104 // cycle is 8ms, so the main loop will poll cts on average 5 times per
mjr 0:3ca2fd0d22ba 105 // 8ms window. That makes it all but certain that we'll do a send in
mjr 0:3ca2fd0d22ba 106 // a timely fashion on every grayscale cycle.
mjr 0:3ca2fd0d22ba 107 //
mjr 0:3ca2fd0d22ba 108 // The point of the 2/3 window is to guarantee that the data send is
mjr 0:3ca2fd0d22ba 109 // finished before the grayscale cycle ends. The TLC5940 chips require
mjr 0:3ca2fd0d22ba 110 // this; data transmission has to be entirely between blanking intervals.
mjr 0:3ca2fd0d22ba 111 // The main loop and interrupt handler are operating asynchronously
mjr 0:3ca2fd0d22ba 112 // relative to one another, so the exact phase alignment will vary
mjr 0:3ca2fd0d22ba 113 // randomly. If we start a transmission within the 2/3 window, we're
mjr 0:3ca2fd0d22ba 114 // guaranteed to have at least 3.5ms (1/3 of the cycle) left before
mjr 0:3ca2fd0d22ba 115 // the next blanking interval. The transmission only takes ~100us,
mjr 0:3ca2fd0d22ba 116 // so we're leaving tons of margin for error in the timing - we have
mjr 0:3ca2fd0d22ba 117 // 34x longer than we need.
mjr 0:3ca2fd0d22ba 118 //
mjr 0:3ca2fd0d22ba 119 // The main loop can easily absorb the extra ~100us of overhead without
mjr 0:3ca2fd0d22ba 120 // even noticing. The loop spends most of its time polling devices, so
mjr 0:3ca2fd0d22ba 121 // it's really mostly idle time to start with. So we're effectively
mjr 0:3ca2fd0d22ba 122 // reallocating some idle time to useful work. The chunk of time is
mjr 0:3ca2fd0d22ba 123 // only about 6% of one loop iteration, so we're not even significantly
mjr 0:3ca2fd0d22ba 124 // extending the occasional iterations that actually do this work.
mjr 0:3ca2fd0d22ba 125 // (If we had a 2ms chunk of monolithic work to do, that could start
mjr 0:3ca2fd0d22ba 126 // to add undesirable latency to other polling tasks. 100us won't.)
mjr 0:3ca2fd0d22ba 127 //
mjr 0:3ca2fd0d22ba 128 // We could conceivably reduce this overhead slightly by adding DMA,
mjr 0:3ca2fd0d22ba 129 // but I'm not sure it would actually do much good. Setting up the DMA
mjr 0:3ca2fd0d22ba 130 // transfer would probably take at least 20us in CPU time just to set
mjr 0:3ca2fd0d22ba 131 // up all of the registers. And SPI is so fast that the DMA transfer
mjr 0:3ca2fd0d22ba 132 // would saturate the CPU memory bus for the 30us or so of the transfer.
mjr 0:3ca2fd0d22ba 133 // (I have my suspicions that this bus saturation effect might be part
mjr 0:3ca2fd0d22ba 134 // of the problem I was having getting DMA working in the first place.)
mjr 0:3ca2fd0d22ba 135 // So we'd go from 100us of overhead per cycle to at maybe 50us per
mjr 0:3ca2fd0d22ba 136 // cycle. We'd also have to introduce some concurrency controls to the
mjr 0:3ca2fd0d22ba 137 // output "set" operation that we don't need with the current scheme
mjr 0:3ca2fd0d22ba 138 // (because it's synchronous). So overall I think the current
mjr 0:3ca2fd0d22ba 139 // synchronous approach is almost as good in terms of performance as
mjr 0:3ca2fd0d22ba 140 // an asynchronous DMA setup would be, and it's a heck of a lot simpler
mjr 0:3ca2fd0d22ba 141 // and seems very reliable.
mjr 0:3ca2fd0d22ba 142 //
mjr 0:3ca2fd0d22ba 143 // --------------------------------------------------------------------------
mjr 0:3ca2fd0d22ba 144
mjr 0:3ca2fd0d22ba 145
mjr 0:3ca2fd0d22ba 146 /**
mjr 0:3ca2fd0d22ba 147 * SPI speed used by the mbed to communicate with the TLC5940
mjr 0:3ca2fd0d22ba 148 * The TLC5940 supports up to 30Mhz. It's best to keep this as
mjr 0:3ca2fd0d22ba 149 * high as possible, since a higher SPI speed yields a faster
mjr 0:3ca2fd0d22ba 150 * grayscale data update. However, I've seen some slight
mjr 0:3ca2fd0d22ba 151 * instability in the signal in my breadboard setup using the
mjr 0:3ca2fd0d22ba 152 * full 30MHz, so I've reduced this slightly, which seems to
mjr 0:3ca2fd0d22ba 153 * yield a solid signal. The limit will vary according to how
mjr 0:3ca2fd0d22ba 154 * clean the signal path is to the chips; you can probably crank
mjr 0:3ca2fd0d22ba 155 * this up to full speed if you have a well-designed PCB, good
mjr 0:3ca2fd0d22ba 156 * decoupling capacitors near the 5940 VCC/GND pins, and short
mjr 0:3ca2fd0d22ba 157 * wires between the KL25Z and the PCB. A short, clean path to
mjr 0:3ca2fd0d22ba 158 * KL25Z ground seems especially important.
mjr 0:3ca2fd0d22ba 159 *
mjr 0:3ca2fd0d22ba 160 * The SPI clock must be fast enough that the data transmission
mjr 0:3ca2fd0d22ba 161 * time for a full update is comfortably less than the blanking
mjr 0:3ca2fd0d22ba 162 * cycle time. The grayscale refresh requires 192 bits per TLC5940
mjr 0:3ca2fd0d22ba 163 * in the daisy chain, and each bit takes one SPI clock to send.
mjr 0:3ca2fd0d22ba 164 * Our reference setup in the Pinscape controller allows for up to
mjr 0:3ca2fd0d22ba 165 * 4 TLC5940s, so a full refresh cycle on a fully populated system
mjr 0:3ca2fd0d22ba 166 * would be 768 SPI clocks. The blanking cycle is 4096 GSCLK cycles.
mjr 0:3ca2fd0d22ba 167 *
mjr 0:3ca2fd0d22ba 168 * t(blank) = 4096 * 1/GSCLK_SPEED
mjr 0:3ca2fd0d22ba 169 * t(refresh) = 768 * 1/SPI_SPEED
mjr 0:3ca2fd0d22ba 170 * Therefore: SPI_SPEED must be > 768/4096 * GSCLK_SPEED
mjr 0:3ca2fd0d22ba 171 *
mjr 0:3ca2fd0d22ba 172 * Since the SPI speed can be so high, and since we want to keep
mjr 0:3ca2fd0d22ba 173 * the GSCLK speed relatively low, the constraint above simply
mjr 0:3ca2fd0d22ba 174 * isn't a factor. E.g., at SPI=30MHz and GSCLK=500kHz,
mjr 0:3ca2fd0d22ba 175 * t(blank) is 8192us and t(refresh) is 25us.
mjr 0:3ca2fd0d22ba 176 */
mjr 0:3ca2fd0d22ba 177 #define SPI_SPEED 28000000
mjr 0:3ca2fd0d22ba 178
mjr 0:3ca2fd0d22ba 179 /**
mjr 0:3ca2fd0d22ba 180 * The rate at which the GSCLK pin is pulsed. This also controls
mjr 0:3ca2fd0d22ba 181 * how often the reset function is called. The reset function call
mjr 0:3ca2fd0d22ba 182 * interval is (1/GSCLK_SPEED) * 4096. The maximum reliable rate is
mjr 0:3ca2fd0d22ba 183 * around 32Mhz. It's best to keep this rate as low as possible:
mjr 0:3ca2fd0d22ba 184 * the higher the rate, the higher the refresh() call frequency,
mjr 0:3ca2fd0d22ba 185 * so the higher the CPU load. Higher frequencies also make it more
mjr 0:3ca2fd0d22ba 186 * challenging to wire the chips for clean signal transmission, so
mjr 0:3ca2fd0d22ba 187 * minimizing the clock speed will help with signal stability.
mjr 0:3ca2fd0d22ba 188 *
mjr 0:3ca2fd0d22ba 189 * The lower bound depends on the application. For driving lights,
mjr 0:3ca2fd0d22ba 190 * the limiting factor is flicker: the lower the rate, the more
mjr 0:3ca2fd0d22ba 191 * noticeable the flicker. Incandescents tend to look flicker-free
mjr 0:3ca2fd0d22ba 192 * at about 50 Hz (205 kHz grayscale clock). LEDs need slightly
mjr 0:3ca2fd0d22ba 193 * faster rates.
mjr 0:3ca2fd0d22ba 194 */
mjr 0:3ca2fd0d22ba 195 #define GSCLK_SPEED 350000
mjr 0:3ca2fd0d22ba 196
mjr 0:3ca2fd0d22ba 197 class TLC5940
mjr 0:3ca2fd0d22ba 198 {
mjr 0:3ca2fd0d22ba 199 public:
mjr 0:3ca2fd0d22ba 200 /**
mjr 0:3ca2fd0d22ba 201 * Set up the TLC5940
mjr 0:3ca2fd0d22ba 202 *
mjr 0:3ca2fd0d22ba 203 * @param SCLK - The SCK pin of the SPI bus
mjr 0:3ca2fd0d22ba 204 * @param MOSI - The MOSI pin of the SPI bus
mjr 0:3ca2fd0d22ba 205 * @param GSCLK - The GSCLK pin of the TLC5940(s)
mjr 0:3ca2fd0d22ba 206 * @param BLANK - The BLANK pin of the TLC5940(s)
mjr 0:3ca2fd0d22ba 207 * @param XLAT - The XLAT pin of the TLC5940(s)
mjr 0:3ca2fd0d22ba 208 * @param nchips - The number of TLC5940s (if you are daisy chaining)
mjr 0:3ca2fd0d22ba 209 */
mjr 0:3ca2fd0d22ba 210 TLC5940(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, PinName XLAT, int nchips)
mjr 0:3ca2fd0d22ba 211 : spi(MOSI, NC, SCLK),
mjr 0:3ca2fd0d22ba 212 gsclk(GSCLK),
mjr 0:3ca2fd0d22ba 213 blank(BLANK),
mjr 0:3ca2fd0d22ba 214 xlat(XLAT),
mjr 0:3ca2fd0d22ba 215 nchips(nchips)
mjr 0:3ca2fd0d22ba 216 {
mjr 0:3ca2fd0d22ba 217 // start up initially disabled
mjr 0:3ca2fd0d22ba 218 enabled = false;
mjr 0:3ca2fd0d22ba 219
mjr 0:3ca2fd0d22ba 220 // set XLAT to initially off
mjr 0:3ca2fd0d22ba 221 xlat = 0;
mjr 0:3ca2fd0d22ba 222
mjr 0:3ca2fd0d22ba 223 // Assert BLANK while starting up, to keep the outputs turned off until
mjr 0:3ca2fd0d22ba 224 // everything is stable. This helps prevent spurious flashes during startup.
mjr 0:3ca2fd0d22ba 225 // (That's not particularly important for lights, but it matters more for
mjr 0:3ca2fd0d22ba 226 // tactile devices. It's a bit alarming to fire a replay knocker on every
mjr 0:3ca2fd0d22ba 227 // power-on, for example.)
mjr 0:3ca2fd0d22ba 228 blank = 1;
mjr 0:3ca2fd0d22ba 229
mjr 0:3ca2fd0d22ba 230 // Configure SPI format and speed. The KL25Z only supports 8-bit mode.
mjr 0:3ca2fd0d22ba 231 // We nominally need to write the data in 12-bit chunks for the TLC5940
mjr 0:3ca2fd0d22ba 232 // grayscale levels, but SPI is ultimately just a bit-level serial format,
mjr 0:3ca2fd0d22ba 233 // so we can reformat the 12-bit blocks into 8-bit bytes to fit the
mjr 0:3ca2fd0d22ba 234 // KL25Z's limits. This should work equally well on other microcontrollers
mjr 0:3ca2fd0d22ba 235 // that are more flexible. The TLC5940 requires polarity/phase format 0.
mjr 0:3ca2fd0d22ba 236 spi.format(8, 0);
mjr 0:3ca2fd0d22ba 237 spi.frequency(SPI_SPEED);
mjr 0:3ca2fd0d22ba 238
mjr 0:3ca2fd0d22ba 239 // Send out a full data set to the chips, to clear out any random
mjr 0:3ca2fd0d22ba 240 // startup data from the registers. Include some extra bits - there
mjr 0:3ca2fd0d22ba 241 // are some cases (such as after sending dot correct commands) where
mjr 0:3ca2fd0d22ba 242 // an extra bit per chip is required, and the initial state is
mjr 0:3ca2fd0d22ba 243 // unpredictable, so send extra bits to make sure we cover all bases.
mjr 0:3ca2fd0d22ba 244 // This does no harm; extra bits just fall off the end of the daisy
mjr 0:3ca2fd0d22ba 245 // chain, and since we want all registers initially set to 0, we can
mjr 0:3ca2fd0d22ba 246 // send arbitrarily many extra 0's.
mjr 0:3ca2fd0d22ba 247 for (int i = 0 ; i < nchips*25 ; ++i)
mjr 0:3ca2fd0d22ba 248 spi.write(0x00);
mjr 0:3ca2fd0d22ba 249
mjr 0:3ca2fd0d22ba 250 // do an initial XLAT to latch all of these "0" values into the
mjr 0:3ca2fd0d22ba 251 // grayscale registers
mjr 0:3ca2fd0d22ba 252 xlat = 1;
mjr 0:3ca2fd0d22ba 253 xlat = 0;
mjr 0:3ca2fd0d22ba 254
mjr 0:3ca2fd0d22ba 255 // Allocate our SPI buffer. The transfer on each cycle is 192 bits per
mjr 0:3ca2fd0d22ba 256 // chip = 24 bytes per chip.
mjr 0:3ca2fd0d22ba 257 spilen = nchips*24;
mjr 0:3ca2fd0d22ba 258 spibuf = new uint8_t[spilen];
mjr 0:3ca2fd0d22ba 259 memset(spibuf, 0x00, spilen);
mjr 0:3ca2fd0d22ba 260
mjr 0:3ca2fd0d22ba 261 // Configure the GSCLK output's frequency
mjr 0:3ca2fd0d22ba 262 gsclk.period(1.0/GSCLK_SPEED);
mjr 0:3ca2fd0d22ba 263
mjr 0:3ca2fd0d22ba 264 // we're not yet ready to send new data to the chips
mjr 0:3ca2fd0d22ba 265 cts = false;
mjr 0:3ca2fd0d22ba 266
mjr 0:3ca2fd0d22ba 267 // we don't need an XLAT signal until we send data
mjr 0:3ca2fd0d22ba 268 needXlat = false;
mjr 0:3ca2fd0d22ba 269 }
mjr 0:3ca2fd0d22ba 270
mjr 0:3ca2fd0d22ba 271 // Global enable/disble. When disabled, we assert the blanking signal
mjr 0:3ca2fd0d22ba 272 // continuously to keep all outputs turned off. This can be used during
mjr 0:3ca2fd0d22ba 273 // startup and sleep mode to prevent spurious output signals from
mjr 0:3ca2fd0d22ba 274 // uninitialized grayscale registers. The chips have random values in
mjr 0:3ca2fd0d22ba 275 // their internal registers when power is first applied, so we have to
mjr 0:3ca2fd0d22ba 276 // explicitly send the initial zero levels after power cycling the chips.
mjr 0:3ca2fd0d22ba 277 // The chips might not have power even when the KL25Z is running, because
mjr 0:3ca2fd0d22ba 278 // they might be powered from a separate power supply from the KL25Z
mjr 0:3ca2fd0d22ba 279 // (the Pinscape Expansion Boards work this way). Global blanking helps
mjr 0:3ca2fd0d22ba 280 // us start up more cleanly by suppressing all outputs until we can be
mjr 0:3ca2fd0d22ba 281 // reasonably sure that the various chip registers are initialized.
mjr 0:3ca2fd0d22ba 282 void enable(bool f)
mjr 0:3ca2fd0d22ba 283 {
mjr 0:3ca2fd0d22ba 284 // note the new setting
mjr 0:3ca2fd0d22ba 285 enabled = f;
mjr 0:3ca2fd0d22ba 286
mjr 0:3ca2fd0d22ba 287 // If disabled, apply blanking immediately. If enabled, do nothing
mjr 0:3ca2fd0d22ba 288 // extra; we'll drop the blanking signal at the end of the next
mjr 0:3ca2fd0d22ba 289 // blanking interval as normal.
mjr 0:3ca2fd0d22ba 290 if (!f)
mjr 0:3ca2fd0d22ba 291 {
mjr 0:3ca2fd0d22ba 292 // disable interrupts, since the blanking interrupt writes gsclk too
mjr 0:3ca2fd0d22ba 293 __disable_irq();
mjr 0:3ca2fd0d22ba 294
mjr 0:3ca2fd0d22ba 295 // turn off the GS clock and assert BLANK to turn off all outputs
mjr 0:3ca2fd0d22ba 296 gsclk.write(0);
mjr 0:3ca2fd0d22ba 297 blank = 1;
mjr 0:3ca2fd0d22ba 298
mjr 0:3ca2fd0d22ba 299 // done messing with shared data
mjr 0:3ca2fd0d22ba 300 __enable_irq();
mjr 0:3ca2fd0d22ba 301 }
mjr 0:3ca2fd0d22ba 302
mjr 0:3ca2fd0d22ba 303 }
mjr 0:3ca2fd0d22ba 304
mjr 0:3ca2fd0d22ba 305 // Start the clock running
mjr 0:3ca2fd0d22ba 306 void start()
mjr 0:3ca2fd0d22ba 307 {
mjr 0:3ca2fd0d22ba 308 // Set up the first call to the reset function, which asserts BLANK to
mjr 0:3ca2fd0d22ba 309 // end the PWM cycle and handles new grayscale data output and latching.
mjr 0:3ca2fd0d22ba 310 // The original version of this library uses a timer to call reset
mjr 0:3ca2fd0d22ba 311 // periodically, but that approach is somewhat problematic because the
mjr 0:3ca2fd0d22ba 312 // reset function itself takes a small amount of time to run, so the
mjr 0:3ca2fd0d22ba 313 // *actual* cycle is slightly longer than what we get from counting
mjr 0:3ca2fd0d22ba 314 // GS clocks. Running reset on a timer therefore causes the calls to
mjr 0:3ca2fd0d22ba 315 // slip out of phase with the actual full cycles, which causes
mjr 0:3ca2fd0d22ba 316 // premature blanking that shows up as visible flicker. To get the
mjr 0:3ca2fd0d22ba 317 // reset cycle to line up more precisely with a full PWM cycle, it
mjr 0:3ca2fd0d22ba 318 // works better to set up a new timer at the end of each cycle. That
mjr 0:3ca2fd0d22ba 319 // organically accounts for the time spent in the interrupt handler.
mjr 0:3ca2fd0d22ba 320 // This doesn't result in perfectly uniform timing, since interrupt
mjr 0:3ca2fd0d22ba 321 // latency varies slightly on each interrupt, but it does guarantee
mjr 0:3ca2fd0d22ba 322 // that the blanking will never be premature - all variation will go
mjr 0:3ca2fd0d22ba 323 // into the tail end of the cycle after the 4096 GS clocks. That
mjr 0:3ca2fd0d22ba 324 // might cause some brightness variation, but it won't cause flicker,
mjr 0:3ca2fd0d22ba 325 // and in practice any brightness variation from this seems to be too
mjr 0:3ca2fd0d22ba 326 // small to be visible.
mjr 0:3ca2fd0d22ba 327 armReset();
mjr 0:3ca2fd0d22ba 328 }
mjr 0:3ca2fd0d22ba 329
mjr 0:3ca2fd0d22ba 330 /*
mjr 0:3ca2fd0d22ba 331 * Set an output. 'idx' is the output index: 0 is OUT0 on the first
mjr 0:3ca2fd0d22ba 332 * chip, 1 is OUT1 on the first chip, 16 is OUT0 on the second chip
mjr 0:3ca2fd0d22ba 333 * in the daisy chain, etc. 'data' is the brightness value for the
mjr 0:3ca2fd0d22ba 334 * output, 0=off, 4095=full brightness.
mjr 0:3ca2fd0d22ba 335 */
mjr 0:3ca2fd0d22ba 336 void set(int idx, unsigned short data)
mjr 0:3ca2fd0d22ba 337 {
mjr 0:3ca2fd0d22ba 338 // validate the index
mjr 0:3ca2fd0d22ba 339 if (idx >= 0 && idx < nchips*16)
mjr 0:3ca2fd0d22ba 340 {
mjr 0:3ca2fd0d22ba 341 #if DATA_UPDATE_INSIDE_BLANKING
mjr 0:3ca2fd0d22ba 342 // If we send data within the blanking interval, turn off interrupts while
mjr 0:3ca2fd0d22ba 343 // modifying the buffer, since the send happens in the interrupt handler.
mjr 0:3ca2fd0d22ba 344 __disable_irq();
mjr 0:3ca2fd0d22ba 345 #endif
mjr 0:3ca2fd0d22ba 346
mjr 0:3ca2fd0d22ba 347 // Figure the SPI buffer location of the output we're changing. The SPI
mjr 0:3ca2fd0d22ba 348 // buffer has the packed bit format that we send across the wire, with 12
mjr 0:3ca2fd0d22ba 349 // bits per output, arranged from last output to first output (N = number
mjr 0:3ca2fd0d22ba 350 // of outputs = nchips*16):
mjr 0:3ca2fd0d22ba 351 //
mjr 0:3ca2fd0d22ba 352 // byte 0 = high 8 bits of output N-1
mjr 0:3ca2fd0d22ba 353 // 1 = low 4 bits of output N-1 | high 4 bits of output N-2
mjr 0:3ca2fd0d22ba 354 // 2 = low 8 bits of N-2
mjr 0:3ca2fd0d22ba 355 // 3 = high 8 bits of N-3
mjr 0:3ca2fd0d22ba 356 // 4 = low 4 bits of N-3 | high 4 bits of N-2
mjr 0:3ca2fd0d22ba 357 // 5 = low 8bits of N-4
mjr 0:3ca2fd0d22ba 358 // ...
mjr 0:3ca2fd0d22ba 359 // 24*nchips-3 = high 8 bits of output 1
mjr 0:3ca2fd0d22ba 360 // 24*nchips-2 = low 4 bits of output 1 | high 4 bits of output 0
mjr 0:3ca2fd0d22ba 361 // 24*nchips-1 = low 8 bits of output 0
mjr 0:3ca2fd0d22ba 362 //
mjr 0:3ca2fd0d22ba 363 // So this update will affect two bytes. If the output number if even, we're
mjr 0:3ca2fd0d22ba 364 // in the high 4 + low 8 pair; if odd, we're in the high 8 + low 4 pair.
mjr 0:3ca2fd0d22ba 365 int di = nchips*24 - 3 - (3*(idx/2));
mjr 0:3ca2fd0d22ba 366 if (idx & 1)
mjr 0:3ca2fd0d22ba 367 {
mjr 0:3ca2fd0d22ba 368 // ODD = high 8 | low 4
mjr 0:3ca2fd0d22ba 369 spibuf[di] = uint8_t((data >> 4) & 0xff);
mjr 0:3ca2fd0d22ba 370 spibuf[di+1] &= 0x0F;
mjr 0:3ca2fd0d22ba 371 spibuf[di+1] |= uint8_t((data << 4) & 0xf0);
mjr 0:3ca2fd0d22ba 372 }
mjr 0:3ca2fd0d22ba 373 else
mjr 0:3ca2fd0d22ba 374 {
mjr 0:3ca2fd0d22ba 375 // EVEN = high 4 | low 8
mjr 0:3ca2fd0d22ba 376 spibuf[di+1] &= 0xF0;
mjr 0:3ca2fd0d22ba 377 spibuf[di+1] |= uint8_t((data >> 8) & 0x0f);
mjr 0:3ca2fd0d22ba 378 spibuf[di+2] = uint8_t(data & 0xff);
mjr 0:3ca2fd0d22ba 379 }
mjr 0:3ca2fd0d22ba 380
mjr 0:3ca2fd0d22ba 381 #if DATA_UPDATE_INSIDE_BLANKING
mjr 0:3ca2fd0d22ba 382 // re-enable interrupts
mjr 0:3ca2fd0d22ba 383 __enable_irq();
mjr 0:3ca2fd0d22ba 384 #endif
mjr 0:3ca2fd0d22ba 385 }
mjr 0:3ca2fd0d22ba 386 }
mjr 0:3ca2fd0d22ba 387
mjr 0:3ca2fd0d22ba 388 // Update the outputs. In our current implementation, this doesn't do
mjr 0:3ca2fd0d22ba 389 // anything, since we send the current state to the chips on every grayscale
mjr 0:3ca2fd0d22ba 390 // cycle, whether or not there are updates. We provide the interface for
mjr 0:3ca2fd0d22ba 391 // consistency with other peripheral device interfaces in the main loop,
mjr 0:3ca2fd0d22ba 392 // and in case we make any future implementation changes that require some
mjr 0:3ca2fd0d22ba 393 // action to carry out an explicit update.
mjr 0:3ca2fd0d22ba 394 void update(bool force = false)
mjr 0:3ca2fd0d22ba 395 {
mjr 0:3ca2fd0d22ba 396 }
mjr 0:3ca2fd0d22ba 397
mjr 0:3ca2fd0d22ba 398 // Send updates if ready. Our top-level program's main loop calls this on
mjr 0:3ca2fd0d22ba 399 // every iteration. This lets us send grayscale updates to the chips in
mjr 0:3ca2fd0d22ba 400 // regular application context (rather than in interrupt context), to keep
mjr 0:3ca2fd0d22ba 401 // the time in the ISR as short as possible. We return immediately if
mjr 0:3ca2fd0d22ba 402 // we're not within the update window or we've already sent updates for
mjr 0:3ca2fd0d22ba 403 // the current cycle.
mjr 0:3ca2fd0d22ba 404 void send()
mjr 0:3ca2fd0d22ba 405 {
mjr 0:3ca2fd0d22ba 406 // if we're in the transmission window, send the data
mjr 0:3ca2fd0d22ba 407 if (cts)
mjr 0:3ca2fd0d22ba 408 {
mjr 0:3ca2fd0d22ba 409 // Write the data to the SPI port. Note that we go directly
mjr 0:3ca2fd0d22ba 410 // to the hardware registers rather than using the mbed SPI
mjr 0:3ca2fd0d22ba 411 // class, because this makes the operation about 50% faster.
mjr 0:3ca2fd0d22ba 412 // The mbed class checks for input on every byte in case the
mjr 0:3ca2fd0d22ba 413 // SPI connection is bidirectional, but for this application
mjr 0:3ca2fd0d22ba 414 // it's strictly one-way, so we can skip checking for input
mjr 0:3ca2fd0d22ba 415 // and just blast bits to the output register as fast as
mjr 0:3ca2fd0d22ba 416 // it'll take them. Before writing the output register
mjr 0:3ca2fd0d22ba 417 // ("D"), we have to check the status register ("S") and see
mjr 0:3ca2fd0d22ba 418 // that the Transmit Empty Flag (SPTEF) is set. The
mjr 0:3ca2fd0d22ba 419 // procedure is: spin until SPTEF s set in "S", write the
mjr 0:3ca2fd0d22ba 420 // next byte to "D", loop until out of bytes.
mjr 0:3ca2fd0d22ba 421 uint8_t *p = spibuf;
mjr 0:3ca2fd0d22ba 422 for (int i = spilen ; i > 0 ; --i) {
mjr 0:3ca2fd0d22ba 423 while (!(SPI0->S & SPI_S_SPTEF_MASK)) ;
mjr 0:3ca2fd0d22ba 424 SPI0->D = *p++;
mjr 0:3ca2fd0d22ba 425 }
mjr 0:3ca2fd0d22ba 426
mjr 0:3ca2fd0d22ba 427 // we've sent new data, so we need an XLAT signal to latch it
mjr 0:3ca2fd0d22ba 428 needXlat = true;
mjr 0:3ca2fd0d22ba 429
mjr 0:3ca2fd0d22ba 430 // done - we don't need to send again until the next GS cycle
mjr 0:3ca2fd0d22ba 431 cts = false;
mjr 0:3ca2fd0d22ba 432 }
mjr 0:3ca2fd0d22ba 433 }
mjr 0:3ca2fd0d22ba 434
mjr 0:3ca2fd0d22ba 435 private:
mjr 0:3ca2fd0d22ba 436 // SPI port. This is master mode, output only, so we only assign the MOSI
mjr 0:3ca2fd0d22ba 437 // and SCK pins.
mjr 0:3ca2fd0d22ba 438 SPI spi;
mjr 0:3ca2fd0d22ba 439
mjr 0:3ca2fd0d22ba 440 // SPI transfer buffer. This contains the live grayscale data, formatted
mjr 0:3ca2fd0d22ba 441 // for direct transmission to the TLC5940 chips via SPI.
mjr 0:3ca2fd0d22ba 442 uint8_t *volatile spibuf;
mjr 0:3ca2fd0d22ba 443
mjr 0:3ca2fd0d22ba 444 // Length of the SPI buffer in bytes. The native data format of the chips
mjr 0:3ca2fd0d22ba 445 // is 12 bits per output = 1.5 bytes. There are 16 outputs per chip, which
mjr 0:3ca2fd0d22ba 446 // comes to 192 bits == 24 bytes per chip.
mjr 0:3ca2fd0d22ba 447 uint16_t spilen;
mjr 0:3ca2fd0d22ba 448
mjr 0:3ca2fd0d22ba 449 // Dirty: true means that the non-live buffer has new pending data. False means
mjr 0:3ca2fd0d22ba 450 // that the non-live buffer is empty.
mjr 0:3ca2fd0d22ba 451 volatile bool dirty;
mjr 0:3ca2fd0d22ba 452
mjr 0:3ca2fd0d22ba 453 // Enabled: this enables or disables all outputs. When this is true, we assert the
mjr 0:3ca2fd0d22ba 454 // BLANK signal continuously.
mjr 0:3ca2fd0d22ba 455 bool enabled;
mjr 0:3ca2fd0d22ba 456
mjr 0:3ca2fd0d22ba 457 // use a PWM out for the grayscale clock - this provides a stable
mjr 0:3ca2fd0d22ba 458 // square wave signal without consuming CPU
mjr 0:3ca2fd0d22ba 459 FastPWM gsclk;
mjr 0:3ca2fd0d22ba 460
mjr 0:3ca2fd0d22ba 461 // Digital out pins used for the TLC5940
mjr 0:3ca2fd0d22ba 462 DigitalOut blank;
mjr 0:3ca2fd0d22ba 463 DigitalOut xlat;
mjr 0:3ca2fd0d22ba 464
mjr 0:3ca2fd0d22ba 465 // number of daisy-chained TLC5940s we're controlling
mjr 0:3ca2fd0d22ba 466 int nchips;
mjr 0:3ca2fd0d22ba 467
mjr 0:3ca2fd0d22ba 468 // Timeout to end each PWM cycle. This is a one-shot timer that we reset
mjr 0:3ca2fd0d22ba 469 // on each cycle.
mjr 0:3ca2fd0d22ba 470 Timeout resetTimer;
mjr 0:3ca2fd0d22ba 471
mjr 0:3ca2fd0d22ba 472 // Timeout to end the data window for the PWM cycle.
mjr 0:3ca2fd0d22ba 473 Timeout windowTimer;
mjr 0:3ca2fd0d22ba 474
mjr 0:3ca2fd0d22ba 475 // "Clear To Send" flag:
mjr 0:3ca2fd0d22ba 476 volatile bool cts;
mjr 0:3ca2fd0d22ba 477
mjr 0:3ca2fd0d22ba 478 // Do we need an XLAT signal on the next blanking interval?
mjr 0:3ca2fd0d22ba 479 volatile bool needXlat;
mjr 0:3ca2fd0d22ba 480
mjr 0:3ca2fd0d22ba 481 // Reset the grayscale cycle and send the next data update
mjr 0:3ca2fd0d22ba 482 void reset()
mjr 0:3ca2fd0d22ba 483 {
mjr 0:3ca2fd0d22ba 484 // start the blanking cycle
mjr 0:3ca2fd0d22ba 485 startBlank();
mjr 0:3ca2fd0d22ba 486
mjr 0:3ca2fd0d22ba 487 // we're now clear to send the new GS data
mjr 0:3ca2fd0d22ba 488 cts = true;
mjr 0:3ca2fd0d22ba 489
mjr 0:3ca2fd0d22ba 490 #if DATA_UPDATE_INSIDE_BLANKING
mjr 0:3ca2fd0d22ba 491 // We're configured to send the new GS data inline during each
mjr 0:3ca2fd0d22ba 492 // blanking cycle. Send it now.
mjr 0:3ca2fd0d22ba 493 send();
mjr 0:3ca2fd0d22ba 494 #else
mjr 0:3ca2fd0d22ba 495 // We're configured to send GS data during the GS cycle. This means
mjr 0:3ca2fd0d22ba 496 // we can defer the GS data transmission to any point within the next
mjr 0:3ca2fd0d22ba 497 // GS cycle, which will last about 12ms (assuming a 350kHz GS clock).
mjr 0:3ca2fd0d22ba 498 // That's a ton of time given that our GS transmission only takes about
mjr 0:3ca2fd0d22ba 499 // 100us. With such a leisurely time window to work with, we can move
mjr 0:3ca2fd0d22ba 500 // the transmission out of the ISR context and into regular application
mjr 0:3ca2fd0d22ba 501 // context, which is good because it greatly reduces the time we spend
mjr 0:3ca2fd0d22ba 502 // in this ISR, which is good in turn because more ISR time means more
mjr 0:3ca2fd0d22ba 503 // latency for other interrupts and more chances to miss interrupts
mjr 0:3ca2fd0d22ba 504 // entirely.
mjr 0:3ca2fd0d22ba 505 //
mjr 0:3ca2fd0d22ba 506 // The mechanism for deferring the transmission to application context
mjr 0:3ca2fd0d22ba 507 // is simple. The main program loop periodically polls the "cts" flag
mjr 0:3ca2fd0d22ba 508 // and transmits the data if it finds "cts" set. To conform to the
mjr 0:3ca2fd0d22ba 509 // hardware spec for the TLC5940 chips, the data transmission has to
mjr 0:3ca2fd0d22ba 510 // finish before the next blanking interval. This means our time
mjr 0:3ca2fd0d22ba 511 // window to do the transmission is the 12ms of the grayscale cycle
mjr 0:3ca2fd0d22ba 512 // minus the ~100us to do the transmission. So basically 12ms.
mjr 0:3ca2fd0d22ba 513 // Timing is never exact on the KL25Z, though, so we should build in
mjr 0:3ca2fd0d22ba 514 // a little margin for error. To be conservative, we'll say that the
mjr 0:3ca2fd0d22ba 515 // update must begin within the first 2/3 of the grayscale cycle time.
mjr 0:3ca2fd0d22ba 516 // That's an 8ms window, and leaves a 4ms margin of error. It's
mjr 0:3ca2fd0d22ba 517 // almost inconceivable that any of the timing factors would be
mjr 0:3ca2fd0d22ba 518 // outside of those bounds.
mjr 0:3ca2fd0d22ba 519 //
mjr 0:3ca2fd0d22ba 520 // To coordinate this 2/3-of-a-cycle window with the main loop, set
mjr 0:3ca2fd0d22ba 521 // up a timeout to clear the "cts" flag 2/3 into the cycle time. If
mjr 0:3ca2fd0d22ba 522 // for some reason the main loop doesn't do the transmission before
mjr 0:3ca2fd0d22ba 523 // this timer fires, it'll see the "cts" flag turned off and won't
mjr 0:3ca2fd0d22ba 524 // attempt the transmission on this round. (That should essentially
mjr 0:3ca2fd0d22ba 525 // never happen, but it wouldn't be a problem if it happened even with
mjr 0:3ca2fd0d22ba 526 // some regularity, because we'd just transmit the data on the next
mjr 0:3ca2fd0d22ba 527 // cycle. Human users won't notice such a delay.)
mjr 0:3ca2fd0d22ba 528 windowTimer.attach(this, &TLC5940::closeSendWindow,
mjr 0:3ca2fd0d22ba 529 (1.0/GSCLK_SPEED)*4096.0*2.0/3.0);
mjr 0:3ca2fd0d22ba 530 #endif
mjr 0:3ca2fd0d22ba 531
mjr 0:3ca2fd0d22ba 532 // end the blanking interval
mjr 0:3ca2fd0d22ba 533 endBlank();
mjr 0:3ca2fd0d22ba 534
mjr 0:3ca2fd0d22ba 535 // re-arm the reset handler for the next blanking interval
mjr 0:3ca2fd0d22ba 536 armReset();
mjr 0:3ca2fd0d22ba 537 }
mjr 0:3ca2fd0d22ba 538
mjr 0:3ca2fd0d22ba 539 // End the data-send window. This is a timeout routine that fires halfway
mjr 0:3ca2fd0d22ba 540 // through each grayscale cycle. The TLC5940 chips allow new data to be
mjr 0:3ca2fd0d22ba 541 // sent at any time during the grayscale pulse cycle, but the transmission
mjr 0:3ca2fd0d22ba 542 // has to fit into this window. We do these transmissions from the main loop,
mjr 0:3ca2fd0d22ba 543 // so that they happen in application context rather than interrupt context,
mjr 0:3ca2fd0d22ba 544 // but this means that we have to synchronize the main loop activity to the
mjr 0:3ca2fd0d22ba 545 // grayscale timer cycle. To make sure the transmission is done before the
mjr 0:3ca2fd0d22ba 546 // next grayscale cycle ends, we only allow the transmission to start for
mjr 0:3ca2fd0d22ba 547 // the first 2/3 of the cycle. This gives us plenty of time to send the
mjr 0:3ca2fd0d22ba 548 // data and plenty of padding to make sure we don't go too late. Consider
mjr 0:3ca2fd0d22ba 549 // the relative time periods: we run the grayscale clock at 350kHz, and each
mjr 0:3ca2fd0d22ba 550 // grayscale cycle has 4096 steps, so each cycle takes 11.7ms. For the
mjr 0:3ca2fd0d22ba 551 // typical Expansion Board setup with 4 TLC5940 chips, we have 768 bits
mjr 0:3ca2fd0d22ba 552 // to send via SPI at 28 MHz, which nominally takes 27us. The actual
mjr 0:3ca2fd0d22ba 553 // measured time to send 768 bits via send() is 72us, so there's CPU overhead
mjr 0:3ca2fd0d22ba 554 // of about 2.6x. The biggest workable Expnasion Board setup would probably
mjr 0:3ca2fd0d22ba 555 // be around 8 TLC chips, so we'd have twice the bits and twice the
mjr 0:3ca2fd0d22ba 556 // transmission time of our 4-chip scenario, so the send time would be
mjr 0:3ca2fd0d22ba 557 // about 150us. 2/3 of the grayscale cycle gives us an 8ms window to
mjr 0:3ca2fd0d22ba 558 // perform a 150us operation. The main loop runs about every 1.5ms, so
mjr 0:3ca2fd0d22ba 559 // we're all but certain to poll CTS more than once during each 8ms window.
mjr 0:3ca2fd0d22ba 560 // Even if we start at the very end of the window, we still have about 3.5ms
mjr 0:3ca2fd0d22ba 561 // to finish a <150us operation, so we're all but certain to finish in time.
mjr 0:3ca2fd0d22ba 562 void closeSendWindow()
mjr 0:3ca2fd0d22ba 563 {
mjr 0:3ca2fd0d22ba 564 cts = false;
mjr 0:3ca2fd0d22ba 565 }
mjr 0:3ca2fd0d22ba 566
mjr 0:3ca2fd0d22ba 567 // arm the reset handler - this fires at the end of each GS cycle
mjr 0:3ca2fd0d22ba 568 void armReset()
mjr 0:3ca2fd0d22ba 569 {
mjr 0:3ca2fd0d22ba 570 resetTimer.attach(this, &TLC5940::reset, (1.0/GSCLK_SPEED)*4096.0);
mjr 0:3ca2fd0d22ba 571 }
mjr 0:3ca2fd0d22ba 572
mjr 0:3ca2fd0d22ba 573 void startBlank()
mjr 0:3ca2fd0d22ba 574 {
mjr 0:3ca2fd0d22ba 575 // turn off the grayscale clock, and assert BLANK to end the grayscale cycle
mjr 0:3ca2fd0d22ba 576 gsclk.write(0);
mjr 0:3ca2fd0d22ba 577 blank = (enabled ? 1 : 0); // for the slight delay (20ns) required after GSCLK goes low
mjr 0:3ca2fd0d22ba 578 blank = 1;
mjr 0:3ca2fd0d22ba 579 }
mjr 0:3ca2fd0d22ba 580
mjr 0:3ca2fd0d22ba 581 void endBlank()
mjr 0:3ca2fd0d22ba 582 {
mjr 0:3ca2fd0d22ba 583 // if we've sent new grayscale data since the last blanking
mjr 0:3ca2fd0d22ba 584 // interval, latch it by asserting XLAT
mjr 0:3ca2fd0d22ba 585 if (needXlat)
mjr 0:3ca2fd0d22ba 586 {
mjr 0:3ca2fd0d22ba 587 // latch the new data while we're still blanked
mjr 0:3ca2fd0d22ba 588 xlat = 1;
mjr 0:3ca2fd0d22ba 589 xlat = 0;
mjr 0:3ca2fd0d22ba 590 needXlat = false;
mjr 0:3ca2fd0d22ba 591 }
mjr 0:3ca2fd0d22ba 592
mjr 0:3ca2fd0d22ba 593 // End the blanking interval and restart the grayscale clock. Note
mjr 0:3ca2fd0d22ba 594 // that we keep the blanking on if the chips are globally disabled.
mjr 0:3ca2fd0d22ba 595 if (enabled)
mjr 0:3ca2fd0d22ba 596 {
mjr 0:3ca2fd0d22ba 597 blank = 0;
mjr 0:3ca2fd0d22ba 598 gsclk.write(.5);
mjr 0:3ca2fd0d22ba 599 }
mjr 0:3ca2fd0d22ba 600 }
mjr 0:3ca2fd0d22ba 601 };
mjr 0:3ca2fd0d22ba 602
mjr 0:3ca2fd0d22ba 603 #endif