Arnaud VALLEY / Mbed 2 deprecated Pinscape_Controller_V2_arnoz

Dependencies:   mbed FastIO FastPWM USBDevice

Committer:
mjr
Date:
Sat Apr 30 17:43:38 2016 +0000
Revision:
54:fd77a6b2f76c
Parent:
48:058ace2aed1d
Child:
55:4db125cd11a0
TLC5940 with SPI DMA setup in interrupt handler (not quite working)

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 26:cb71c4af2912 1 // Pinscape Controller TLC5940 interface
mjr 26:cb71c4af2912 2 //
mjr 26:cb71c4af2912 3 // Based on Spencer Davis's mbed TLC5940 library. Adapted for the
mjr 54:fd77a6b2f76c 4 // KL25Z and modified to use SPI with DMA to transmit data. The DMA
mjr 54:fd77a6b2f76c 5 // scheme results in greatly reduced CPU load. This version is also
mjr 54:fd77a6b2f76c 6 // simplified to remove dot correction and status input support, which
mjr 54:fd77a6b2f76c 7 // the Pinscape Controller app doesn't use.
mjr 26:cb71c4af2912 8
mjr 26:cb71c4af2912 9
mjr 26:cb71c4af2912 10 #ifndef TLC5940_H
mjr 26:cb71c4af2912 11 #define TLC5940_H
mjr 26:cb71c4af2912 12
mjr 54:fd77a6b2f76c 13 #include "FastPWM.h"
mjr 54:fd77a6b2f76c 14
mjr 38:091e511ce8a0 15 // Data Transmission Mode.
mjr 38:091e511ce8a0 16 //
mjr 38:091e511ce8a0 17 // NOTE! This section contains a possible workaround to try if you're
mjr 38:091e511ce8a0 18 // having data signal stability problems with your TLC5940 chips. If
mjr 40:cc0d9814522b 19 // things are working properly, you can ignore this part.
mjr 33:d832bcab089e 20 //
mjr 38:091e511ce8a0 21 // The software has two options for sending data updates to the chips:
mjr 38:091e511ce8a0 22 //
mjr 40:cc0d9814522b 23 // Mode 0: Send data *during* the grayscale cycle. This is the default,
mjr 40:cc0d9814522b 24 // and it's the standard method the chips are designed for. In this mode,
mjr 40:cc0d9814522b 25 // we start sending an update just after then blanking interval that starts
mjr 40:cc0d9814522b 26 // a new grayscale cycle. The timing is arranged so that the update is
mjr 40:cc0d9814522b 27 // completed well before the end of the grayscale cycle. At the next
mjr 40:cc0d9814522b 28 // blanking interval, we latch the new data, so the new brightness levels
mjr 40:cc0d9814522b 29 // will be shown starting on the next cycle.
mjr 40:cc0d9814522b 30
mjr 38:091e511ce8a0 31 // Mode 1: Send data *between* grayscale cycles. In this mode, we send
mjr 38:091e511ce8a0 32 // each complete update during a blanking period, then latch the update
mjr 38:091e511ce8a0 33 // and start the next grayscale cycle. This isn't the way the chips were
mjr 38:091e511ce8a0 34 // intended to be used, but it works. The disadvantage is that it requires
mjr 40:cc0d9814522b 35 // the blanking interval to be extended long enough for the full data
mjr 40:cc0d9814522b 36 // update (192 bits * the number of chips in the chain). Since the
mjr 40:cc0d9814522b 37 // outputs are turned off throughout the blanking period, this reduces
mjr 38:091e511ce8a0 38 // the overall brightness/intensity of the outputs by reducing the duty
mjr 38:091e511ce8a0 39 // cycle. The TLC5940 chips can't achieve 100% duty cycle to begin with,
mjr 40:cc0d9814522b 40 // since they require a brief minimum time in the blanking interval
mjr 38:091e511ce8a0 41 // between grayscale cycles; however, the minimum is so short that the
mjr 38:091e511ce8a0 42 // duty cycle is close to 100%. With the full data transmission stuffed
mjr 38:091e511ce8a0 43 // into the blanking interval, we reduce the duty cycle further below
mjr 38:091e511ce8a0 44 // 100%. With four chips in the chain, a 28 MHz data clock, and a
mjr 38:091e511ce8a0 45 // 500 kHz grayscale clock, the reduction is about 0.3%.
mjr 33:d832bcab089e 46 //
mjr 40:cc0d9814522b 47 // Mode 0 is the method documented in the manufacturer's data sheet.
mjr 40:cc0d9814522b 48 // It works well empirically with the Pinscape expansion boards.
mjr 40:cc0d9814522b 49 //
mjr 38:091e511ce8a0 50 // So what's the point of Mode 1? In early testing, with a breadboard
mjr 38:091e511ce8a0 51 // setup, I saw some problems with data signal stability, which manifested
mjr 38:091e511ce8a0 52 // as sporadic flickering in the outputs. Switching to Mode 1 improved
mjr 38:091e511ce8a0 53 // the signal stability considerably. I'm therefore leaving this code
mjr 38:091e511ce8a0 54 // available as an option in case anyone runs into similar signal problems
mjr 38:091e511ce8a0 55 // and wants to try the alternative mode as a workaround.
mjr 38:091e511ce8a0 56 //
mjr 38:091e511ce8a0 57 #define DATA_UPDATE_INSIDE_BLANKING 0
mjr 33:d832bcab089e 58
mjr 26:cb71c4af2912 59 #include "mbed.h"
mjr 30:6e9902f06f48 60 #include "SimpleDMA.h"
mjr 47:df7a88cd249c 61 #include "DMAChannels.h"
mjr 26:cb71c4af2912 62
mjr 54:fd77a6b2f76c 63
mjr 26:cb71c4af2912 64 /**
mjr 26:cb71c4af2912 65 * SPI speed used by the mbed to communicate with the TLC5940
mjr 26:cb71c4af2912 66 * The TLC5940 supports up to 30Mhz. It's best to keep this as
mjr 33:d832bcab089e 67 * high as possible, since a higher SPI speed yields a faster
mjr 33:d832bcab089e 68 * grayscale data update. However, I've seen some slight
mjr 33:d832bcab089e 69 * instability in the signal in my breadboard setup using the
mjr 33:d832bcab089e 70 * full 30MHz, so I've reduced this slightly, which seems to
mjr 33:d832bcab089e 71 * yield a solid signal. The limit will vary according to how
mjr 33:d832bcab089e 72 * clean the signal path is to the chips; you can probably crank
mjr 33:d832bcab089e 73 * this up to full speed if you have a well-designed PCB, good
mjr 33:d832bcab089e 74 * decoupling capacitors near the 5940 VCC/GND pins, and short
mjr 33:d832bcab089e 75 * wires between the KL25Z and the PCB. A short, clean path to
mjr 33:d832bcab089e 76 * KL25Z ground seems especially important.
mjr 26:cb71c4af2912 77 *
mjr 26:cb71c4af2912 78 * The SPI clock must be fast enough that the data transmission
mjr 26:cb71c4af2912 79 * time for a full update is comfortably less than the blanking
mjr 26:cb71c4af2912 80 * cycle time. The grayscale refresh requires 192 bits per TLC5940
mjr 26:cb71c4af2912 81 * in the daisy chain, and each bit takes one SPI clock to send.
mjr 26:cb71c4af2912 82 * Our reference setup in the Pinscape controller allows for up to
mjr 26:cb71c4af2912 83 * 4 TLC5940s, so a full refresh cycle on a fully populated system
mjr 26:cb71c4af2912 84 * would be 768 SPI clocks. The blanking cycle is 4096 GSCLK cycles.
mjr 26:cb71c4af2912 85 *
mjr 26:cb71c4af2912 86 * t(blank) = 4096 * 1/GSCLK_SPEED
mjr 26:cb71c4af2912 87 * t(refresh) = 768 * 1/SPI_SPEED
mjr 26:cb71c4af2912 88 * Therefore: SPI_SPEED must be > 768/4096 * GSCLK_SPEED
mjr 26:cb71c4af2912 89 *
mjr 26:cb71c4af2912 90 * Since the SPI speed can be so high, and since we want to keep
mjr 26:cb71c4af2912 91 * the GSCLK speed relatively low, the constraint above simply
mjr 26:cb71c4af2912 92 * isn't a factor. E.g., at SPI=30MHz and GSCLK=500kHz,
mjr 26:cb71c4af2912 93 * t(blank) is 8192us and t(refresh) is 25us.
mjr 26:cb71c4af2912 94 */
mjr 38:091e511ce8a0 95 #define SPI_SPEED 28000000
mjr 26:cb71c4af2912 96
mjr 26:cb71c4af2912 97 /**
mjr 26:cb71c4af2912 98 * The rate at which the GSCLK pin is pulsed. This also controls
mjr 26:cb71c4af2912 99 * how often the reset function is called. The reset function call
mjr 38:091e511ce8a0 100 * interval is (1/GSCLK_SPEED) * 4096. The maximum reliable rate is
mjr 26:cb71c4af2912 101 * around 32Mhz. It's best to keep this rate as low as possible:
mjr 26:cb71c4af2912 102 * the higher the rate, the higher the refresh() call frequency,
mjr 40:cc0d9814522b 103 * so the higher the CPU load. Higher frequencies also make it more
mjr 40:cc0d9814522b 104 * challenging to wire the chips for clean signal transmission, so
mjr 40:cc0d9814522b 105 * minimizing the clock speed will help with signal stability.
mjr 26:cb71c4af2912 106 *
mjr 40:cc0d9814522b 107 * The lower bound depends on the application. For driving lights,
mjr 40:cc0d9814522b 108 * the limiting factor is flicker: the lower the rate, the more
mjr 40:cc0d9814522b 109 * noticeable the flicker. Incandescents tend to look flicker-free
mjr 40:cc0d9814522b 110 * at about 50 Hz (205 kHz grayscale clock). LEDs need slightly
mjr 40:cc0d9814522b 111 * faster rates.
mjr 26:cb71c4af2912 112 */
mjr 40:cc0d9814522b 113 #define GSCLK_SPEED 350000
mjr 26:cb71c4af2912 114
mjr 26:cb71c4af2912 115 class TLC5940
mjr 26:cb71c4af2912 116 {
mjr 26:cb71c4af2912 117 public:
mjr 54:fd77a6b2f76c 118 uint64_t spi_total_time;//$$$
mjr 54:fd77a6b2f76c 119 uint32_t spi_runs;//$$$
mjr 54:fd77a6b2f76c 120
mjr 26:cb71c4af2912 121 /**
mjr 26:cb71c4af2912 122 * Set up the TLC5940
mjr 54:fd77a6b2f76c 123 *
mjr 26:cb71c4af2912 124 * @param SCLK - The SCK pin of the SPI bus
mjr 26:cb71c4af2912 125 * @param MOSI - The MOSI pin of the SPI bus
mjr 26:cb71c4af2912 126 * @param GSCLK - The GSCLK pin of the TLC5940(s)
mjr 26:cb71c4af2912 127 * @param BLANK - The BLANK pin of the TLC5940(s)
mjr 26:cb71c4af2912 128 * @param XLAT - The XLAT pin of the TLC5940(s)
mjr 26:cb71c4af2912 129 * @param nchips - The number of TLC5940s (if you are daisy chaining)
mjr 26:cb71c4af2912 130 */
mjr 26:cb71c4af2912 131 TLC5940(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, PinName XLAT, int nchips)
mjr 47:df7a88cd249c 132 : sdma(DMAch_TLC5940),
mjr 47:df7a88cd249c 133 spi(MOSI, NC, SCLK),
mjr 26:cb71c4af2912 134 gsclk(GSCLK),
mjr 26:cb71c4af2912 135 blank(BLANK),
mjr 26:cb71c4af2912 136 xlat(XLAT),
mjr 33:d832bcab089e 137 nchips(nchips)
mjr 26:cb71c4af2912 138 {
mjr 54:fd77a6b2f76c 139 spi_total_time = 0; spi_runs = 0; // $$$
mjr 54:fd77a6b2f76c 140
mjr 40:cc0d9814522b 141 // start up initially disabled
mjr 40:cc0d9814522b 142 enabled = false;
mjr 40:cc0d9814522b 143
mjr 33:d832bcab089e 144 // set XLAT to initially off
mjr 30:6e9902f06f48 145 xlat = 0;
mjr 33:d832bcab089e 146
mjr 33:d832bcab089e 147 // Assert BLANK while starting up, to keep the outputs turned off until
mjr 33:d832bcab089e 148 // everything is stable. This helps prevent spurious flashes during startup.
mjr 33:d832bcab089e 149 // (That's not particularly important for lights, but it matters more for
mjr 33:d832bcab089e 150 // tactile devices. It's a bit alarming to fire a replay knocker on every
mjr 33:d832bcab089e 151 // power-on, for example.)
mjr 30:6e9902f06f48 152 blank = 1;
mjr 30:6e9902f06f48 153
mjr 26:cb71c4af2912 154 // Configure SPI format and speed. Note that KL25Z ONLY supports 8-bit
mjr 26:cb71c4af2912 155 // mode. The TLC5940 nominally requires 12-bit data blocks for the
mjr 26:cb71c4af2912 156 // grayscale levels, but SPI is ultimately just a bit-level serial format,
mjr 26:cb71c4af2912 157 // so we can reformat the 12-bit blocks into 8-bit bytes to fit the
mjr 26:cb71c4af2912 158 // KL25Z's limits. This should work equally well on other microcontrollers
mjr 38:091e511ce8a0 159 // that are more flexible. The TLC5940 requires polarity/phase format 0.
mjr 26:cb71c4af2912 160 spi.format(8, 0);
mjr 26:cb71c4af2912 161 spi.frequency(SPI_SPEED);
mjr 33:d832bcab089e 162
mjr 33:d832bcab089e 163 // Send out a full data set to the chips, to clear out any random
mjr 33:d832bcab089e 164 // startup data from the registers. Include some extra bits - there
mjr 33:d832bcab089e 165 // are some cases (such as after sending dot correct commands) where
mjr 33:d832bcab089e 166 // an extra bit per chip is required, and the initial state is
mjr 54:fd77a6b2f76c 167 // unpredictable, so send extra bits to make sure we cover all bases.
mjr 54:fd77a6b2f76c 168 // This does no harm; extra bits just fall off the end of the daisy
mjr 54:fd77a6b2f76c 169 // chain, and since we want all registers initially set to 0, we can
mjr 33:d832bcab089e 170 // send arbitrarily many extra 0's.
mjr 33:d832bcab089e 171 for (int i = 0 ; i < nchips*25 ; ++i)
mjr 54:fd77a6b2f76c 172 spi.write(0x00);
mjr 33:d832bcab089e 173
mjr 33:d832bcab089e 174 // do an initial XLAT to latch all of these "0" values into the
mjr 33:d832bcab089e 175 // grayscale registers
mjr 33:d832bcab089e 176 xlat = 1;
mjr 33:d832bcab089e 177 xlat = 0;
mjr 29:582472d0bc57 178
mjr 39:b3815a1c3802 179 // Allocate our DMA buffers. The transfer on each cycle is 192 bits per
mjr 40:cc0d9814522b 180 // chip = 24 bytes per chip. Allocate two buffers, so that we have a
mjr 40:cc0d9814522b 181 // stable buffer that we can send to the chips, and a separate working
mjr 40:cc0d9814522b 182 // copy that we can asynchronously update.
mjr 40:cc0d9814522b 183 dmalen = nchips*24;
mjr 48:058ace2aed1d 184 livebuf = new uint8_t[dmalen*2];
mjr 54:fd77a6b2f76c 185 memset(livebuf, 0x00, dmalen*2);
mjr 40:cc0d9814522b 186
mjr 40:cc0d9814522b 187 // start with buffer 0 live, with no new data pending
mjr 48:058ace2aed1d 188 workbuf = livebuf + dmalen;
mjr 40:cc0d9814522b 189 dirty = false;
mjr 40:cc0d9814522b 190
mjr 30:6e9902f06f48 191 // Set up the Simple DMA interface object. We use the DMA controller to
mjr 30:6e9902f06f48 192 // send grayscale data updates to the TLC5940 chips. This lets the CPU
mjr 30:6e9902f06f48 193 // keep running other tasks while we send gs updates, and importantly
mjr 30:6e9902f06f48 194 // allows our blanking interrupt handler return almost immediately.
mjr 30:6e9902f06f48 195 // The DMA transfer is from our internal DMA buffer to SPI0, which is
mjr 30:6e9902f06f48 196 // the SPI controller physically connected to the TLC5940s.
mjr 54:fd77a6b2f76c 197 SPI0->C2 &= ~SPI_C2_TXDMAE_MASK;
mjr 54:fd77a6b2f76c 198 sdma.attach(this, &TLC5940::dmaDone);
mjr 48:058ace2aed1d 199 sdma.destination(&SPI0->D, false, 8);
mjr 30:6e9902f06f48 200 sdma.trigger(Trigger_SPI0_TX);
mjr 30:6e9902f06f48 201
mjr 30:6e9902f06f48 202 // Configure the GSCLK output's frequency
mjr 26:cb71c4af2912 203 gsclk.period(1.0/GSCLK_SPEED);
mjr 33:d832bcab089e 204
mjr 33:d832bcab089e 205 // mark that we need an initial update
mjr 54:fd77a6b2f76c 206 forceUpdate = true;
mjr 33:d832bcab089e 207 needXlat = false;
mjr 40:cc0d9814522b 208 }
mjr 40:cc0d9814522b 209
mjr 40:cc0d9814522b 210 // Global enable/disble. When disabled, we assert the blanking signal
mjr 40:cc0d9814522b 211 // continuously to keep all outputs turned off. This can be used during
mjr 40:cc0d9814522b 212 // startup and sleep mode to prevent spurious output signals from
mjr 40:cc0d9814522b 213 // uninitialized grayscale registers. The chips have random values in
mjr 40:cc0d9814522b 214 // their internal registers when power is first applied, so we have to
mjr 40:cc0d9814522b 215 // explicitly send the initial zero levels after power cycling the chips.
mjr 40:cc0d9814522b 216 // The chips might not have power even when the KL25Z is running, because
mjr 40:cc0d9814522b 217 // they might be powered from a separate power supply from the KL25Z
mjr 40:cc0d9814522b 218 // (the Pinscape Expansion Boards work this way). Global blanking helps
mjr 40:cc0d9814522b 219 // us start up more cleanly by suppressing all outputs until we can be
mjr 40:cc0d9814522b 220 // reasonably sure that the various chip registers are initialized.
mjr 40:cc0d9814522b 221 void enable(bool f)
mjr 40:cc0d9814522b 222 {
mjr 40:cc0d9814522b 223 // note the new setting
mjr 40:cc0d9814522b 224 enabled = f;
mjr 40:cc0d9814522b 225
mjr 40:cc0d9814522b 226 // if disabled, apply blanking immediately
mjr 40:cc0d9814522b 227 if (!f)
mjr 40:cc0d9814522b 228 {
mjr 40:cc0d9814522b 229 gsclk.write(0);
mjr 40:cc0d9814522b 230 blank = 1;
mjr 40:cc0d9814522b 231 }
mjr 40:cc0d9814522b 232
mjr 40:cc0d9814522b 233 // do a full update with the new setting
mjr 54:fd77a6b2f76c 234 forceUpdate = true;
mjr 40:cc0d9814522b 235 }
mjr 29:582472d0bc57 236
mjr 30:6e9902f06f48 237 // Start the clock running
mjr 29:582472d0bc57 238 void start()
mjr 29:582472d0bc57 239 {
mjr 26:cb71c4af2912 240 // Set up the first call to the reset function, which asserts BLANK to
mjr 26:cb71c4af2912 241 // end the PWM cycle and handles new grayscale data output and latching.
mjr 26:cb71c4af2912 242 // The original version of this library uses a timer to call reset
mjr 26:cb71c4af2912 243 // periodically, but that approach is somewhat problematic because the
mjr 26:cb71c4af2912 244 // reset function itself takes a small amount of time to run, so the
mjr 26:cb71c4af2912 245 // *actual* cycle is slightly longer than what we get from counting
mjr 26:cb71c4af2912 246 // GS clocks. Running reset on a timer therefore causes the calls to
mjr 26:cb71c4af2912 247 // slip out of phase with the actual full cycles, which causes
mjr 26:cb71c4af2912 248 // premature blanking that shows up as visible flicker. To get the
mjr 54:fd77a6b2f76c 249 // reset cycle to line up more precisely with a full PWM cycle, it
mjr 54:fd77a6b2f76c 250 // works better to set up a new timer at the end of each cycle. That
mjr 54:fd77a6b2f76c 251 // organically accounts for the time spent in the interrupt handler.
mjr 54:fd77a6b2f76c 252 // This doesn't result in perfectly uniform timing, since interrupt
mjr 54:fd77a6b2f76c 253 // latency varies slightly on each interrupt, but it does guarantee
mjr 54:fd77a6b2f76c 254 // that the blanking will never be premature - all variation will go
mjr 54:fd77a6b2f76c 255 // into the tail end of the cycle after the 4096 GS clocks. That
mjr 54:fd77a6b2f76c 256 // might cause some brightness variation, but it won't cause flicker,
mjr 54:fd77a6b2f76c 257 // and in practice any brightness variation from this seems to be too
mjr 54:fd77a6b2f76c 258 // small to be visible.
mjr 54:fd77a6b2f76c 259 armReset();
mjr 26:cb71c4af2912 260 }
mjr 26:cb71c4af2912 261
mjr 39:b3815a1c3802 262 /*
mjr 39:b3815a1c3802 263 * Set an output
mjr 26:cb71c4af2912 264 */
mjr 26:cb71c4af2912 265 void set(int idx, unsigned short data)
mjr 26:cb71c4af2912 266 {
mjr 39:b3815a1c3802 267 // validate the index
mjr 39:b3815a1c3802 268 if (idx >= 0 && idx < nchips*16)
mjr 39:b3815a1c3802 269 {
mjr 40:cc0d9814522b 270 // If the buffer isn't dirty, it means that the previous working buffer
mjr 40:cc0d9814522b 271 // was swapped into the live buffer on the last blanking interval. This
mjr 40:cc0d9814522b 272 // means that the working buffer hasn't been updated to the live data yet,
mjr 40:cc0d9814522b 273 // so we need to copy it now.
mjr 54:fd77a6b2f76c 274 //
mjr 54:fd77a6b2f76c 275 // If 'dirty' is false, it can't change to true asynchronously - it can
mjr 54:fd77a6b2f76c 276 // only transition from false to true in application (non-ISR) context.
mjr 54:fd77a6b2f76c 277 // If it's true, though, the interrupt handler can change it to false
mjr 54:fd77a6b2f76c 278 // asynchronously, and can also swap the 'live' and 'work' buffer pointers.
mjr 54:fd77a6b2f76c 279 // This means we must do the whole update atomically if 'dirty' is true.
mjr 54:fd77a6b2f76c 280 __disable_irq();
mjr 40:cc0d9814522b 281 if (!dirty)
mjr 40:cc0d9814522b 282 {
mjr 54:fd77a6b2f76c 283 // Buffer is clean, so the interrupt handler won't touch 'dirty'
mjr 54:fd77a6b2f76c 284 // or the live/work buffer pointers. This means we can do the
mjr 54:fd77a6b2f76c 285 // rest of our work with interrupts on.
mjr 54:fd77a6b2f76c 286 __enable_irq();
mjr 54:fd77a6b2f76c 287
mjr 54:fd77a6b2f76c 288 // get the current live data into our work buffer
mjr 40:cc0d9814522b 289 memcpy(workbuf, livebuf, dmalen);
mjr 40:cc0d9814522b 290 }
mjr 40:cc0d9814522b 291
mjr 54:fd77a6b2f76c 292 // Figure the DMA buffer location of the output we're changing. The DMA
mjr 54:fd77a6b2f76c 293 // buffer has the packed bit format that we send across the wire, with 12
mjr 54:fd77a6b2f76c 294 // bits per output, arranged from last output to first output (N = number
mjr 54:fd77a6b2f76c 295 // of outputs = nchips*16):
mjr 39:b3815a1c3802 296 //
mjr 39:b3815a1c3802 297 // byte 0 = high 8 bits of output N-1
mjr 39:b3815a1c3802 298 // 1 = low 4 bits of output N-1 | high 4 bits of output N-2
mjr 39:b3815a1c3802 299 // 2 = low 8 bits of N-2
mjr 39:b3815a1c3802 300 // 3 = high 8 bits of N-3
mjr 39:b3815a1c3802 301 // 4 = low 4 bits of N-3 | high 4 bits of N-2
mjr 39:b3815a1c3802 302 // 5 = low 8bits of N-4
mjr 39:b3815a1c3802 303 // ...
mjr 39:b3815a1c3802 304 // 24*nchips-3 = high 8 bits of output 1
mjr 39:b3815a1c3802 305 // 24*nchips-2 = low 4 bits of output 1 | high 4 bits of output 0
mjr 39:b3815a1c3802 306 // 24*nchips-1 = low 8 bits of output 0
mjr 39:b3815a1c3802 307 //
mjr 39:b3815a1c3802 308 // So this update will affect two bytes. If the output number if even, we're
mjr 39:b3815a1c3802 309 // in the high 4 + low 8 pair; if odd, we're in the high 8 + low 4 pair.
mjr 39:b3815a1c3802 310 int di = nchips*24 - 3 - (3*(idx/2));
mjr 39:b3815a1c3802 311 if (idx & 1)
mjr 39:b3815a1c3802 312 {
mjr 39:b3815a1c3802 313 // ODD = high 8 | low 4
mjr 40:cc0d9814522b 314 workbuf[di] = uint8_t((data >> 4) & 0xff);
mjr 40:cc0d9814522b 315 workbuf[di+1] &= 0x0F;
mjr 40:cc0d9814522b 316 workbuf[di+1] |= uint8_t((data << 4) & 0xf0);
mjr 39:b3815a1c3802 317 }
mjr 39:b3815a1c3802 318 else
mjr 39:b3815a1c3802 319 {
mjr 39:b3815a1c3802 320 // EVEN = high 4 | low 8
mjr 40:cc0d9814522b 321 workbuf[di+1] &= 0xF0;
mjr 40:cc0d9814522b 322 workbuf[di+1] |= uint8_t((data >> 8) & 0x0f);
mjr 40:cc0d9814522b 323 workbuf[di+2] = uint8_t(data & 0xff);
mjr 39:b3815a1c3802 324 }
mjr 39:b3815a1c3802 325
mjr 54:fd77a6b2f76c 326 // if we weren't dirty before, we are now
mjr 54:fd77a6b2f76c 327 if (!dirty)
mjr 54:fd77a6b2f76c 328 {
mjr 54:fd77a6b2f76c 329 // we need an update
mjr 54:fd77a6b2f76c 330 dirty = true;
mjr 54:fd77a6b2f76c 331 }
mjr 54:fd77a6b2f76c 332 else
mjr 54:fd77a6b2f76c 333 {
mjr 54:fd77a6b2f76c 334 // The buffer was already dirty, so we had to write the buffer with
mjr 54:fd77a6b2f76c 335 // interrupts off. We're done, so we can re-enable interrupts now.
mjr 54:fd77a6b2f76c 336 __enable_irq();
mjr 54:fd77a6b2f76c 337 }
mjr 39:b3815a1c3802 338 }
mjr 26:cb71c4af2912 339 }
mjr 40:cc0d9814522b 340
mjr 40:cc0d9814522b 341 // Update the outputs. We automatically update the outputs on the grayscale timer
mjr 40:cc0d9814522b 342 // when we have pending changes, so it's not necessary to call this explicitly after
mjr 40:cc0d9814522b 343 // making a change via set(). This can be called to force an update when the chips
mjr 40:cc0d9814522b 344 // might be out of sync with our internal state, such as after power-on.
mjr 40:cc0d9814522b 345 void update(bool force = false)
mjr 40:cc0d9814522b 346 {
mjr 40:cc0d9814522b 347 if (force)
mjr 54:fd77a6b2f76c 348 forceUpdate = true;
mjr 40:cc0d9814522b 349 }
mjr 26:cb71c4af2912 350
mjr 26:cb71c4af2912 351 private:
mjr 26:cb71c4af2912 352 // current level for each output
mjr 26:cb71c4af2912 353 unsigned short *gs;
mjr 26:cb71c4af2912 354
mjr 30:6e9902f06f48 355 // Simple DMA interface object
mjr 30:6e9902f06f48 356 SimpleDMA sdma;
mjr 30:6e9902f06f48 357
mjr 40:cc0d9814522b 358 // DMA transfer buffers - double buffer. Each time we have data to transmit to the
mjr 40:cc0d9814522b 359 // TLC5940 chips, we format the data into the working half of this buffer exactly as
mjr 40:cc0d9814522b 360 // it will go across the wire, then hand the buffer to the DMA controller to move
mjr 40:cc0d9814522b 361 // through the SPI port. This memory block is actually two buffers, one live and
mjr 40:cc0d9814522b 362 // one pending. When we're ready to send updates to the chips, we swap the working
mjr 40:cc0d9814522b 363 // buffer into the live buffer so that we can send the latest updates. We keep a
mjr 40:cc0d9814522b 364 // separate working copy so that our live copy is stable, so that we don't alter
mjr 40:cc0d9814522b 365 // any data in the midst of an asynchronous DMA transmission to the chips.
mjr 48:058ace2aed1d 366 uint8_t *volatile livebuf;
mjr 48:058ace2aed1d 367 uint8_t *volatile workbuf;
mjr 40:cc0d9814522b 368
mjr 40:cc0d9814522b 369 // length of each DMA buffer, in bytes - 12 bits = 1.5 bytes per output, 16 outputs
mjr 40:cc0d9814522b 370 // per chip -> 24 bytes per chip
mjr 40:cc0d9814522b 371 uint16_t dmalen;
mjr 40:cc0d9814522b 372
mjr 40:cc0d9814522b 373 // Dirty: true means that the non-live buffer has new pending data. False means
mjr 40:cc0d9814522b 374 // that the non-live buffer is empty.
mjr 54:fd77a6b2f76c 375 volatile bool dirty;
mjr 54:fd77a6b2f76c 376
mjr 54:fd77a6b2f76c 377 // Force an update: true means that we'll send our GS data to the chips even if
mjr 54:fd77a6b2f76c 378 // the buffer isn't dirty.
mjr 54:fd77a6b2f76c 379 volatile bool forceUpdate;
mjr 40:cc0d9814522b 380
mjr 40:cc0d9814522b 381 // Enabled: this enables or disables all outputs. When this is true, we assert the
mjr 40:cc0d9814522b 382 // BLANK signal continuously.
mjr 40:cc0d9814522b 383 bool enabled;
mjr 30:6e9902f06f48 384
mjr 26:cb71c4af2912 385 // SPI port - only MOSI and SCK are used
mjr 26:cb71c4af2912 386 SPI spi;
mjr 26:cb71c4af2912 387
mjr 26:cb71c4af2912 388 // use a PWM out for the grayscale clock - this provides a stable
mjr 26:cb71c4af2912 389 // square wave signal without consuming CPU
mjr 54:fd77a6b2f76c 390 FastPWM gsclk;
mjr 26:cb71c4af2912 391
mjr 26:cb71c4af2912 392 // Digital out pins used for the TLC5940
mjr 26:cb71c4af2912 393 DigitalOut blank;
mjr 26:cb71c4af2912 394 DigitalOut xlat;
mjr 26:cb71c4af2912 395
mjr 26:cb71c4af2912 396 // number of daisy-chained TLC5940s we're controlling
mjr 26:cb71c4af2912 397 int nchips;
mjr 26:cb71c4af2912 398
mjr 26:cb71c4af2912 399 // Timeout to end each PWM cycle. This is a one-shot timer that we reset
mjr 26:cb71c4af2912 400 // on each cycle.
mjr 38:091e511ce8a0 401 Timeout resetTimer;
mjr 26:cb71c4af2912 402
mjr 33:d832bcab089e 403 // Do we need an XLAT signal on the next blanking interval?
mjr 33:d832bcab089e 404 volatile bool needXlat;
mjr 54:fd77a6b2f76c 405
mjr 40:cc0d9814522b 406 // Reset the grayscale cycle and send the next data update
mjr 26:cb71c4af2912 407 void reset()
mjr 26:cb71c4af2912 408 {
mjr 30:6e9902f06f48 409 // start the blanking cycle
mjr 30:6e9902f06f48 410 startBlank();
mjr 33:d832bcab089e 411
mjr 54:fd77a6b2f76c 412 #if !DATA_UPDATE_INSIDE_BLANKING
mjr 48:058ace2aed1d 413 // We're configured to send new GS data during the GS cycle,
mjr 48:058ace2aed1d 414 // not during the blanking interval, so end the blanking
mjr 48:058ace2aed1d 415 // interval now, before we start sending the new data. Ending
mjr 48:058ace2aed1d 416 // the blanking interval starts the new GS cycle.
mjr 33:d832bcab089e 417 //
mjr 48:058ace2aed1d 418 // (For the other configuration, we send GS data during the
mjr 48:058ace2aed1d 419 // blanking interval, so in that case we DON'T end the blanking
mjr 48:058ace2aed1d 420 // interval yet - we defer that until the end-of-DMA interrupt
mjr 48:058ace2aed1d 421 // handler, which fires after the GS data send has completed.)
mjr 33:d832bcab089e 422 endBlank();
mjr 48:058ace2aed1d 423 #endif
mjr 48:058ace2aed1d 424
mjr 54:fd77a6b2f76c 425 // if we have pending grayscale data, update the DMA data
mjr 54:fd77a6b2f76c 426 bool sendGS = true; // $$$
mjr 54:fd77a6b2f76c 427 if (dirty)
mjr 54:fd77a6b2f76c 428 {
mjr 54:fd77a6b2f76c 429 // The working buffer has changes since our last update. Swap
mjr 54:fd77a6b2f76c 430 // the live and working buffers so that we send the latest updates.
mjr 54:fd77a6b2f76c 431 uint8_t *tmp = livebuf;
mjr 54:fd77a6b2f76c 432 livebuf = workbuf;
mjr 54:fd77a6b2f76c 433 workbuf = tmp;
mjr 54:fd77a6b2f76c 434
mjr 54:fd77a6b2f76c 435 // the working buffer is no longer dirty
mjr 54:fd77a6b2f76c 436 dirty = false;
mjr 54:fd77a6b2f76c 437 sendGS = true;
mjr 54:fd77a6b2f76c 438 }
mjr 54:fd77a6b2f76c 439 else if (forceUpdate)
mjr 54:fd77a6b2f76c 440 {
mjr 54:fd77a6b2f76c 441 // send the GS data and consume the forced update flag
mjr 54:fd77a6b2f76c 442 sendGS = true;
mjr 54:fd77a6b2f76c 443 forceUpdate = false;
mjr 54:fd77a6b2f76c 444 }
mjr 54:fd77a6b2f76c 445
mjr 54:fd77a6b2f76c 446 // Set the new DMA source to the live buffer. Note that we start
mjr 54:fd77a6b2f76c 447 // the DMA transfer with the *second* byte - the first byte must
mjr 54:fd77a6b2f76c 448 // be sent by the CPU rather than the DMA module, as outlined in
mjr 54:fd77a6b2f76c 449 // the KL25Z hardware reference manual.
mjr 54:fd77a6b2f76c 450
mjr 54:fd77a6b2f76c 451 // Start the new DMA transfer.
mjr 54:fd77a6b2f76c 452 //
mjr 54:fd77a6b2f76c 453 // The hardware reference manual says that the CPU has to send
mjr 54:fd77a6b2f76c 454 // the first byte of a DMA transfer explicitly. This is required
mjr 54:fd77a6b2f76c 455 // to avoid a hardware deadlock condition that happens due to
mjr 54:fd77a6b2f76c 456 // a timing interaction between the SPI and DMA controllers.
mjr 54:fd77a6b2f76c 457 // The correct sequence per the manual is:
mjr 54:fd77a6b2f76c 458 //
mjr 54:fd77a6b2f76c 459 // - reset the SPI controller
mjr 54:fd77a6b2f76c 460 // - set up the DMA registers, starting at the 2nd byte to send
mjr 54:fd77a6b2f76c 461 // - read the SPI status register (SPI0->S), wait for SPTEF to be set
mjr 54:fd77a6b2f76c 462 // - write the first byte to the SPI data register (SPI0->D)
mjr 54:fd77a6b2f76c 463 // - enable TXDMAE in the SPI control register (SPI0->C2)
mjr 54:fd77a6b2f76c 464 //
mjr 54:fd77a6b2f76c 465 if (sendGS)
mjr 48:058ace2aed1d 466 {
mjr 54:fd77a6b2f76c 467 #if 1 // $$$
mjr 54:fd77a6b2f76c 468 Timer t; t.start(); //$$$
mjr 54:fd77a6b2f76c 469 uint8_t *p = livebuf;
mjr 54:fd77a6b2f76c 470 for (int i = dmalen ; i != 0 ; --i) {
mjr 54:fd77a6b2f76c 471 while (!(SPI0->S & SPI_S_SPTEF_MASK)) ;
mjr 54:fd77a6b2f76c 472 SPI0->D = *p++;
mjr 54:fd77a6b2f76c 473 }
mjr 54:fd77a6b2f76c 474 needXlat = true;
mjr 54:fd77a6b2f76c 475
mjr 54:fd77a6b2f76c 476 spi_total_time += t.read_us();
mjr 54:fd77a6b2f76c 477 spi_runs += 1;
mjr 54:fd77a6b2f76c 478 #else
mjr 54:fd77a6b2f76c 479 // disable DMA on SPI0
mjr 54:fd77a6b2f76c 480 SPI0->C2 &= ~SPI_C2_TXDMAE_MASK;
mjr 54:fd77a6b2f76c 481
mjr 54:fd77a6b2f76c 482 // reset SPI0
mjr 54:fd77a6b2f76c 483 SPI0->C1 &= ~SPI_C1_SPE_MASK;
mjr 54:fd77a6b2f76c 484
mjr 54:fd77a6b2f76c 485 // set up a transfer from the second byte of the buffer
mjr 54:fd77a6b2f76c 486 sdma.source(livebuf + 1, true, 8);
mjr 54:fd77a6b2f76c 487 sdma.start(dmalen - 1, false);
mjr 54:fd77a6b2f76c 488
mjr 54:fd77a6b2f76c 489 // enable SPI0
mjr 54:fd77a6b2f76c 490 SPI0->C1 |= SPI_C1_SPE_MASK;
mjr 54:fd77a6b2f76c 491
mjr 54:fd77a6b2f76c 492 // wait for the TX buffer to clear, then write the first byte manually
mjr 54:fd77a6b2f76c 493 while (!(SPI0->S & SPI_S_SPTEF_MASK)) ;
mjr 54:fd77a6b2f76c 494 SPI0->D = livebuf[0];
mjr 54:fd77a6b2f76c 495
mjr 54:fd77a6b2f76c 496 // enable DMA to carry out the rest of the transfer
mjr 48:058ace2aed1d 497 SPI0->C2 |= SPI_C2_TXDMAE_MASK;
mjr 54:fd77a6b2f76c 498
mjr 54:fd77a6b2f76c 499 // we'll need a translate on the next blanking cycle
mjr 54:fd77a6b2f76c 500 needXlat = true;
mjr 54:fd77a6b2f76c 501 #endif
mjr 48:058ace2aed1d 502 }
mjr 54:fd77a6b2f76c 503
mjr 54:fd77a6b2f76c 504 #if !DATA_UPDATE_INSIDE_BLANKING
mjr 54:fd77a6b2f76c 505 // arm the reset handler
mjr 54:fd77a6b2f76c 506 armReset();
mjr 54:fd77a6b2f76c 507 #endif
mjr 54:fd77a6b2f76c 508 }
mjr 54:fd77a6b2f76c 509
mjr 54:fd77a6b2f76c 510 // arm the reset handler - this fires at the end of each GS cycle
mjr 54:fd77a6b2f76c 511 void armReset()
mjr 54:fd77a6b2f76c 512 {
mjr 54:fd77a6b2f76c 513 resetTimer.attach(this, &TLC5940::reset, (1.0/GSCLK_SPEED)*4096.0);
mjr 30:6e9902f06f48 514 }
mjr 30:6e9902f06f48 515
mjr 30:6e9902f06f48 516 void startBlank()
mjr 30:6e9902f06f48 517 {
mjr 54:fd77a6b2f76c 518 //static int i=0; i=(i+1)%200; extern void diagLED(int,int,int); diagLED(i<100,i>=100,0);//$$$
mjr 54:fd77a6b2f76c 519
mjr 30:6e9902f06f48 520 // turn off the grayscale clock, and assert BLANK to end the grayscale cycle
mjr 30:6e9902f06f48 521 gsclk.write(0);
mjr 54:fd77a6b2f76c 522 blank = (enabled ? 1 : 0); // for the slight delay (20ns) required after GSCLK goes low
mjr 30:6e9902f06f48 523 blank = 1;
mjr 30:6e9902f06f48 524 }
mjr 26:cb71c4af2912 525
mjr 33:d832bcab089e 526 void endBlank()
mjr 30:6e9902f06f48 527 {
mjr 54:fd77a6b2f76c 528 //static int i=0; i=(i+1)%200; extern void diagLED(int,int,int); diagLED(-1,i<100,-1);//$$$
mjr 54:fd77a6b2f76c 529
mjr 33:d832bcab089e 530 // if we've sent new grayscale data since the last blanking
mjr 33:d832bcab089e 531 // interval, latch it by asserting XLAT
mjr 33:d832bcab089e 532 if (needXlat)
mjr 30:6e9902f06f48 533 {
mjr 26:cb71c4af2912 534 // latch the new data while we're still blanked
mjr 26:cb71c4af2912 535 xlat = 1;
mjr 26:cb71c4af2912 536 xlat = 0;
mjr 33:d832bcab089e 537 needXlat = false;
mjr 26:cb71c4af2912 538 }
mjr 26:cb71c4af2912 539
mjr 40:cc0d9814522b 540 // End the blanking interval and restart the grayscale clock. Note
mjr 40:cc0d9814522b 541 // that we keep the blanking on if the chips are globally disabled.
mjr 54:fd77a6b2f76c 542 if (enabled)
mjr 54:fd77a6b2f76c 543 {
mjr 54:fd77a6b2f76c 544 blank = 0;
mjr 54:fd77a6b2f76c 545 gsclk.write(.5);
mjr 54:fd77a6b2f76c 546 }
mjr 26:cb71c4af2912 547 }
mjr 26:cb71c4af2912 548
mjr 30:6e9902f06f48 549 // Interrupt handler for DMA completion. The DMA controller calls this
mjr 30:6e9902f06f48 550 // when it finishes with the transfer request we set up above. When the
mjr 30:6e9902f06f48 551 // transfer is done, we simply end the blanking cycle and start a new
mjr 30:6e9902f06f48 552 // grayscale cycle.
mjr 30:6e9902f06f48 553 void dmaDone()
mjr 30:6e9902f06f48 554 {
mjr 54:fd77a6b2f76c 555 //static int i=0; i=(i+1)%200; extern void diagLED(int,int,int); diagLED(i<100,-1,-1);//$$$
mjr 54:fd77a6b2f76c 556
mjr 48:058ace2aed1d 557 // disable DMA triggering in the SPI controller until we set
mjr 48:058ace2aed1d 558 // up the next transfer
mjr 48:058ace2aed1d 559 SPI0->C2 &= ~SPI_C2_TXDMAE_MASK;
mjr 54:fd77a6b2f76c 560 SPI0->C1 &= ~SPI_C1_SPE_MASK;
mjr 48:058ace2aed1d 561
mjr 33:d832bcab089e 562 // mark that we need to assert XLAT to latch the new
mjr 33:d832bcab089e 563 // grayscale data during the next blanking interval
mjr 54:fd77a6b2f76c 564 needXlat = true;
mjr 33:d832bcab089e 565
mjr 33:d832bcab089e 566 #if DATA_UPDATE_INSIDE_BLANKING
mjr 33:d832bcab089e 567 // we're doing the gs update within the blanking cycle, so end
mjr 33:d832bcab089e 568 // the blanking cycle now that the transfer has completed
mjr 33:d832bcab089e 569 endBlank();
mjr 54:fd77a6b2f76c 570
mjr 54:fd77a6b2f76c 571 // set up the next blanking interrupt
mjr 54:fd77a6b2f76c 572 armReset();
mjr 33:d832bcab089e 573 #endif
mjr 30:6e9902f06f48 574 }
mjr 30:6e9902f06f48 575
mjr 26:cb71c4af2912 576 };
mjr 26:cb71c4af2912 577
mjr 26:cb71c4af2912 578 #endif