An input/output controller for virtual pinball machines, with plunger position tracking, accelerometer-based nudge sensing, button input encoding, and feedback device control.

Dependencies:   USBDevice mbed FastAnalogIn FastIO FastPWM SimpleDMA

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers TLC5940.h Source File

TLC5940.h

00001 // Pinscape Controller TLC5940 interface
00002 //
00003 // Based on Spencer Davis's mbed TLC5940 library.  Adapted for the
00004 // KL25Z, and simplified to just the functions needed for this
00005 // application.  In particular, this version doesn't include support 
00006 // for dot correction programming or status input.  This version also
00007 // uses a different approach for sending the grayscale data updates,
00008 // sending updates during the blanking interval rather than overlapping
00009 // them with the PWM cycle.  This results in very slightly longer 
00010 // blanking intervals when updates are pending, effectively reducing 
00011 // the PWM "on" duty cycle (and thus the output brightness) by about 
00012 // 0.3%.  This shouldn't be perceptible to users, so it's a small
00013 // trade-off for the advantage gained, which is much better signal 
00014 // stability when using multiple TLC5940s daisy-chained together.
00015 // I saw a lot of instability when using the overlapped approach,
00016 // which seems to be eliminated entirely when sending updates during
00017 // the blanking interval.
00018 
00019  
00020 #ifndef TLC5940_H
00021 #define TLC5940_H
00022 
00023 // Should we do the grayscale update within the blanking interval?
00024 // If this is set to 1, we'll send grayscale data during the blanking
00025 // interval; if 0, we'll send grayscale during the PWM cycle.
00026 // Mode 0 is the *intended* way of using these chips, but mode 1
00027 // produces a more stable signal in my test setup.
00028 //
00029 // In my breadboard testing, using the standard data-during-PWM
00030 // mode causes some amount of signal instability with multiple
00031 // daisy-chained TLC5940's.  It appears that there's some signal
00032 // interference (maybe RF or electrical ringing in the wires) that
00033 // can make the bit data and/or clock prone to noise that causes
00034 // random bits to propagate down the daisy chain.  This happens
00035 // frequently enough in my breadboard setup to be visible as
00036 // regular flicker.  Careful wiring, short wire runs, and decoupling
00037 // capacitors noticeably improve it, but I haven't been able to 
00038 // eliminate it entirely in my test setup.  Using the data-during-
00039 // blanking mode, however, *does* eliminate it entirely.
00040 //
00041 // It clearly should be possible to eliminate the signal problems
00042 // in a well-designed PCB layout, but for the time being, I'm
00043 // making data-during-blanking the default, since it provides
00044 // such a noticeable improvement in my test setup, and the cost
00045 // is minimal.  The cost is that it lengthens the blanking interval
00046 // slightly.  With four chips and the SPI clock at 28MHz, the 
00047 // full data update takes 27us; with the PWM clock at 500kHz, the 
00048 // grayscale cycle is 8192us.  This means that the 27us data send 
00049 // keeps the BLANK asserted for an additional 0.3% of the cycle 
00050 // time, which in term reduces output brightness by the same amount.
00051 // This brightness reduction isn't noticeable on its own, but it
00052 // can be seen as a flicker on data cycles if we send data on
00053 // some blanking cycles but not on others.  To eliminate the
00054 // flicker, the code sends a data update on *every* cycle when
00055 // using this mode to ensure that the 0.3% brightness reduction
00056 // is uniform across time.
00057 //
00058 // When using this code with TLC5940 chips on a PCB, I recommend
00059 // doing a test: set this to 0, run the board, turn on all outputs
00060 // (connected to LEDs), and observe the results.  If you don't
00061 // see any randomness or flicker in a minute or two of observation,
00062 // you're getting a good clean signal throughout the daisy chain
00063 // and don't need the workaround.  If you do see any instability, 
00064 // set this back to 1.
00065 #define DATA_UPDATE_INSIDE_BLANKING  1
00066 
00067 #include "mbed.h"
00068 #include "FastPWM.h"
00069 #include "SimpleDMA.h"
00070 
00071 /**
00072   * SPI speed used by the mbed to communicate with the TLC5940
00073   * The TLC5940 supports up to 30Mhz.  It's best to keep this as
00074   * high as possible, since a higher SPI speed yields a faster 
00075   * grayscale data update.  However, I've seen some slight
00076   * instability in the signal in my breadboard setup using the
00077   * full 30MHz, so I've reduced this slightly, which seems to
00078   * yield a solid signal.  The limit will vary according to how
00079   * clean the signal path is to the chips; you can probably crank
00080   * this up to full speed if you have a well-designed PCB, good
00081   * decoupling capacitors near the 5940 VCC/GND pins, and short
00082   * wires between the KL25Z and the PCB.  A short, clean path to
00083   * KL25Z ground seems especially important.
00084   *
00085   * The SPI clock must be fast enough that the data transmission
00086   * time for a full update is comfortably less than the blanking 
00087   * cycle time.  The grayscale refresh requires 192 bits per TLC5940 
00088   * in the daisy chain, and each bit takes one SPI clock to send.  
00089   * Our reference setup in the Pinscape controller allows for up to 
00090   * 4 TLC5940s, so a full refresh cycle on a fully populated system 
00091   * would be 768 SPI clocks.  The blanking cycle is 4096 GSCLK cycles.  
00092   *
00093   *   t(blank) = 4096 * 1/GSCLK_SPEED
00094   *   t(refresh) = 768 * 1/SPI_SPEED
00095   *   Therefore:  SPI_SPEED must be > 768/4096 * GSCLK_SPEED
00096   *
00097   * Since the SPI speed can be so high, and since we want to keep
00098   * the GSCLK speed relatively low, the constraint above simply
00099   * isn't a factor.  E.g., at SPI=30MHz and GSCLK=500kHz, 
00100   * t(blank) is 8192us and t(refresh) is 25us.
00101   */
00102 #define SPI_SPEED 2800000
00103 
00104 /**
00105   * The rate at which the GSCLK pin is pulsed.   This also controls 
00106   * how often the reset function is called.   The reset function call
00107   * rate is (1/GSCLK_SPEED) * 4096.  The maximum reliable rate is
00108   * around 32Mhz.  It's best to keep this rate as low as possible:
00109   * the higher the rate, the higher the refresh() call frequency,
00110   * so the higher the CPU load.
00111   *
00112   * The lower bound is probably dependent on the application.  For 
00113   * driving LEDs, the limiting factor is that lower rates will increase
00114   * visible flicker.  200 kHz seems to be a good lower bound for LEDs.  
00115   * That provides about 48 cycles per second - that's about the same as
00116   * the 50 Hz A/C cycle rate in many countries, which was itself chosen
00117   * so that incandescent lights don't flicker.  (This rate is a function 
00118   * of human eye physiology, which has its own refresh cycle of sorts
00119   * that runs at about 50 Hz.  If you're designing an LED system for
00120   * viewing by cats or drosophila, you might want to look into your
00121   * target species' eye physiology, since the persistence of vision
00122   * rate varies quite a bit from species to species.)  Flicker tends to 
00123   * be more noticeable in LEDs than in incandescents, since LEDs don't
00124   * have the thermal inertia of incandescents, so we use a slightly
00125   * higher default here.  500 kHz = 122 full grayscale cycles per
00126   * second = 122 reset calls per second (call every 8ms).
00127   */
00128 #define GSCLK_SPEED    500000
00129 
00130 /**
00131   *  This class controls a TLC5940 PWM driver IC.
00132   *
00133   *  Using the TLC5940 class to control an LED:
00134   *  @code
00135   *  #include "mbed.h"
00136   *  #include "TLC5940.h"
00137   *  
00138   *  // Create the TLC5940 instance
00139   *  TLC5940 tlc(p7, p5, p21, p9, p10, p11, p12, 1);
00140   *  
00141   *  int main()
00142   *  {   
00143   *      // Enable the first LED
00144   *      tlc.set(0, 0xfff);
00145   *      
00146   *      while(1)
00147   *      {
00148   *      }
00149   *  }
00150   *  @endcode
00151   */
00152 class TLC5940
00153 {
00154 public:
00155     /**
00156       *  Set up the TLC5940
00157       *  @param SCLK - The SCK pin of the SPI bus
00158       *  @param MOSI - The MOSI pin of the SPI bus
00159       *  @param GSCLK - The GSCLK pin of the TLC5940(s)
00160       *  @param BLANK - The BLANK pin of the TLC5940(s)
00161       *  @param XLAT - The XLAT pin of the TLC5940(s)
00162       *  @param nchips - The number of TLC5940s (if you are daisy chaining)
00163       */
00164     TLC5940(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, PinName XLAT, int nchips)
00165         : spi(MOSI, NC, SCLK),
00166           gsclk(GSCLK),
00167           blank(BLANK),
00168           xlat(XLAT),
00169           nchips(nchips)
00170     {
00171         // set XLAT to initially off
00172         xlat = 0;
00173         
00174         // Assert BLANK while starting up, to keep the outputs turned off until
00175         // everything is stable.  This helps prevent spurious flashes during startup.
00176         // (That's not particularly important for lights, but it matters more for
00177         // tactile devices.  It's a bit alarming to fire a replay knocker on every
00178         // power-on, for example.)
00179         blank = 1;
00180         
00181         // allocate the grayscale buffer, and set all outputs to fully off
00182         gs = new unsigned short[nchips*16];
00183         memset(gs, 0, nchips*16*sizeof(gs[0]));
00184         
00185         // Configure SPI format and speed.  Note that KL25Z ONLY supports 8-bit
00186         // mode.  The TLC5940 nominally requires 12-bit data blocks for the
00187         // grayscale levels, but SPI is ultimately just a bit-level serial format,
00188         // so we can reformat the 12-bit blocks into 8-bit bytes to fit the 
00189         // KL25Z's limits.  This should work equally well on other microcontrollers 
00190         // that are more flexible.  The TLC5940 appears to require polarity/phase
00191         // format 0.
00192         spi.format(8, 0);
00193         spi.frequency(SPI_SPEED);
00194         
00195         // Send out a full data set to the chips, to clear out any random
00196         // startup data from the registers.  Include some extra bits - there
00197         // are some cases (such as after sending dot correct commands) where
00198         // an extra bit per chip is required, and the initial state is 
00199         // somewhat unpredictable, so send extra just to make sure we cover
00200         // all bases.  This does no harm; extra bits just fall off the end of
00201         // the daisy chain, and since we want all registers set to 0, we can
00202         // send arbitrarily many extra 0's.
00203         for (int i = 0 ; i < nchips*25 ; ++i)
00204             spi.write(0);
00205             
00206         // do an initial XLAT to latch all of these "0" values into the
00207         // grayscale registers
00208         xlat = 1;
00209         xlat = 0;
00210 
00211         // Allocate a DMA buffer.  The transfer on each cycle is 192 bits per
00212         // chip = 24 bytes per chip.
00213         dmabuf = new char[nchips*24];
00214         
00215         // Set up the Simple DMA interface object.  We use the DMA controller to
00216         // send grayscale data updates to the TLC5940 chips.  This lets the CPU
00217         // keep running other tasks while we send gs updates, and importantly
00218         // allows our blanking interrupt handler return almost immediately.
00219         // The DMA transfer is from our internal DMA buffer to SPI0, which is
00220         // the SPI controller physically connected to the TLC5940s.
00221         sdma.source(dmabuf, 1);
00222         sdma.destination(&(SPI0->D), 0, 8);
00223         sdma.trigger(Trigger_SPI0_TX);
00224         sdma.attach(this, &TLC5940::dmaDone);
00225         
00226         // Enable DMA on SPI0.  SimpleDMA doesn't do this for us; we have to
00227         // do it explicitly.  This is just a matter of setting bit 5 (TXDMAE)
00228         // in the SPI controllers Control Register 2 (C2).
00229         SPI0->C2 |= 0x20; // set bit 5 = 0x20 = TXDMAE in SPI0 control register 2
00230 
00231         // Configure the GSCLK output's frequency
00232         gsclk.period(1.0/GSCLK_SPEED);
00233         
00234         // mark that we need an initial update
00235         newGSData = true;
00236         needXlat = false;
00237      }
00238     
00239     // Start the clock running
00240     void start()
00241     {        
00242         // Set up the first call to the reset function, which asserts BLANK to
00243         // end the PWM cycle and handles new grayscale data output and latching.
00244         // The original version of this library uses a timer to call reset
00245         // periodically, but that approach is somewhat problematic because the
00246         // reset function itself takes a small amount of time to run, so the
00247         // *actual* cycle is slightly longer than what we get from counting
00248         // GS clocks.  Running reset on a timer therefore causes the calls to
00249         // slip out of phase with the actual full cycles, which causes 
00250         // premature blanking that shows up as visible flicker.  To get the
00251         // reset cycle to line up exactly with a full PWM cycle, it works
00252         // better to set up a new timer on each cycle, *after* we've finished
00253         // with the somewhat unpredictable overhead of the interrupt handler.
00254         // This ensures that we'll get much closer to exact alignment of the
00255         // cycle phase, and in any case the worst that happens is that some
00256         // cycles are very slightly too long or short (due to imperfections
00257         // in the timer clock vs the PWM clock that determines the GSCLCK
00258         // output to the TLC5940), which is far less noticeable than a 
00259         // constantly rotating phase misalignment.
00260         reset_timer.attach(this, &TLC5940::reset, (1.0/GSCLK_SPEED)*4096.0);
00261     }
00262     
00263     ~TLC5940()
00264     {
00265         delete [] gs;
00266         delete [] dmabuf;
00267     }
00268 
00269     /**
00270       *  Set the next chunk of grayscale data to be sent
00271       *  @param data - Array of 16 bit shorts containing 16 12 bit grayscale data chunks per TLC5940
00272       *  @note These must be in intervals of at least (1/GSCLK_SPEED) * 4096 to be sent
00273       */
00274     void set(int idx, unsigned short data) 
00275     {
00276         // store the data, and flag the pending update for the interrupt handler to carry out
00277         gs[idx] = data; 
00278         newGSData = true;
00279     }
00280 
00281 private:
00282     // current level for each output
00283     unsigned short *gs;
00284     
00285     // Simple DMA interface object
00286     SimpleDMA sdma;
00287 
00288     // DMA transfer buffer.  Each time we have data to transmit to the TLC5940 chips,
00289     // we format the data into this buffer exactly as it will go across the wire, then
00290     // hand the buffer to the DMA controller to move through the SPI port.
00291     char *dmabuf;
00292     
00293     // SPI port - only MOSI and SCK are used
00294     SPI spi;
00295 
00296     // use a PWM out for the grayscale clock - this provides a stable
00297     // square wave signal without consuming CPU
00298     FastPWM gsclk;
00299 
00300     // Digital out pins used for the TLC5940
00301     DigitalOut blank;
00302     DigitalOut xlat;
00303     
00304     // number of daisy-chained TLC5940s we're controlling
00305     int nchips;
00306 
00307     // Timeout to end each PWM cycle.  This is a one-shot timer that we reset
00308     // on each cycle.
00309     Timeout reset_timer;
00310     
00311     // Has new GS/DC data been loaded?
00312     volatile bool newGSData;
00313     
00314     // Do we need an XLAT signal on the next blanking interval?
00315     volatile bool needXlat;
00316 
00317     // Function to reset the display and send the next chunks of data
00318     void reset()
00319     {
00320         // start the blanking cycle
00321         startBlank();
00322         
00323 #if DATA_UPDATE_INSIDE_BLANKING
00324         // We're configured to send the new GS data entirely within
00325         // the blanking interval.  Start the DMA transfer now, and
00326         // return without ending the blanking interval.  The DMA
00327         // completion interrupt handler will do that when the data
00328         // update has completed.  
00329         //
00330         // Note that we do the data update/ unconditionally in the 
00331         // send-during-blanking case, whether or not we have new GS 
00332         // data.  This is because the update causes a 0.3% reduction 
00333         // in brightness because of the elongated BLANK interval.
00334         // That would be visible as a flicker on each update if we
00335         // did updates on some cycles and not others.  By doing an
00336         // update on every cycle, we make the brightness reduction
00337         // uniform across time, which makes it less perceptible.
00338         update();
00339         
00340 #else // DATA_UPDATE_INSIDE_BLANKING
00341         
00342         // end the blanking interval
00343         endBlank();
00344         
00345         // if we have pending grayscale data, start sending it
00346         if (newGSData)
00347             update();
00348 
00349 #endif // DATA_UPDATE_INSIDE_BLANKING
00350     }
00351 
00352     void startBlank()
00353     {
00354         // turn off the grayscale clock, and assert BLANK to end the grayscale cycle
00355         gsclk.write(0);
00356         blank = 1;        
00357     }
00358             
00359     void endBlank()
00360     {
00361         // if we've sent new grayscale data since the last blanking
00362         // interval, latch it by asserting XLAT
00363         if (needXlat)
00364         {
00365             // latch the new data while we're still blanked
00366             xlat = 1;
00367             xlat = 0;
00368             needXlat = false;
00369         }
00370 
00371         // end the blanking interval and restart the grayscale clock
00372         blank = 0;
00373         gsclk.write(.5);
00374         
00375         // set up the next blanking interrupt
00376         reset_timer.attach(this, &TLC5940::reset, (1.0/GSCLK_SPEED)*4096.0);
00377     }
00378     
00379     void update()
00380     {
00381         // Send new grayscale data to the TLC5940 chips.
00382         //
00383         // To do this, we set up our DMA buffer with the bytes formatted exactly
00384         // as they will go across the wire, then kick off the transfer request with 
00385         // the DMA controller.  We can then return from the interrupt and continue
00386         // with other tasks while the DMA hardware handles the transfer for us.
00387         // When the transfer is completed, the DMA controller will fire an
00388         // interrupt, which will call our interrupt handler, which will finish
00389         // the blanking cycle.
00390         //
00391         // The serial format orders the outputs from last to first (output #15 on 
00392         // the last chip in the daisy-chain to output #0 on the first chip).  For 
00393         // each output, we send 12 bits containing the grayscale level (0 = fully 
00394         // off, 0xFFF = fully on).  Bit order is most significant bit first.  
00395         // 
00396         // The KL25Z SPI can only send in 8-bit increments, so we need to divvy up 
00397         // the 12-bit outputs into 8-bit bytes.  Each pair of 12-bit outputs adds up 
00398         // to 24 bits, which divides evenly into 3 bytes, so send each pairs of 
00399         // outputs as three bytes:
00400         //
00401         //   [    element i+1 bits   ]  [ element i bits        ]
00402         //   11 10 9 8 7 6 5 4 3 2 1 0  11 10 9 8 7 6 5 4 3 2 1 0
00403         //   [  first byte   ] [   second byte  ] [  third byte ]
00404         for (int i = (16 * nchips) - 2, dst = 0 ; i >= 0 ; i -= 2)
00405         {
00406             // first byte - element i+1 bits 4-11
00407             dmabuf[dst++] = (((gs[i+1] & 0xFF0) >> 4) & 0xff);
00408             
00409             // second byte - element i+1 bits 0-3, then element i bits 8-11
00410             dmabuf[dst++] = ((((gs[i+1] & 0x00F) << 4) | ((gs[i] & 0xF00) >> 8)) & 0xFF);
00411             
00412             // third byte - element i bits 0-7
00413             dmabuf[dst++] = (gs[i] & 0x0FF);
00414         }
00415         
00416         // Start the DMA transfer
00417         sdma.start(nchips*24);
00418         
00419         // we've now cleared the new GS data
00420         newGSData = false;
00421     }
00422 
00423     // Interrupt handler for DMA completion.  The DMA controller calls this
00424     // when it finishes with the transfer request we set up above.  When the
00425     // transfer is done, we simply end the blanking cycle and start a new
00426     // grayscale cycle.    
00427     void dmaDone()
00428     {
00429         // mark that we need to assert XLAT to latch the new
00430         // grayscale data during the next blanking interval
00431         needXlat = true;
00432         
00433 #if DATA_UPDATE_INSIDE_BLANKING
00434         // we're doing the gs update within the blanking cycle, so end
00435         // the blanking cycle now that the transfer has completed
00436         endBlank();
00437 #endif
00438     }
00439 
00440 };
00441  
00442 #endif