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