![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
work in progress
Dependencies: FastAnalogIn FastIO USBDevice mbed FastPWM SimpleDMA
Fork of Pinscape_Controller by
TLC5940/TLC5940.h@38:697e10d8fb80, 2015-12-16 (annotated)
- Committer:
- mkalkbrenner
- Date:
- Wed Dec 16 15:37:59 2015 +0000
- Revision:
- 38:697e10d8fb80
- Parent:
- 35:d832bcab089e
fixed default configuration for extension boards
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
mjr | 28:cb71c4af2912 | 1 | // Pinscape Controller TLC5940 interface |
mjr | 28:cb71c4af2912 | 2 | // |
mjr | 28:cb71c4af2912 | 3 | // Based on Spencer Davis's mbed TLC5940 library. Adapted for the |
mjr | 28:cb71c4af2912 | 4 | // KL25Z, and simplified to just the functions needed for this |
mjr | 28:cb71c4af2912 | 5 | // application. In particular, this version doesn't include support |
mjr | 28:cb71c4af2912 | 6 | // for dot correction programming or status input. This version also |
mjr | 28:cb71c4af2912 | 7 | // uses a different approach for sending the grayscale data updates, |
mjr | 28:cb71c4af2912 | 8 | // sending updates during the blanking interval rather than overlapping |
mjr | 28:cb71c4af2912 | 9 | // them with the PWM cycle. This results in very slightly longer |
mjr | 28:cb71c4af2912 | 10 | // blanking intervals when updates are pending, effectively reducing |
mjr | 28:cb71c4af2912 | 11 | // the PWM "on" duty cycle (and thus the output brightness) by about |
mjr | 28:cb71c4af2912 | 12 | // 0.3%. This shouldn't be perceptible to users, so it's a small |
mjr | 28:cb71c4af2912 | 13 | // trade-off for the advantage gained, which is much better signal |
mjr | 28:cb71c4af2912 | 14 | // stability when using multiple TLC5940s daisy-chained together. |
mjr | 28:cb71c4af2912 | 15 | // I saw a lot of instability when using the overlapped approach, |
mjr | 28:cb71c4af2912 | 16 | // which seems to be eliminated entirely when sending updates during |
mjr | 28:cb71c4af2912 | 17 | // the blanking interval. |
mjr | 28:cb71c4af2912 | 18 | |
mjr | 28:cb71c4af2912 | 19 | |
mjr | 28:cb71c4af2912 | 20 | #ifndef TLC5940_H |
mjr | 28:cb71c4af2912 | 21 | #define TLC5940_H |
mjr | 28:cb71c4af2912 | 22 | |
mjr | 35:d832bcab089e | 23 | // Should we do the grayscale update within the blanking interval? |
mjr | 35:d832bcab089e | 24 | // If this is set to 1, we'll send grayscale data during the blanking |
mjr | 35:d832bcab089e | 25 | // interval; if 0, we'll send grayscale during the PWM cycle. |
mjr | 35:d832bcab089e | 26 | // Mode 0 is the *intended* way of using these chips, but mode 1 |
mjr | 35:d832bcab089e | 27 | // produces a more stable signal in my test setup. |
mjr | 35:d832bcab089e | 28 | // |
mjr | 35:d832bcab089e | 29 | // In my breadboard testing, using the standard data-during-PWM |
mjr | 35:d832bcab089e | 30 | // mode causes some amount of signal instability with multiple |
mjr | 35:d832bcab089e | 31 | // daisy-chained TLC5940's. It appears that there's some signal |
mjr | 35:d832bcab089e | 32 | // interference (maybe RF or electrical ringing in the wires) that |
mjr | 35:d832bcab089e | 33 | // can make the bit data and/or clock prone to noise that causes |
mjr | 35:d832bcab089e | 34 | // random bits to propagate down the daisy chain. This happens |
mjr | 35:d832bcab089e | 35 | // frequently enough in my breadboard setup to be visible as |
mjr | 35:d832bcab089e | 36 | // regular flicker. Careful wiring, short wire runs, and decoupling |
mjr | 35:d832bcab089e | 37 | // capacitors noticeably improve it, but I haven't been able to |
mjr | 35:d832bcab089e | 38 | // eliminate it entirely in my test setup. Using the data-during- |
mjr | 35:d832bcab089e | 39 | // blanking mode, however, *does* eliminate it entirely. |
mjr | 35:d832bcab089e | 40 | // |
mjr | 35:d832bcab089e | 41 | // It clearly should be possible to eliminate the signal problems |
mjr | 35:d832bcab089e | 42 | // in a well-designed PCB layout, but for the time being, I'm |
mjr | 35:d832bcab089e | 43 | // making data-during-blanking the default, since it provides |
mjr | 35:d832bcab089e | 44 | // such a noticeable improvement in my test setup, and the cost |
mjr | 35:d832bcab089e | 45 | // is minimal. The cost is that it lengthens the blanking interval |
mjr | 35:d832bcab089e | 46 | // slightly. With four chips and the SPI clock at 28MHz, the |
mjr | 35:d832bcab089e | 47 | // full data update takes 27us; with the PWM clock at 500kHz, the |
mjr | 35:d832bcab089e | 48 | // grayscale cycle is 8192us. This means that the 27us data send |
mjr | 35:d832bcab089e | 49 | // keeps the BLANK asserted for an additional 0.3% of the cycle |
mjr | 35:d832bcab089e | 50 | // time, which in term reduces output brightness by the same amount. |
mjr | 35:d832bcab089e | 51 | // This brightness reduction isn't noticeable on its own, but it |
mjr | 35:d832bcab089e | 52 | // can be seen as a flicker on data cycles if we send data on |
mjr | 35:d832bcab089e | 53 | // some blanking cycles but not on others. To eliminate the |
mjr | 35:d832bcab089e | 54 | // flicker, the code sends a data update on *every* cycle when |
mjr | 35:d832bcab089e | 55 | // using this mode to ensure that the 0.3% brightness reduction |
mjr | 35:d832bcab089e | 56 | // is uniform across time. |
mjr | 35:d832bcab089e | 57 | // |
mjr | 35:d832bcab089e | 58 | // When using this code with TLC5940 chips on a PCB, I recommend |
mjr | 35:d832bcab089e | 59 | // doing a test: set this to 0, run the board, turn on all outputs |
mjr | 35:d832bcab089e | 60 | // (connected to LEDs), and observe the results. If you don't |
mjr | 35:d832bcab089e | 61 | // see any randomness or flicker in a minute or two of observation, |
mjr | 35:d832bcab089e | 62 | // you're getting a good clean signal throughout the daisy chain |
mjr | 35:d832bcab089e | 63 | // and don't need the workaround. If you do see any instability, |
mjr | 35:d832bcab089e | 64 | // set this back to 1. |
mjr | 35:d832bcab089e | 65 | #define DATA_UPDATE_INSIDE_BLANKING 1 |
mjr | 35:d832bcab089e | 66 | |
mjr | 28:cb71c4af2912 | 67 | #include "mbed.h" |
mjr | 28:cb71c4af2912 | 68 | #include "FastPWM.h" |
mjr | 32:6e9902f06f48 | 69 | #include "SimpleDMA.h" |
mjr | 28:cb71c4af2912 | 70 | |
mjr | 28:cb71c4af2912 | 71 | /** |
mjr | 28:cb71c4af2912 | 72 | * SPI speed used by the mbed to communicate with the TLC5940 |
mjr | 28:cb71c4af2912 | 73 | * The TLC5940 supports up to 30Mhz. It's best to keep this as |
mjr | 35:d832bcab089e | 74 | * high as possible, since a higher SPI speed yields a faster |
mjr | 35:d832bcab089e | 75 | * grayscale data update. However, I've seen some slight |
mjr | 35:d832bcab089e | 76 | * instability in the signal in my breadboard setup using the |
mjr | 35:d832bcab089e | 77 | * full 30MHz, so I've reduced this slightly, which seems to |
mjr | 35:d832bcab089e | 78 | * yield a solid signal. The limit will vary according to how |
mjr | 35:d832bcab089e | 79 | * clean the signal path is to the chips; you can probably crank |
mjr | 35:d832bcab089e | 80 | * this up to full speed if you have a well-designed PCB, good |
mjr | 35:d832bcab089e | 81 | * decoupling capacitors near the 5940 VCC/GND pins, and short |
mjr | 35:d832bcab089e | 82 | * wires between the KL25Z and the PCB. A short, clean path to |
mjr | 35:d832bcab089e | 83 | * KL25Z ground seems especially important. |
mjr | 28:cb71c4af2912 | 84 | * |
mjr | 28:cb71c4af2912 | 85 | * The SPI clock must be fast enough that the data transmission |
mjr | 28:cb71c4af2912 | 86 | * time for a full update is comfortably less than the blanking |
mjr | 28:cb71c4af2912 | 87 | * cycle time. The grayscale refresh requires 192 bits per TLC5940 |
mjr | 28:cb71c4af2912 | 88 | * in the daisy chain, and each bit takes one SPI clock to send. |
mjr | 28:cb71c4af2912 | 89 | * Our reference setup in the Pinscape controller allows for up to |
mjr | 28:cb71c4af2912 | 90 | * 4 TLC5940s, so a full refresh cycle on a fully populated system |
mjr | 28:cb71c4af2912 | 91 | * would be 768 SPI clocks. The blanking cycle is 4096 GSCLK cycles. |
mjr | 28:cb71c4af2912 | 92 | * |
mjr | 28:cb71c4af2912 | 93 | * t(blank) = 4096 * 1/GSCLK_SPEED |
mjr | 28:cb71c4af2912 | 94 | * t(refresh) = 768 * 1/SPI_SPEED |
mjr | 28:cb71c4af2912 | 95 | * Therefore: SPI_SPEED must be > 768/4096 * GSCLK_SPEED |
mjr | 28:cb71c4af2912 | 96 | * |
mjr | 28:cb71c4af2912 | 97 | * Since the SPI speed can be so high, and since we want to keep |
mjr | 28:cb71c4af2912 | 98 | * the GSCLK speed relatively low, the constraint above simply |
mjr | 28:cb71c4af2912 | 99 | * isn't a factor. E.g., at SPI=30MHz and GSCLK=500kHz, |
mjr | 28:cb71c4af2912 | 100 | * t(blank) is 8192us and t(refresh) is 25us. |
mjr | 28:cb71c4af2912 | 101 | */ |
mjr | 35:d832bcab089e | 102 | #define SPI_SPEED 2800000 |
mjr | 28:cb71c4af2912 | 103 | |
mjr | 28:cb71c4af2912 | 104 | /** |
mjr | 28:cb71c4af2912 | 105 | * The rate at which the GSCLK pin is pulsed. This also controls |
mjr | 28:cb71c4af2912 | 106 | * how often the reset function is called. The reset function call |
mjr | 28:cb71c4af2912 | 107 | * rate is (1/GSCLK_SPEED) * 4096. The maximum reliable rate is |
mjr | 28:cb71c4af2912 | 108 | * around 32Mhz. It's best to keep this rate as low as possible: |
mjr | 28:cb71c4af2912 | 109 | * the higher the rate, the higher the refresh() call frequency, |
mjr | 28:cb71c4af2912 | 110 | * so the higher the CPU load. |
mjr | 28:cb71c4af2912 | 111 | * |
mjr | 28:cb71c4af2912 | 112 | * The lower bound is probably dependent on the application. For |
mjr | 28:cb71c4af2912 | 113 | * driving LEDs, the limiting factor is that lower rates will increase |
mjr | 28:cb71c4af2912 | 114 | * visible flicker. 200 kHz seems to be a good lower bound for LEDs. |
mjr | 28:cb71c4af2912 | 115 | * That provides about 48 cycles per second - that's about the same as |
mjr | 28:cb71c4af2912 | 116 | * the 50 Hz A/C cycle rate in many countries, which was itself chosen |
mjr | 28:cb71c4af2912 | 117 | * so that incandescent lights don't flicker. (This rate is a function |
mjr | 28:cb71c4af2912 | 118 | * of human eye physiology, which has its own refresh cycle of sorts |
mjr | 28:cb71c4af2912 | 119 | * that runs at about 50 Hz. If you're designing an LED system for |
mjr | 28:cb71c4af2912 | 120 | * viewing by cats or drosophila, you might want to look into your |
mjr | 28:cb71c4af2912 | 121 | * target species' eye physiology, since the persistence of vision |
mjr | 28:cb71c4af2912 | 122 | * rate varies quite a bit from species to species.) Flicker tends to |
mjr | 28:cb71c4af2912 | 123 | * be more noticeable in LEDs than in incandescents, since LEDs don't |
mjr | 28:cb71c4af2912 | 124 | * have the thermal inertia of incandescents, so we use a slightly |
mjr | 28:cb71c4af2912 | 125 | * higher default here. 500 kHz = 122 full grayscale cycles per |
mjr | 28:cb71c4af2912 | 126 | * second = 122 reset calls per second (call every 8ms). |
mjr | 28:cb71c4af2912 | 127 | */ |
mjr | 28:cb71c4af2912 | 128 | #define GSCLK_SPEED 500000 |
mjr | 28:cb71c4af2912 | 129 | |
mjr | 28:cb71c4af2912 | 130 | /** |
mjr | 28:cb71c4af2912 | 131 | * This class controls a TLC5940 PWM driver IC. |
mjr | 28:cb71c4af2912 | 132 | * |
mjr | 28:cb71c4af2912 | 133 | * Using the TLC5940 class to control an LED: |
mjr | 28:cb71c4af2912 | 134 | * @code |
mjr | 28:cb71c4af2912 | 135 | * #include "mbed.h" |
mjr | 28:cb71c4af2912 | 136 | * #include "TLC5940.h" |
mjr | 28:cb71c4af2912 | 137 | * |
mjr | 28:cb71c4af2912 | 138 | * // Create the TLC5940 instance |
mjr | 28:cb71c4af2912 | 139 | * TLC5940 tlc(p7, p5, p21, p9, p10, p11, p12, 1); |
mjr | 28:cb71c4af2912 | 140 | * |
mjr | 28:cb71c4af2912 | 141 | * int main() |
mjr | 28:cb71c4af2912 | 142 | * { |
mjr | 28:cb71c4af2912 | 143 | * // Enable the first LED |
mjr | 28:cb71c4af2912 | 144 | * tlc.set(0, 0xfff); |
mjr | 28:cb71c4af2912 | 145 | * |
mjr | 28:cb71c4af2912 | 146 | * while(1) |
mjr | 28:cb71c4af2912 | 147 | * { |
mjr | 28:cb71c4af2912 | 148 | * } |
mjr | 28:cb71c4af2912 | 149 | * } |
mjr | 28:cb71c4af2912 | 150 | * @endcode |
mjr | 28:cb71c4af2912 | 151 | */ |
mjr | 28:cb71c4af2912 | 152 | class TLC5940 |
mjr | 28:cb71c4af2912 | 153 | { |
mjr | 28:cb71c4af2912 | 154 | public: |
mjr | 28:cb71c4af2912 | 155 | /** |
mjr | 28:cb71c4af2912 | 156 | * Set up the TLC5940 |
mjr | 28:cb71c4af2912 | 157 | * @param SCLK - The SCK pin of the SPI bus |
mjr | 28:cb71c4af2912 | 158 | * @param MOSI - The MOSI pin of the SPI bus |
mjr | 28:cb71c4af2912 | 159 | * @param GSCLK - The GSCLK pin of the TLC5940(s) |
mjr | 28:cb71c4af2912 | 160 | * @param BLANK - The BLANK pin of the TLC5940(s) |
mjr | 28:cb71c4af2912 | 161 | * @param XLAT - The XLAT pin of the TLC5940(s) |
mjr | 28:cb71c4af2912 | 162 | * @param nchips - The number of TLC5940s (if you are daisy chaining) |
mjr | 28:cb71c4af2912 | 163 | */ |
mjr | 28:cb71c4af2912 | 164 | TLC5940(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, PinName XLAT, int nchips) |
mjr | 28:cb71c4af2912 | 165 | : spi(MOSI, NC, SCLK), |
mjr | 28:cb71c4af2912 | 166 | gsclk(GSCLK), |
mjr | 28:cb71c4af2912 | 167 | blank(BLANK), |
mjr | 28:cb71c4af2912 | 168 | xlat(XLAT), |
mjr | 35:d832bcab089e | 169 | nchips(nchips) |
mjr | 28:cb71c4af2912 | 170 | { |
mjr | 35:d832bcab089e | 171 | // set XLAT to initially off |
mjr | 32:6e9902f06f48 | 172 | xlat = 0; |
mjr | 35:d832bcab089e | 173 | |
mjr | 35:d832bcab089e | 174 | // Assert BLANK while starting up, to keep the outputs turned off until |
mjr | 35:d832bcab089e | 175 | // everything is stable. This helps prevent spurious flashes during startup. |
mjr | 35:d832bcab089e | 176 | // (That's not particularly important for lights, but it matters more for |
mjr | 35:d832bcab089e | 177 | // tactile devices. It's a bit alarming to fire a replay knocker on every |
mjr | 35:d832bcab089e | 178 | // power-on, for example.) |
mjr | 32:6e9902f06f48 | 179 | blank = 1; |
mjr | 32:6e9902f06f48 | 180 | |
mjr | 35:d832bcab089e | 181 | // allocate the grayscale buffer, and set all outputs to fully off |
mjr | 28:cb71c4af2912 | 182 | gs = new unsigned short[nchips*16]; |
mjr | 30:2097c6f8f2db | 183 | memset(gs, 0, nchips*16*sizeof(gs[0])); |
mjr | 28:cb71c4af2912 | 184 | |
mjr | 28:cb71c4af2912 | 185 | // Configure SPI format and speed. Note that KL25Z ONLY supports 8-bit |
mjr | 28:cb71c4af2912 | 186 | // mode. The TLC5940 nominally requires 12-bit data blocks for the |
mjr | 28:cb71c4af2912 | 187 | // grayscale levels, but SPI is ultimately just a bit-level serial format, |
mjr | 28:cb71c4af2912 | 188 | // so we can reformat the 12-bit blocks into 8-bit bytes to fit the |
mjr | 28:cb71c4af2912 | 189 | // KL25Z's limits. This should work equally well on other microcontrollers |
mjr | 28:cb71c4af2912 | 190 | // that are more flexible. The TLC5940 appears to require polarity/phase |
mjr | 28:cb71c4af2912 | 191 | // format 0. |
mjr | 28:cb71c4af2912 | 192 | spi.format(8, 0); |
mjr | 28:cb71c4af2912 | 193 | spi.frequency(SPI_SPEED); |
mjr | 35:d832bcab089e | 194 | |
mjr | 35:d832bcab089e | 195 | // Send out a full data set to the chips, to clear out any random |
mjr | 35:d832bcab089e | 196 | // startup data from the registers. Include some extra bits - there |
mjr | 35:d832bcab089e | 197 | // are some cases (such as after sending dot correct commands) where |
mjr | 35:d832bcab089e | 198 | // an extra bit per chip is required, and the initial state is |
mjr | 35:d832bcab089e | 199 | // somewhat unpredictable, so send extra just to make sure we cover |
mjr | 35:d832bcab089e | 200 | // all bases. This does no harm; extra bits just fall off the end of |
mjr | 35:d832bcab089e | 201 | // the daisy chain, and since we want all registers set to 0, we can |
mjr | 35:d832bcab089e | 202 | // send arbitrarily many extra 0's. |
mjr | 35:d832bcab089e | 203 | for (int i = 0 ; i < nchips*25 ; ++i) |
mjr | 35:d832bcab089e | 204 | spi.write(0); |
mjr | 35:d832bcab089e | 205 | |
mjr | 35:d832bcab089e | 206 | // do an initial XLAT to latch all of these "0" values into the |
mjr | 35:d832bcab089e | 207 | // grayscale registers |
mjr | 35:d832bcab089e | 208 | xlat = 1; |
mjr | 35:d832bcab089e | 209 | xlat = 0; |
mjr | 31:582472d0bc57 | 210 | |
mjr | 32:6e9902f06f48 | 211 | // Allocate a DMA buffer. The transfer on each cycle is 192 bits per |
mjr | 32:6e9902f06f48 | 212 | // chip = 24 bytes per chip. |
mjr | 32:6e9902f06f48 | 213 | dmabuf = new char[nchips*24]; |
mjr | 28:cb71c4af2912 | 214 | |
mjr | 32:6e9902f06f48 | 215 | // Set up the Simple DMA interface object. We use the DMA controller to |
mjr | 32:6e9902f06f48 | 216 | // send grayscale data updates to the TLC5940 chips. This lets the CPU |
mjr | 32:6e9902f06f48 | 217 | // keep running other tasks while we send gs updates, and importantly |
mjr | 32:6e9902f06f48 | 218 | // allows our blanking interrupt handler return almost immediately. |
mjr | 32:6e9902f06f48 | 219 | // The DMA transfer is from our internal DMA buffer to SPI0, which is |
mjr | 32:6e9902f06f48 | 220 | // the SPI controller physically connected to the TLC5940s. |
mjr | 32:6e9902f06f48 | 221 | sdma.source(dmabuf, 1); |
mjr | 32:6e9902f06f48 | 222 | sdma.destination(&(SPI0->D), 0, 8); |
mjr | 32:6e9902f06f48 | 223 | sdma.trigger(Trigger_SPI0_TX); |
mjr | 32:6e9902f06f48 | 224 | sdma.attach(this, &TLC5940::dmaDone); |
mjr | 32:6e9902f06f48 | 225 | |
mjr | 32:6e9902f06f48 | 226 | // Enable DMA on SPI0. SimpleDMA doesn't do this for us; we have to |
mjr | 32:6e9902f06f48 | 227 | // do it explicitly. This is just a matter of setting bit 5 (TXDMAE) |
mjr | 32:6e9902f06f48 | 228 | // in the SPI controllers Control Register 2 (C2). |
mjr | 32:6e9902f06f48 | 229 | SPI0->C2 |= 0x20; // set bit 5 = 0x20 = TXDMAE in SPI0 control register 2 |
mjr | 32:6e9902f06f48 | 230 | |
mjr | 32:6e9902f06f48 | 231 | // Configure the GSCLK output's frequency |
mjr | 28:cb71c4af2912 | 232 | gsclk.period(1.0/GSCLK_SPEED); |
mjr | 35:d832bcab089e | 233 | |
mjr | 35:d832bcab089e | 234 | // mark that we need an initial update |
mjr | 35:d832bcab089e | 235 | newGSData = true; |
mjr | 35:d832bcab089e | 236 | needXlat = false; |
mjr | 32:6e9902f06f48 | 237 | } |
mjr | 31:582472d0bc57 | 238 | |
mjr | 32:6e9902f06f48 | 239 | // Start the clock running |
mjr | 31:582472d0bc57 | 240 | void start() |
mjr | 31:582472d0bc57 | 241 | { |
mjr | 28:cb71c4af2912 | 242 | // Set up the first call to the reset function, which asserts BLANK to |
mjr | 28:cb71c4af2912 | 243 | // end the PWM cycle and handles new grayscale data output and latching. |
mjr | 28:cb71c4af2912 | 244 | // The original version of this library uses a timer to call reset |
mjr | 28:cb71c4af2912 | 245 | // periodically, but that approach is somewhat problematic because the |
mjr | 28:cb71c4af2912 | 246 | // reset function itself takes a small amount of time to run, so the |
mjr | 28:cb71c4af2912 | 247 | // *actual* cycle is slightly longer than what we get from counting |
mjr | 28:cb71c4af2912 | 248 | // GS clocks. Running reset on a timer therefore causes the calls to |
mjr | 28:cb71c4af2912 | 249 | // slip out of phase with the actual full cycles, which causes |
mjr | 28:cb71c4af2912 | 250 | // premature blanking that shows up as visible flicker. To get the |
mjr | 28:cb71c4af2912 | 251 | // reset cycle to line up exactly with a full PWM cycle, it works |
mjr | 28:cb71c4af2912 | 252 | // better to set up a new timer on each cycle, *after* we've finished |
mjr | 28:cb71c4af2912 | 253 | // with the somewhat unpredictable overhead of the interrupt handler. |
mjr | 28:cb71c4af2912 | 254 | // This ensures that we'll get much closer to exact alignment of the |
mjr | 28:cb71c4af2912 | 255 | // cycle phase, and in any case the worst that happens is that some |
mjr | 28:cb71c4af2912 | 256 | // cycles are very slightly too long or short (due to imperfections |
mjr | 28:cb71c4af2912 | 257 | // in the timer clock vs the PWM clock that determines the GSCLCK |
mjr | 28:cb71c4af2912 | 258 | // output to the TLC5940), which is far less noticeable than a |
mjr | 28:cb71c4af2912 | 259 | // constantly rotating phase misalignment. |
mjr | 28:cb71c4af2912 | 260 | reset_timer.attach(this, &TLC5940::reset, (1.0/GSCLK_SPEED)*4096.0); |
mjr | 28:cb71c4af2912 | 261 | } |
mjr | 28:cb71c4af2912 | 262 | |
mjr | 28:cb71c4af2912 | 263 | ~TLC5940() |
mjr | 28:cb71c4af2912 | 264 | { |
mjr | 28:cb71c4af2912 | 265 | delete [] gs; |
mjr | 32:6e9902f06f48 | 266 | delete [] dmabuf; |
mjr | 28:cb71c4af2912 | 267 | } |
mjr | 28:cb71c4af2912 | 268 | |
mjr | 28:cb71c4af2912 | 269 | /** |
mjr | 28:cb71c4af2912 | 270 | * Set the next chunk of grayscale data to be sent |
mjr | 28:cb71c4af2912 | 271 | * @param data - Array of 16 bit shorts containing 16 12 bit grayscale data chunks per TLC5940 |
mjr | 28:cb71c4af2912 | 272 | * @note These must be in intervals of at least (1/GSCLK_SPEED) * 4096 to be sent |
mjr | 28:cb71c4af2912 | 273 | */ |
mjr | 28:cb71c4af2912 | 274 | void set(int idx, unsigned short data) |
mjr | 28:cb71c4af2912 | 275 | { |
mjr | 28:cb71c4af2912 | 276 | // store the data, and flag the pending update for the interrupt handler to carry out |
mjr | 28:cb71c4af2912 | 277 | gs[idx] = data; |
mjr | 32:6e9902f06f48 | 278 | newGSData = true; |
mjr | 28:cb71c4af2912 | 279 | } |
mjr | 28:cb71c4af2912 | 280 | |
mjr | 28:cb71c4af2912 | 281 | private: |
mjr | 28:cb71c4af2912 | 282 | // current level for each output |
mjr | 28:cb71c4af2912 | 283 | unsigned short *gs; |
mjr | 28:cb71c4af2912 | 284 | |
mjr | 32:6e9902f06f48 | 285 | // Simple DMA interface object |
mjr | 32:6e9902f06f48 | 286 | SimpleDMA sdma; |
mjr | 32:6e9902f06f48 | 287 | |
mjr | 32:6e9902f06f48 | 288 | // DMA transfer buffer. Each time we have data to transmit to the TLC5940 chips, |
mjr | 32:6e9902f06f48 | 289 | // we format the data into this buffer exactly as it will go across the wire, then |
mjr | 32:6e9902f06f48 | 290 | // hand the buffer to the DMA controller to move through the SPI port. |
mjr | 32:6e9902f06f48 | 291 | char *dmabuf; |
mjr | 32:6e9902f06f48 | 292 | |
mjr | 28:cb71c4af2912 | 293 | // SPI port - only MOSI and SCK are used |
mjr | 28:cb71c4af2912 | 294 | SPI spi; |
mjr | 28:cb71c4af2912 | 295 | |
mjr | 28:cb71c4af2912 | 296 | // use a PWM out for the grayscale clock - this provides a stable |
mjr | 28:cb71c4af2912 | 297 | // square wave signal without consuming CPU |
mjr | 28:cb71c4af2912 | 298 | FastPWM gsclk; |
mjr | 28:cb71c4af2912 | 299 | |
mjr | 28:cb71c4af2912 | 300 | // Digital out pins used for the TLC5940 |
mjr | 28:cb71c4af2912 | 301 | DigitalOut blank; |
mjr | 28:cb71c4af2912 | 302 | DigitalOut xlat; |
mjr | 28:cb71c4af2912 | 303 | |
mjr | 28:cb71c4af2912 | 304 | // number of daisy-chained TLC5940s we're controlling |
mjr | 28:cb71c4af2912 | 305 | int nchips; |
mjr | 28:cb71c4af2912 | 306 | |
mjr | 28:cb71c4af2912 | 307 | // Timeout to end each PWM cycle. This is a one-shot timer that we reset |
mjr | 28:cb71c4af2912 | 308 | // on each cycle. |
mjr | 28:cb71c4af2912 | 309 | Timeout reset_timer; |
mjr | 28:cb71c4af2912 | 310 | |
mjr | 28:cb71c4af2912 | 311 | // Has new GS/DC data been loaded? |
mjr | 28:cb71c4af2912 | 312 | volatile bool newGSData; |
mjr | 35:d832bcab089e | 313 | |
mjr | 35:d832bcab089e | 314 | // Do we need an XLAT signal on the next blanking interval? |
mjr | 35:d832bcab089e | 315 | volatile bool needXlat; |
mjr | 28:cb71c4af2912 | 316 | |
mjr | 28:cb71c4af2912 | 317 | // Function to reset the display and send the next chunks of data |
mjr | 28:cb71c4af2912 | 318 | void reset() |
mjr | 28:cb71c4af2912 | 319 | { |
mjr | 32:6e9902f06f48 | 320 | // start the blanking cycle |
mjr | 32:6e9902f06f48 | 321 | startBlank(); |
mjr | 35:d832bcab089e | 322 | |
mjr | 35:d832bcab089e | 323 | #if DATA_UPDATE_INSIDE_BLANKING |
mjr | 35:d832bcab089e | 324 | // We're configured to send the new GS data entirely within |
mjr | 35:d832bcab089e | 325 | // the blanking interval. Start the DMA transfer now, and |
mjr | 35:d832bcab089e | 326 | // return without ending the blanking interval. The DMA |
mjr | 35:d832bcab089e | 327 | // completion interrupt handler will do that when the data |
mjr | 35:d832bcab089e | 328 | // update has completed. |
mjr | 35:d832bcab089e | 329 | // |
mjr | 35:d832bcab089e | 330 | // Note that we do the data update/ unconditionally in the |
mjr | 35:d832bcab089e | 331 | // send-during-blanking case, whether or not we have new GS |
mjr | 35:d832bcab089e | 332 | // data. This is because the update causes a 0.3% reduction |
mjr | 35:d832bcab089e | 333 | // in brightness because of the elongated BLANK interval. |
mjr | 35:d832bcab089e | 334 | // That would be visible as a flicker on each update if we |
mjr | 35:d832bcab089e | 335 | // did updates on some cycles and not others. By doing an |
mjr | 35:d832bcab089e | 336 | // update on every cycle, we make the brightness reduction |
mjr | 35:d832bcab089e | 337 | // uniform across time, which makes it less perceptible. |
mjr | 35:d832bcab089e | 338 | update(); |
mjr | 35:d832bcab089e | 339 | |
mjr | 35:d832bcab089e | 340 | #else // DATA_UPDATE_INSIDE_BLANKING |
mjr | 35:d832bcab089e | 341 | |
mjr | 35:d832bcab089e | 342 | // end the blanking interval |
mjr | 35:d832bcab089e | 343 | endBlank(); |
mjr | 35:d832bcab089e | 344 | |
mjr | 35:d832bcab089e | 345 | // if we have pending grayscale data, start sending it |
mjr | 35:d832bcab089e | 346 | if (newGSData) |
mjr | 28:cb71c4af2912 | 347 | update(); |
mjr | 28:cb71c4af2912 | 348 | |
mjr | 35:d832bcab089e | 349 | #endif // DATA_UPDATE_INSIDE_BLANKING |
mjr | 32:6e9902f06f48 | 350 | } |
mjr | 32:6e9902f06f48 | 351 | |
mjr | 32:6e9902f06f48 | 352 | void startBlank() |
mjr | 32:6e9902f06f48 | 353 | { |
mjr | 32:6e9902f06f48 | 354 | // turn off the grayscale clock, and assert BLANK to end the grayscale cycle |
mjr | 32:6e9902f06f48 | 355 | gsclk.write(0); |
mjr | 32:6e9902f06f48 | 356 | blank = 1; |
mjr | 32:6e9902f06f48 | 357 | } |
mjr | 28:cb71c4af2912 | 358 | |
mjr | 35:d832bcab089e | 359 | void endBlank() |
mjr | 32:6e9902f06f48 | 360 | { |
mjr | 35:d832bcab089e | 361 | // if we've sent new grayscale data since the last blanking |
mjr | 35:d832bcab089e | 362 | // interval, latch it by asserting XLAT |
mjr | 35:d832bcab089e | 363 | if (needXlat) |
mjr | 32:6e9902f06f48 | 364 | { |
mjr | 28:cb71c4af2912 | 365 | // latch the new data while we're still blanked |
mjr | 28:cb71c4af2912 | 366 | xlat = 1; |
mjr | 28:cb71c4af2912 | 367 | xlat = 0; |
mjr | 35:d832bcab089e | 368 | needXlat = false; |
mjr | 28:cb71c4af2912 | 369 | } |
mjr | 28:cb71c4af2912 | 370 | |
mjr | 28:cb71c4af2912 | 371 | // end the blanking interval and restart the grayscale clock |
mjr | 28:cb71c4af2912 | 372 | blank = 0; |
mjr | 28:cb71c4af2912 | 373 | gsclk.write(.5); |
mjr | 28:cb71c4af2912 | 374 | |
mjr | 28:cb71c4af2912 | 375 | // set up the next blanking interrupt |
mjr | 28:cb71c4af2912 | 376 | reset_timer.attach(this, &TLC5940::reset, (1.0/GSCLK_SPEED)*4096.0); |
mjr | 28:cb71c4af2912 | 377 | } |
mjr | 28:cb71c4af2912 | 378 | |
mjr | 28:cb71c4af2912 | 379 | void update() |
mjr | 28:cb71c4af2912 | 380 | { |
mjr | 32:6e9902f06f48 | 381 | // Send new grayscale data to the TLC5940 chips. |
mjr | 32:6e9902f06f48 | 382 | // |
mjr | 32:6e9902f06f48 | 383 | // To do this, we set up our DMA buffer with the bytes formatted exactly |
mjr | 32:6e9902f06f48 | 384 | // as they will go across the wire, then kick off the transfer request with |
mjr | 32:6e9902f06f48 | 385 | // the DMA controller. We can then return from the interrupt and continue |
mjr | 32:6e9902f06f48 | 386 | // with other tasks while the DMA hardware handles the transfer for us. |
mjr | 32:6e9902f06f48 | 387 | // When the transfer is completed, the DMA controller will fire an |
mjr | 32:6e9902f06f48 | 388 | // interrupt, which will call our interrupt handler, which will finish |
mjr | 32:6e9902f06f48 | 389 | // the blanking cycle. |
mjr | 32:6e9902f06f48 | 390 | // |
mjr | 32:6e9902f06f48 | 391 | // The serial format orders the outputs from last to first (output #15 on |
mjr | 32:6e9902f06f48 | 392 | // the last chip in the daisy-chain to output #0 on the first chip). For |
mjr | 32:6e9902f06f48 | 393 | // each output, we send 12 bits containing the grayscale level (0 = fully |
mjr | 32:6e9902f06f48 | 394 | // off, 0xFFF = fully on). Bit order is most significant bit first. |
mjr | 28:cb71c4af2912 | 395 | // |
mjr | 28:cb71c4af2912 | 396 | // The KL25Z SPI can only send in 8-bit increments, so we need to divvy up |
mjr | 28:cb71c4af2912 | 397 | // the 12-bit outputs into 8-bit bytes. Each pair of 12-bit outputs adds up |
mjr | 28:cb71c4af2912 | 398 | // to 24 bits, which divides evenly into 3 bytes, so send each pairs of |
mjr | 28:cb71c4af2912 | 399 | // outputs as three bytes: |
mjr | 28:cb71c4af2912 | 400 | // |
mjr | 28:cb71c4af2912 | 401 | // [ element i+1 bits ] [ element i bits ] |
mjr | 28:cb71c4af2912 | 402 | // 11 10 9 8 7 6 5 4 3 2 1 0 11 10 9 8 7 6 5 4 3 2 1 0 |
mjr | 28:cb71c4af2912 | 403 | // [ first byte ] [ second byte ] [ third byte ] |
mjr | 32:6e9902f06f48 | 404 | for (int i = (16 * nchips) - 2, dst = 0 ; i >= 0 ; i -= 2) |
mjr | 28:cb71c4af2912 | 405 | { |
mjr | 28:cb71c4af2912 | 406 | // first byte - element i+1 bits 4-11 |
mjr | 32:6e9902f06f48 | 407 | dmabuf[dst++] = (((gs[i+1] & 0xFF0) >> 4) & 0xff); |
mjr | 28:cb71c4af2912 | 408 | |
mjr | 28:cb71c4af2912 | 409 | // second byte - element i+1 bits 0-3, then element i bits 8-11 |
mjr | 32:6e9902f06f48 | 410 | dmabuf[dst++] = ((((gs[i+1] & 0x00F) << 4) | ((gs[i] & 0xF00) >> 8)) & 0xFF); |
mjr | 28:cb71c4af2912 | 411 | |
mjr | 28:cb71c4af2912 | 412 | // third byte - element i bits 0-7 |
mjr | 32:6e9902f06f48 | 413 | dmabuf[dst++] = (gs[i] & 0x0FF); |
mjr | 28:cb71c4af2912 | 414 | } |
mjr | 32:6e9902f06f48 | 415 | |
mjr | 32:6e9902f06f48 | 416 | // Start the DMA transfer |
mjr | 32:6e9902f06f48 | 417 | sdma.start(nchips*24); |
mjr | 35:d832bcab089e | 418 | |
mjr | 35:d832bcab089e | 419 | // we've now cleared the new GS data |
mjr | 35:d832bcab089e | 420 | newGSData = false; |
mjr | 28:cb71c4af2912 | 421 | } |
mjr | 32:6e9902f06f48 | 422 | |
mjr | 32:6e9902f06f48 | 423 | // Interrupt handler for DMA completion. The DMA controller calls this |
mjr | 32:6e9902f06f48 | 424 | // when it finishes with the transfer request we set up above. When the |
mjr | 32:6e9902f06f48 | 425 | // transfer is done, we simply end the blanking cycle and start a new |
mjr | 32:6e9902f06f48 | 426 | // grayscale cycle. |
mjr | 32:6e9902f06f48 | 427 | void dmaDone() |
mjr | 32:6e9902f06f48 | 428 | { |
mjr | 35:d832bcab089e | 429 | // mark that we need to assert XLAT to latch the new |
mjr | 35:d832bcab089e | 430 | // grayscale data during the next blanking interval |
mjr | 35:d832bcab089e | 431 | needXlat = true; |
mjr | 35:d832bcab089e | 432 | |
mjr | 35:d832bcab089e | 433 | #if DATA_UPDATE_INSIDE_BLANKING |
mjr | 35:d832bcab089e | 434 | // we're doing the gs update within the blanking cycle, so end |
mjr | 35:d832bcab089e | 435 | // the blanking cycle now that the transfer has completed |
mjr | 35:d832bcab089e | 436 | endBlank(); |
mjr | 35:d832bcab089e | 437 | #endif |
mjr | 32:6e9902f06f48 | 438 | } |
mjr | 32:6e9902f06f48 | 439 | |
mjr | 28:cb71c4af2912 | 440 | }; |
mjr | 28:cb71c4af2912 | 441 | |
mjr | 28:cb71c4af2912 | 442 | #endif |