An I/O controller for virtual pinball machines: accelerometer nudge sensing, analog plunger input, button input encoding, LedWiz compatible output controls, and more.
Dependencies: mbed FastIO FastPWM USBDevice
Fork of Pinscape_Controller by
TLC5940.h
00001 // Pinscape Controller TLC5940 interface 00002 // 00003 // Based on Spencer Davis's mbed TLC5940 library. Adapted for the 00004 // KL25Z and simplified (removes dot correction and status input 00005 // support). 00006 00007 00008 #ifndef TLC5940_H 00009 #define TLC5940_H 00010 00011 #include "NewPwm.h" 00012 00013 // -------------------------------------------------------------------------- 00014 // Data Transmission Mode. 00015 // 00016 // NOTE! This section contains a possible workaround to try if you're 00017 // having data signal stability problems with your TLC5940 chips. If 00018 // things are working properly, you can ignore this part. 00019 // 00020 // The software has two options for sending data updates to the chips: 00021 // 00022 // Mode 0: Send data *during* the grayscale cycle. This is the default, 00023 // and it's the standard method the chips are designed for. In this mode, 00024 // we start sending an update just after then blanking interval that starts 00025 // a new grayscale cycle. The timing is arranged so that the update is 00026 // completed well before the end of the grayscale cycle. At the next 00027 // blanking interval, we latch the new data, so the new brightness levels 00028 // will be shown starting on the next cycle. 00029 // 00030 // Mode 1: Send data *between* grayscale cycles. In this mode, we send 00031 // each complete update during a blanking period, then latch the update 00032 // and start the next grayscale cycle. This isn't the way the chips were 00033 // intended to be used, but it works. The disadvantage is that it requires 00034 // the blanking interval to be extended long enough for the full data 00035 // update (192 bits * the number of chips in the chain). Since the 00036 // outputs are turned off throughout the blanking period, this reduces 00037 // the overall brightness/intensity of the outputs by reducing the duty 00038 // cycle. The TLC5940 chips can't achieve 100% duty cycle to begin with, 00039 // since they require a brief minimum time in the blanking interval 00040 // between grayscale cycles; however, the minimum is so short that the 00041 // duty cycle is close to 100%. With the full data transmission stuffed 00042 // into the blanking interval, we reduce the duty cycle further below 00043 // 100%. With four chips in the chain, a 28 MHz data clock, and a 00044 // 500 kHz grayscale clock, the reduction is about 0.3%. 00045 // 00046 // Mode 0 is the method documented in the manufacturer's data sheet. 00047 // It works well empirically with the Pinscape expansion boards. 00048 // 00049 // So what's the point of Mode 1? In early testing, with a breadboard 00050 // setup, I saw some problems with data signal stability, which manifested 00051 // as sporadic flickering in the outputs. Switching to Mode 1 improved 00052 // the signal stability considerably. I'm therefore leaving this code 00053 // available as an option in case anyone runs into similar signal problems 00054 // and wants to try the alternative mode as a workaround. 00055 // 00056 #define DATA_UPDATE_INSIDE_BLANKING 0 00057 00058 #include "mbed.h" 00059 00060 00061 // -------------------------------------------------------------------------- 00062 // Some notes on the data transmission design 00063 // 00064 // I spent a while working on using DMA to send the data, thinking that 00065 // this would reduce the CPU load. But I couldn't get this working 00066 // reliably; there was some kind of timing interaction or race condition 00067 // that caused crashes when initiating the DMA transfer from within the 00068 // blanking interrupt. I spent quite a while trying to debug it and 00069 // couldn't figure out what was going on. There are some complications 00070 // involved in using DMA with SPI that are documented in the KL25Z 00071 // reference manual, and I was following those carefully, but I suspect 00072 // that the problem was somehow related to that, because it seemed to 00073 // be sporadic and timing-related, and I couldn't find any software race 00074 // conditions or concurrency issues that could explain it. 00075 // 00076 // I finally decided that I wasn't going to crack that and started looking 00077 // for alternatives, so out of curiosity, I measured the time needed for a 00078 // synchronous (CPU-driven) SPI send, to see how it would fit into various 00079 // places in the code. This turned out to be faster than I expected: with 00080 // SPI at 28MHz, the measured time for a synchronous send is about 72us for 00081 // 4 chips worth of GS data (192 bits), which I expect to be the typical 00082 // Expansion Board setup. For an 8-chip setup, which will probably be 00083 // about the maximum workable setup, the time would be 144us. We only have 00084 // to send the data once per grayscale cycle, and each cycle is 11.7ms with 00085 // the grayscale clock at 350kHz (4096 steps per cycle divided by 350,000 00086 // steps per second = 11.7ms per cycle), so this is only 1% overhead. The 00087 // main loop spends most of its time polling anyway, so we have plenty of 00088 // cycles to reallocate from idle polling to the sending the data. 00089 // 00090 // The easiest place to do the send is in the blanking interval ISR, but 00091 // I wanted to keep this out of the ISR. It's only ~100us, but even so, 00092 // it's critical to minimize time in ISRs so that we don't miss other 00093 // interrupts. So instead, I set it up so that the ISR coordinates with 00094 // the main loop via a flag: 00095 // 00096 // - In the blanking interrupt, set a flag ("cts" = clear to send), 00097 // and arm a timeout that fires 2/3 through the next blanking cycle 00098 // 00099 // - In the main loop, poll "cts" each time through the loop. When 00100 // cts is true, send the data synchronously and clear the flag. 00101 // Do nothing when cts is false. 00102 // 00103 // The main loop runs on about a 1.5ms cycle, and 2/3 of the grayscale 00104 // cycle is 8ms, so the main loop will poll cts on average 5 times per 00105 // 8ms window. That makes it all but certain that we'll do a send in 00106 // a timely fashion on every grayscale cycle. 00107 // 00108 // The point of the 2/3 window is to guarantee that the data send is 00109 // finished before the grayscale cycle ends. The TLC5940 chips require 00110 // this; data transmission has to be entirely between blanking intervals. 00111 // The main loop and interrupt handler are operating asynchronously 00112 // relative to one another, so the exact phase alignment will vary 00113 // randomly. If we start a transmission within the 2/3 window, we're 00114 // guaranteed to have at least 3.5ms (1/3 of the cycle) left before 00115 // the next blanking interval. The transmission only takes ~100us, 00116 // so we're leaving tons of margin for error in the timing - we have 00117 // 34x longer than we need. 00118 // 00119 // The main loop can easily absorb the extra ~100us of overhead without 00120 // even noticing. The loop spends most of its time polling devices, so 00121 // it's really mostly idle time to start with. So we're effectively 00122 // reallocating some idle time to useful work. The chunk of time is 00123 // only about 6% of one loop iteration, so we're not even significantly 00124 // extending the occasional iterations that actually do this work. 00125 // (If we had a 2ms chunk of monolithic work to do, that could start 00126 // to add undesirable latency to other polling tasks. 100us won't.) 00127 // 00128 // We could conceivably reduce this overhead slightly by adding DMA, 00129 // but I'm not sure it would actually do much good. Setting up the DMA 00130 // transfer would probably take at least 20us in CPU time just to set 00131 // up all of the registers. And SPI is so fast that the DMA transfer 00132 // would saturate the CPU memory bus for the 30us or so of the transfer. 00133 // (I have my suspicions that this bus saturation effect might be part 00134 // of the problem I was having getting DMA working in the first place.) 00135 // So we'd go from 100us of overhead per cycle to at maybe 50us per 00136 // cycle. We'd also have to introduce some concurrency controls to the 00137 // output "set" operation that we don't need with the current scheme 00138 // (because it's synchronous). So overall I think the current 00139 // synchronous approach is almost as good in terms of performance as 00140 // an asynchronous DMA setup would be, and it's a heck of a lot simpler 00141 // and seems very reliable. 00142 // 00143 // -------------------------------------------------------------------------- 00144 00145 00146 /** 00147 * SPI speed used by the mbed to communicate with the TLC5940 00148 * The TLC5940 supports up to 30Mhz. It's best to keep this as 00149 * high as possible, since a higher SPI speed yields a faster 00150 * grayscale data update. However, I've seen some slight 00151 * instability in the signal in my breadboard setup using the 00152 * full 30MHz, so I've reduced this slightly, which seems to 00153 * yield a solid signal. The limit will vary according to how 00154 * clean the signal path is to the chips; you can probably crank 00155 * this up to full speed if you have a well-designed PCB, good 00156 * decoupling capacitors near the 5940 VCC/GND pins, and short 00157 * wires between the KL25Z and the PCB. A short, clean path to 00158 * KL25Z ground seems especially important. 00159 * 00160 * The SPI clock must be fast enough that the data transmission 00161 * time for a full update is comfortably less than the blanking 00162 * cycle time. The grayscale refresh requires 192 bits per TLC5940 00163 * in the daisy chain, and each bit takes one SPI clock to send. 00164 * Our reference setup in the Pinscape controller allows for up to 00165 * 4 TLC5940s, so a full refresh cycle on a fully populated system 00166 * would be 768 SPI clocks. The blanking cycle is 4096 GSCLK cycles. 00167 * 00168 * t(blank) = 4096 * 1/GSCLK_SPEED 00169 * t(refresh) = 768 * 1/SPI_SPEED 00170 * Therefore: SPI_SPEED must be > 768/4096 * GSCLK_SPEED 00171 * 00172 * Since the SPI speed can be so high, and since we want to keep 00173 * the GSCLK speed relatively low, the constraint above simply 00174 * isn't a factor. E.g., at SPI=30MHz and GSCLK=500kHz, 00175 * t(blank) is 8192us and t(refresh) is 25us. 00176 */ 00177 #define SPI_SPEED 28000000 00178 00179 /** 00180 * The rate at which the GSCLK pin is pulsed. This also controls 00181 * how often the reset function is called. The reset function call 00182 * interval in seconds is (4096/GSCLK_SPEED). The maximum reliable 00183 * rate is around 32Mhz. It's best to keep this rate as low as 00184 * possible: the higher the rate, the higher the refresh() call 00185 * frequency, so the higher the CPU load. Higher frequencies also 00186 * make it more challenging to wire the chips for clean signal 00187 * transmission. Lower clock speeds are more forgiving of wiring 00188 * quality. 00189 * 00190 * The lower bound depends on the application. For driving lights, 00191 * the limiting factor is flicker: the lower the rate, the more 00192 * noticeable the flicker. Incandescents tend to look flicker-free 00193 * at about 50 Hz (205 kHz grayscale clock). LEDs need significantly 00194 * faster rates than incandescents, since they don't have the thermal 00195 * lag of incandescents; for flicker-free LEDs, you usually need at 00196 * least 200Hz (GSCLK_SPEED 819200). 00197 */ 00198 #define GSCLK_SPEED 350000 00199 00200 class TLC5940 00201 { 00202 public: 00203 /** 00204 * Set up the TLC5940 00205 * 00206 * @param SCLK - The SCK pin of the SPI bus 00207 * @param MOSI - The MOSI pin of the SPI bus 00208 * @param GSCLK - The GSCLK pin of the TLC5940(s) 00209 * @param BLANK - The BLANK pin of the TLC5940(s) 00210 * @param XLAT - The XLAT pin of the TLC5940(s) 00211 * @param nchips - The number of TLC5940s (if you are daisy chaining) 00212 */ 00213 TLC5940(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, PinName XLAT, int nchips) 00214 : spi(MOSI, NC, SCLK), 00215 gsclk(GSCLK), 00216 blank(BLANK, 1), 00217 xlat(XLAT), 00218 nchips(nchips) 00219 { 00220 // start up initially disabled 00221 enabled = false; 00222 00223 // set XLAT to initially off 00224 xlat = 0; 00225 00226 // Assert BLANK while starting up, to keep the outputs turned off until 00227 // everything is stable. This helps prevent spurious flashes during startup. 00228 // (That's not particularly important for lights, but it matters more for 00229 // tactile devices. It's a bit alarming to fire a replay knocker on every 00230 // power-on, for example.) 00231 blank = 1; 00232 00233 // Configure SPI format and speed. The KL25Z only supports 8-bit mode. 00234 // We nominally need to write the data in 12-bit chunks for the TLC5940 00235 // grayscale levels, but SPI is ultimately just a bit-level serial format, 00236 // so we can reformat the 12-bit blocks into 8-bit bytes to fit the 00237 // KL25Z's limits. This should work equally well on other microcontrollers 00238 // that are more flexible. The TLC5940 requires polarity/phase format 0. 00239 spi.format(8, 0); 00240 spi.frequency(SPI_SPEED); 00241 00242 // Send out a full data set to the chips, to clear out any random 00243 // startup data from the registers. Include some extra bits - there 00244 // are some cases (such as after sending dot correct commands) where 00245 // an extra bit per chip is required, and the initial state is 00246 // unpredictable, so send extra bits to make sure we cover all bases. 00247 // This does no harm; extra bits just fall off the end of the daisy 00248 // chain, and since we want all registers initially set to 0, we can 00249 // send arbitrarily many extra 0's. 00250 for (int i = 0 ; i < nchips*25 ; ++i) 00251 spi.write(0x00); 00252 00253 // do an initial XLAT to latch all of these "0" values into the 00254 // grayscale registers 00255 xlat = 1; 00256 xlat = 0; 00257 00258 // Allocate our SPI buffer. The transfer on each cycle is 192 bits per 00259 // chip = 24 bytes per chip. 00260 spilen = nchips*24; 00261 spibuf = new uint8_t[spilen]; 00262 memset(spibuf, 0x00, spilen); 00263 00264 // Configure the GSCLK output's frequency 00265 gsclk.getUnit()->period(1.0f/GSCLK_SPEED); 00266 00267 // we're not yet ready to send new data to the chips 00268 cts = false; 00269 00270 // we don't need an XLAT signal until we send data 00271 needXlat = false; 00272 } 00273 00274 // Global enable/disble. When disabled, we assert the blanking signal 00275 // continuously to keep all outputs turned off. This can be used during 00276 // startup and sleep mode to prevent spurious output signals from 00277 // uninitialized grayscale registers. The chips have random values in 00278 // their internal registers when power is first applied, so we have to 00279 // explicitly send the initial zero levels after power cycling the chips. 00280 // The chips might not have power even when the KL25Z is running, because 00281 // they might be powered from a separate power supply from the KL25Z 00282 // (the Pinscape Expansion Boards work this way). Global blanking helps 00283 // us start up more cleanly by suppressing all outputs until we can be 00284 // reasonably sure that the various chip registers are initialized. 00285 void enable(bool f) 00286 { 00287 // note the new setting 00288 enabled = f; 00289 00290 // If disabled, apply blanking immediately. If enabled, do nothing 00291 // extra; we'll drop the blanking signal at the end of the next 00292 // blanking interval as normal. 00293 if (!f) 00294 { 00295 // disable interrupts, since the blanking interrupt writes gsclk too 00296 __disable_irq(); 00297 00298 // turn off the GS clock and assert BLANK to turn off all outputs 00299 gsclk.glitchFreeWrite(0); 00300 wait_us(3); 00301 blank = 1; 00302 00303 // done messing with shared data 00304 __enable_irq(); 00305 } 00306 00307 } 00308 00309 // Start the clock running 00310 void start() 00311 { 00312 // Set up the first call to the reset function, which asserts BLANK to 00313 // end the PWM cycle and handles new grayscale data output and latching. 00314 // The original version of this library used a timer to call reset 00315 // periodically, but that approach is somewhat problematic because the 00316 // reset function itself takes a small amount of time to run, so the 00317 // *actual* cycle is slightly longer than what we get from counting 00318 // GS clocks. Running reset on a timer therefore causes the calls to 00319 // slip out of phase with the actual full cycles, which causes 00320 // premature blanking that shows up as visible flicker. To get the 00321 // reset cycle to line up more precisely with a full PWM cycle, it 00322 // works better to set up a new timer at the end of each cycle. That 00323 // organically accounts for the time spent in the interrupt handler. 00324 // This doesn't result in perfectly uniform timing, since interrupt 00325 // latency varies slightly on each interrupt, but it does guarantee 00326 // that the blanking will never be premature - all variation will go 00327 // into the tail end of the cycle after the 4096 GS clocks. That 00328 // might cause some brightness variation, but it won't cause flicker, 00329 // and in practice any brightness variation from this seems to be too 00330 // small to be visible. 00331 armReset(); 00332 } 00333 00334 // stop the timer 00335 void stop() 00336 { 00337 disarmReset(); 00338 } 00339 00340 /* 00341 * Set an output. 'idx' is the output index: 0 is OUT0 on the first 00342 * chip, 1 is OUT1 on the first chip, 16 is OUT0 on the second chip 00343 * in the daisy chain, etc. 'data' is the brightness value for the 00344 * output, 0=off, 4095=full brightness. 00345 */ 00346 void set(int idx, unsigned short data) 00347 { 00348 // validate the index 00349 if (idx >= 0 && idx < nchips*16) 00350 { 00351 #if DATA_UPDATE_INSIDE_BLANKING 00352 // If we send data within the blanking interval, turn off interrupts while 00353 // modifying the buffer, since the send happens in the interrupt handler. 00354 __disable_irq(); 00355 #endif 00356 00357 // Figure the SPI buffer location of the output we're changing. The SPI 00358 // buffer has the packed bit format that we send across the wire, with 12 00359 // bits per output, arranged from last output to first output (N = number 00360 // of outputs = nchips*16): 00361 // 00362 // byte 0 = high 8 bits of output N-1 00363 // 1 = low 4 bits of output N-1 | high 4 bits of output N-2 00364 // 2 = low 8 bits of N-2 00365 // 3 = high 8 bits of N-3 00366 // 4 = low 4 bits of N-3 | high 4 bits of N-2 00367 // 5 = low 8bits of N-4 00368 // ... 00369 // 24*nchips-3 = high 8 bits of output 1 00370 // 24*nchips-2 = low 4 bits of output 1 | high 4 bits of output 0 00371 // 24*nchips-1 = low 8 bits of output 0 00372 // 00373 // So this update will affect two bytes. If the output number if even, we're 00374 // in the high 4 + low 8 pair; if odd, we're in the high 8 + low 4 pair. 00375 int di = nchips*24 - 3 - (3*(idx/2)); 00376 if (idx & 1) 00377 { 00378 // ODD = high 8 | low 4 00379 spibuf[di] = uint8_t((data >> 4) & 0xff); 00380 spibuf[di+1] &= 0x0F; 00381 spibuf[di+1] |= uint8_t((data << 4) & 0xf0); 00382 } 00383 else 00384 { 00385 // EVEN = high 4 | low 8 00386 spibuf[di+1] &= 0xF0; 00387 spibuf[di+1] |= uint8_t((data >> 8) & 0x0f); 00388 spibuf[di+2] = uint8_t(data & 0xff); 00389 } 00390 00391 #if DATA_UPDATE_INSIDE_BLANKING 00392 // re-enable interrupts 00393 __enable_irq(); 00394 #endif 00395 } 00396 } 00397 00398 // Send updates if ready. Our top-level program's main loop calls this on 00399 // every iteration. This lets us send grayscale updates to the chips in 00400 // regular application context (rather than in interrupt context), to keep 00401 // the time in the ISR as short as possible. We return immediately if 00402 // we're not within the update window or we've already sent updates for 00403 // the current cycle. 00404 void send() 00405 { 00406 // if we're in the transmission window, send the data 00407 if (cts) 00408 { 00409 // Write the data to the SPI port. Note that we go directly 00410 // to the hardware registers rather than using the mbed SPI 00411 // class, because this makes the operation about 50% faster. 00412 // The mbed class checks for input on every byte in case the 00413 // SPI connection is bidirectional, but for this application 00414 // it's strictly one-way, so we can skip checking for input 00415 // and just blast bits to the output register as fast as 00416 // it'll take them. Before writing the output register 00417 // ("D"), we have to check the status register ("S") and see 00418 // that the Transmit Empty Flag (SPTEF) is set. The 00419 // procedure is: spin until SPTEF is set in "S", write the 00420 // next byte to "D", loop until out of bytes. 00421 uint8_t *p = spibuf; 00422 for (int i = spilen ; i > 0 ; --i) { 00423 while (!(SPI0->S & SPI_S_SPTEF_MASK)) ; 00424 SPI0->D = *p++; 00425 } 00426 00427 // we've sent new data, so we need an XLAT signal to latch it 00428 needXlat = true; 00429 00430 // done - we don't need to send again until the next GS cycle 00431 cts = false; 00432 } 00433 } 00434 00435 private: 00436 // SPI port. This is master mode, output only, so we only assign the MOSI 00437 // and SCK pins. 00438 SPI spi; 00439 00440 // SPI transfer buffer. This contains the live grayscale data, formatted 00441 // for direct transmission to the TLC5940 chips via SPI. 00442 uint8_t *volatile spibuf; 00443 00444 // Length of the SPI buffer in bytes. The native data format of the chips 00445 // is 12 bits per output = 1.5 bytes. There are 16 outputs per chip, which 00446 // comes to 192 bits == 24 bytes per chip. 00447 uint16_t spilen; 00448 00449 // Dirty: true means that the non-live buffer has new pending data. False means 00450 // that the non-live buffer is empty. 00451 volatile bool dirty; 00452 00453 // Enabled: this enables or disables all outputs. When this is true, we assert the 00454 // BLANK signal continuously. 00455 bool enabled; 00456 00457 // use a PWM out for the grayscale clock - this provides a stable 00458 // square wave signal without consuming CPU 00459 NewPwmOut gsclk; 00460 00461 // Digital out pins used for the TLC5940 00462 DigitalOut blank; 00463 DigitalOut xlat; 00464 00465 // number of daisy-chained TLC5940s we're controlling 00466 int nchips; 00467 00468 // Timeout to end each PWM cycle. This is a one-shot timer that we reset 00469 // on each cycle. 00470 Timeout resetTimer; 00471 00472 // Timeout to end the data window for the PWM cycle. 00473 Timeout windowTimer; 00474 00475 // "Clear To Send" flag: 00476 volatile bool cts; 00477 00478 // Do we need an XLAT signal on the next blanking interval? 00479 volatile bool needXlat; 00480 00481 // Reset the grayscale cycle and send the next data update 00482 void reset() 00483 { 00484 // start the blanking cycle 00485 startBlank(); 00486 00487 // we're now clear to send the new GS data 00488 cts = true; 00489 00490 #if DATA_UPDATE_INSIDE_BLANKING 00491 // We're configured to send the new GS data inline during each 00492 // blanking cycle. Send it now. 00493 send(); 00494 #else 00495 // We're configured to send GS data during the GS cycle. This means 00496 // we can defer the GS data transmission to any point within the next 00497 // GS cycle, which will last about 12ms (assuming a 350kHz GS clock). 00498 // That's a ton of time given that our GS transmission only takes about 00499 // 100us. With such a leisurely time window to work with, we can move 00500 // the transmission out of the ISR context and into regular application 00501 // context, which is good because it greatly reduces the time we spend 00502 // in this ISR, which is good in turn because more ISR time means more 00503 // latency for other interrupts and more chances to miss interrupts 00504 // entirely. 00505 // 00506 // The mechanism for deferring the transmission to application context 00507 // is simple. The main program loop periodically polls the "cts" flag 00508 // and transmits the data if it finds "cts" set. To conform to the 00509 // hardware spec for the TLC5940 chips, the data transmission has to 00510 // finish before the next blanking interval. This means our time 00511 // window to do the transmission is the 12ms of the grayscale cycle 00512 // minus the ~100us to do the transmission. So basically 12ms. 00513 // Timing is never exact on the KL25Z, though, so we should build in 00514 // a little margin for error. To be conservative, we'll say that the 00515 // update must begin within the first 2/3 of the grayscale cycle time. 00516 // That's an 8ms window, and leaves a 4ms margin of error. It's 00517 // almost inconceivable that any of the timing factors would be 00518 // outside of those bounds. 00519 // 00520 // To coordinate this 2/3-of-a-cycle window with the main loop, set 00521 // up a timeout to clear the "cts" flag 2/3 into the cycle time. If 00522 // for some reason the main loop doesn't do the transmission before 00523 // this timer fires, it'll see the "cts" flag turned off and won't 00524 // attempt the transmission on this round. (That should essentially 00525 // never happen, but it wouldn't be a problem even if it happened with 00526 // some regularity, because we'd just transmit the data on the next 00527 // cycle.) 00528 windowTimer.attach_us(this, &TLC5940::closeSendWindow, 00529 uint32_t((1.0f/GSCLK_SPEED)*4096.0f*2.0f/3.0f*1000000.0f)); 00530 #endif 00531 00532 // end the blanking interval 00533 endBlank(); 00534 00535 // re-arm the reset handler for the next blanking interval 00536 armReset(); 00537 } 00538 00539 // End the data-send window. This is a timeout routine that fires halfway 00540 // through each grayscale cycle. The TLC5940 chips allow new data to be 00541 // sent at any time during the grayscale pulse cycle, but the transmission 00542 // has to fit into this window. We do these transmissions from the main loop, 00543 // so that they happen in application context rather than interrupt context, 00544 // but this means that we have to synchronize the main loop activity to the 00545 // grayscale timer cycle. To make sure the transmission is done before the 00546 // next grayscale cycle ends, we only allow the transmission to start for 00547 // the first 2/3 of the cycle. This gives us plenty of time to send the 00548 // data and plenty of padding to make sure we don't go too late. Consider 00549 // the relative time periods: we run the grayscale clock at 350kHz, and each 00550 // grayscale cycle has 4096 steps, so each cycle takes 11.7ms. For the 00551 // typical Expansion Board setup with 4 TLC5940 chips, we have 768 bits 00552 // to send via SPI at 28 MHz, which nominally takes 27us. The actual 00553 // measured time to send 768 bits via send() is 72us, so there's CPU overhead 00554 // of about 2.6x. The biggest workable Expnasion Board setup would probably 00555 // be around 8 TLC chips, so we'd have twice the bits and twice the 00556 // transmission time of our 4-chip scenario, so the send time would be 00557 // about 150us. 2/3 of the grayscale cycle gives us an 8ms window to 00558 // perform a 150us operation. The main loop runs about every 1.5ms, so 00559 // we're all but certain to poll CTS more than once during each 8ms window. 00560 // Even if we start at the very end of the window, we still have about 3.5ms 00561 // to finish a <150us operation, so we're all but certain to finish in time. 00562 void closeSendWindow() 00563 { 00564 cts = false; 00565 } 00566 00567 // arm the reset handler - this fires at the end of each GS cycle 00568 void armReset() 00569 { 00570 resetTimer.attach_us(this, &TLC5940::reset, 00571 uint32_t((1.0/GSCLK_SPEED)*4096.0*1000000.0f)); 00572 } 00573 00574 void disarmReset() 00575 { 00576 resetTimer.detach(); 00577 } 00578 00579 void startBlank() 00580 { 00581 // turn off the grayscale clock 00582 gsclk.glitchFreeWrite(0); 00583 00584 // Make sure the gsclk cycle ends, since the TLC5940 data sheet 00585 // says we can't take BLANK high until GSCLK has been low for 20ns. 00586 // (We don't have to add any padding for the 20ns, since it'll take 00587 // at least one CPU cycle of 60ns to return from waitEndCycle(). 00588 // That routine won't return until GSCLK is low, so it will have 00589 // low for at least 60ns by the time we get back from this call.) 00590 gsclk.waitEndCycle(); 00591 00592 // assert BLANK to end the grayscale cycle 00593 blank = 1; 00594 } 00595 00596 void endBlank() 00597 { 00598 // if we've sent new grayscale data since the last blanking 00599 // interval, latch it by asserting XLAT 00600 if (needXlat) 00601 { 00602 // latch the new data while we're still blanked 00603 xlat = 1; 00604 xlat = 0; 00605 needXlat = false; 00606 } 00607 00608 // End the blanking interval and restart the grayscale clock. Note 00609 // that we keep the blanking on if the chips are globally disabled. 00610 if (enabled) 00611 { 00612 blank = 0; 00613 gsclk.write(.5); 00614 } 00615 } 00616 }; 00617 00618 #endif
Generated on Wed Jul 13 2022 03:30:11 by 1.7.2