work in progress
Dependencies: FastAnalogIn FastIO USBDevice mbed FastPWM SimpleDMA
Fork of Pinscape_Controller by
TLC5940/TLC5940.h@32:6e9902f06f48, 2015-09-25 (annotated)
- Committer:
- mjr
- Date:
- Fri Sep 25 21:28:31 2015 +0000
- Revision:
- 32:6e9902f06f48
- Parent:
- 31:582472d0bc57
- Child:
- 35:d832bcab089e
Use DMA for TLC5940 SPI transfer to reduce time interrupt handler (fixes problem with MMA8415Q freezing up). All LedWiz flashing modes now fully supported.
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 | 28:cb71c4af2912 | 23 | #include "mbed.h" |
mjr | 28:cb71c4af2912 | 24 | #include "FastPWM.h" |
mjr | 32:6e9902f06f48 | 25 | #include "SimpleDMA.h" |
mjr | 28:cb71c4af2912 | 26 | |
mjr | 28:cb71c4af2912 | 27 | /** |
mjr | 28:cb71c4af2912 | 28 | * SPI speed used by the mbed to communicate with the TLC5940 |
mjr | 28:cb71c4af2912 | 29 | * The TLC5940 supports up to 30Mhz. It's best to keep this as |
mjr | 28:cb71c4af2912 | 30 | * high as the microcontroller will allow, since a higher SPI |
mjr | 28:cb71c4af2912 | 31 | * speed yields a faster grayscale data update. However, if |
mjr | 28:cb71c4af2912 | 32 | * you have problems with unreliable signal transmission to the |
mjr | 28:cb71c4af2912 | 33 | * TLC5940s, reducing this speed might help. |
mjr | 28:cb71c4af2912 | 34 | * |
mjr | 28:cb71c4af2912 | 35 | * The SPI clock must be fast enough that the data transmission |
mjr | 28:cb71c4af2912 | 36 | * time for a full update is comfortably less than the blanking |
mjr | 28:cb71c4af2912 | 37 | * cycle time. The grayscale refresh requires 192 bits per TLC5940 |
mjr | 28:cb71c4af2912 | 38 | * in the daisy chain, and each bit takes one SPI clock to send. |
mjr | 28:cb71c4af2912 | 39 | * Our reference setup in the Pinscape controller allows for up to |
mjr | 28:cb71c4af2912 | 40 | * 4 TLC5940s, so a full refresh cycle on a fully populated system |
mjr | 28:cb71c4af2912 | 41 | * would be 768 SPI clocks. The blanking cycle is 4096 GSCLK cycles. |
mjr | 28:cb71c4af2912 | 42 | * |
mjr | 28:cb71c4af2912 | 43 | * t(blank) = 4096 * 1/GSCLK_SPEED |
mjr | 28:cb71c4af2912 | 44 | * t(refresh) = 768 * 1/SPI_SPEED |
mjr | 28:cb71c4af2912 | 45 | * Therefore: SPI_SPEED must be > 768/4096 * GSCLK_SPEED |
mjr | 28:cb71c4af2912 | 46 | * |
mjr | 28:cb71c4af2912 | 47 | * Since the SPI speed can be so high, and since we want to keep |
mjr | 28:cb71c4af2912 | 48 | * the GSCLK speed relatively low, the constraint above simply |
mjr | 28:cb71c4af2912 | 49 | * isn't a factor. E.g., at SPI=30MHz and GSCLK=500kHz, |
mjr | 28:cb71c4af2912 | 50 | * t(blank) is 8192us and t(refresh) is 25us. |
mjr | 28:cb71c4af2912 | 51 | */ |
mjr | 28:cb71c4af2912 | 52 | #define SPI_SPEED 3000000 |
mjr | 28:cb71c4af2912 | 53 | |
mjr | 28:cb71c4af2912 | 54 | /** |
mjr | 28:cb71c4af2912 | 55 | * The rate at which the GSCLK pin is pulsed. This also controls |
mjr | 28:cb71c4af2912 | 56 | * how often the reset function is called. The reset function call |
mjr | 28:cb71c4af2912 | 57 | * rate is (1/GSCLK_SPEED) * 4096. The maximum reliable rate is |
mjr | 28:cb71c4af2912 | 58 | * around 32Mhz. It's best to keep this rate as low as possible: |
mjr | 28:cb71c4af2912 | 59 | * the higher the rate, the higher the refresh() call frequency, |
mjr | 28:cb71c4af2912 | 60 | * so the higher the CPU load. |
mjr | 28:cb71c4af2912 | 61 | * |
mjr | 28:cb71c4af2912 | 62 | * The lower bound is probably dependent on the application. For |
mjr | 28:cb71c4af2912 | 63 | * driving LEDs, the limiting factor is that lower rates will increase |
mjr | 28:cb71c4af2912 | 64 | * visible flicker. 200 kHz seems to be a good lower bound for LEDs. |
mjr | 28:cb71c4af2912 | 65 | * That provides about 48 cycles per second - that's about the same as |
mjr | 28:cb71c4af2912 | 66 | * the 50 Hz A/C cycle rate in many countries, which was itself chosen |
mjr | 28:cb71c4af2912 | 67 | * so that incandescent lights don't flicker. (This rate is a function |
mjr | 28:cb71c4af2912 | 68 | * of human eye physiology, which has its own refresh cycle of sorts |
mjr | 28:cb71c4af2912 | 69 | * that runs at about 50 Hz. If you're designing an LED system for |
mjr | 28:cb71c4af2912 | 70 | * viewing by cats or drosophila, you might want to look into your |
mjr | 28:cb71c4af2912 | 71 | * target species' eye physiology, since the persistence of vision |
mjr | 28:cb71c4af2912 | 72 | * rate varies quite a bit from species to species.) Flicker tends to |
mjr | 28:cb71c4af2912 | 73 | * be more noticeable in LEDs than in incandescents, since LEDs don't |
mjr | 28:cb71c4af2912 | 74 | * have the thermal inertia of incandescents, so we use a slightly |
mjr | 28:cb71c4af2912 | 75 | * higher default here. 500 kHz = 122 full grayscale cycles per |
mjr | 28:cb71c4af2912 | 76 | * second = 122 reset calls per second (call every 8ms). |
mjr | 28:cb71c4af2912 | 77 | */ |
mjr | 28:cb71c4af2912 | 78 | #define GSCLK_SPEED 500000 |
mjr | 28:cb71c4af2912 | 79 | |
mjr | 28:cb71c4af2912 | 80 | /** |
mjr | 28:cb71c4af2912 | 81 | * This class controls a TLC5940 PWM driver IC. |
mjr | 28:cb71c4af2912 | 82 | * |
mjr | 28:cb71c4af2912 | 83 | * Using the TLC5940 class to control an LED: |
mjr | 28:cb71c4af2912 | 84 | * @code |
mjr | 28:cb71c4af2912 | 85 | * #include "mbed.h" |
mjr | 28:cb71c4af2912 | 86 | * #include "TLC5940.h" |
mjr | 28:cb71c4af2912 | 87 | * |
mjr | 28:cb71c4af2912 | 88 | * // Create the TLC5940 instance |
mjr | 28:cb71c4af2912 | 89 | * TLC5940 tlc(p7, p5, p21, p9, p10, p11, p12, 1); |
mjr | 28:cb71c4af2912 | 90 | * |
mjr | 28:cb71c4af2912 | 91 | * int main() |
mjr | 28:cb71c4af2912 | 92 | * { |
mjr | 28:cb71c4af2912 | 93 | * // Enable the first LED |
mjr | 28:cb71c4af2912 | 94 | * tlc.set(0, 0xfff); |
mjr | 28:cb71c4af2912 | 95 | * |
mjr | 28:cb71c4af2912 | 96 | * while(1) |
mjr | 28:cb71c4af2912 | 97 | * { |
mjr | 28:cb71c4af2912 | 98 | * } |
mjr | 28:cb71c4af2912 | 99 | * } |
mjr | 28:cb71c4af2912 | 100 | * @endcode |
mjr | 28:cb71c4af2912 | 101 | */ |
mjr | 28:cb71c4af2912 | 102 | class TLC5940 |
mjr | 28:cb71c4af2912 | 103 | { |
mjr | 28:cb71c4af2912 | 104 | public: |
mjr | 28:cb71c4af2912 | 105 | /** |
mjr | 28:cb71c4af2912 | 106 | * Set up the TLC5940 |
mjr | 28:cb71c4af2912 | 107 | * @param SCLK - The SCK pin of the SPI bus |
mjr | 28:cb71c4af2912 | 108 | * @param MOSI - The MOSI pin of the SPI bus |
mjr | 28:cb71c4af2912 | 109 | * @param GSCLK - The GSCLK pin of the TLC5940(s) |
mjr | 28:cb71c4af2912 | 110 | * @param BLANK - The BLANK pin of the TLC5940(s) |
mjr | 28:cb71c4af2912 | 111 | * @param XLAT - The XLAT pin of the TLC5940(s) |
mjr | 28:cb71c4af2912 | 112 | * @param nchips - The number of TLC5940s (if you are daisy chaining) |
mjr | 28:cb71c4af2912 | 113 | */ |
mjr | 28:cb71c4af2912 | 114 | TLC5940(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, PinName XLAT, int nchips) |
mjr | 28:cb71c4af2912 | 115 | : spi(MOSI, NC, SCLK), |
mjr | 28:cb71c4af2912 | 116 | gsclk(GSCLK), |
mjr | 28:cb71c4af2912 | 117 | blank(BLANK), |
mjr | 28:cb71c4af2912 | 118 | xlat(XLAT), |
mjr | 28:cb71c4af2912 | 119 | nchips(nchips), |
mjr | 30:2097c6f8f2db | 120 | newGSData(true) |
mjr | 28:cb71c4af2912 | 121 | { |
mjr | 32:6e9902f06f48 | 122 | // Set initial output pin states - XLAT off, BLANK on (BLANK turns off |
mjr | 32:6e9902f06f48 | 123 | // all of the outputs while we're setting up) |
mjr | 32:6e9902f06f48 | 124 | xlat = 0; |
mjr | 32:6e9902f06f48 | 125 | blank = 1; |
mjr | 32:6e9902f06f48 | 126 | |
mjr | 28:cb71c4af2912 | 127 | // allocate the grayscale buffer |
mjr | 28:cb71c4af2912 | 128 | gs = new unsigned short[nchips*16]; |
mjr | 30:2097c6f8f2db | 129 | memset(gs, 0, nchips*16*sizeof(gs[0])); |
mjr | 28:cb71c4af2912 | 130 | |
mjr | 28:cb71c4af2912 | 131 | // Configure SPI format and speed. Note that KL25Z ONLY supports 8-bit |
mjr | 28:cb71c4af2912 | 132 | // mode. The TLC5940 nominally requires 12-bit data blocks for the |
mjr | 28:cb71c4af2912 | 133 | // grayscale levels, but SPI is ultimately just a bit-level serial format, |
mjr | 28:cb71c4af2912 | 134 | // so we can reformat the 12-bit blocks into 8-bit bytes to fit the |
mjr | 28:cb71c4af2912 | 135 | // KL25Z's limits. This should work equally well on other microcontrollers |
mjr | 28:cb71c4af2912 | 136 | // that are more flexible. The TLC5940 appears to require polarity/phase |
mjr | 28:cb71c4af2912 | 137 | // format 0. |
mjr | 28:cb71c4af2912 | 138 | spi.format(8, 0); |
mjr | 28:cb71c4af2912 | 139 | spi.frequency(SPI_SPEED); |
mjr | 31:582472d0bc57 | 140 | |
mjr | 32:6e9902f06f48 | 141 | // Allocate a DMA buffer. The transfer on each cycle is 192 bits per |
mjr | 32:6e9902f06f48 | 142 | // chip = 24 bytes per chip. |
mjr | 32:6e9902f06f48 | 143 | dmabuf = new char[nchips*24]; |
mjr | 28:cb71c4af2912 | 144 | |
mjr | 32:6e9902f06f48 | 145 | // Set up the Simple DMA interface object. We use the DMA controller to |
mjr | 32:6e9902f06f48 | 146 | // send grayscale data updates to the TLC5940 chips. This lets the CPU |
mjr | 32:6e9902f06f48 | 147 | // keep running other tasks while we send gs updates, and importantly |
mjr | 32:6e9902f06f48 | 148 | // allows our blanking interrupt handler return almost immediately. |
mjr | 32:6e9902f06f48 | 149 | // The DMA transfer is from our internal DMA buffer to SPI0, which is |
mjr | 32:6e9902f06f48 | 150 | // the SPI controller physically connected to the TLC5940s. |
mjr | 32:6e9902f06f48 | 151 | sdma.source(dmabuf, 1); |
mjr | 32:6e9902f06f48 | 152 | sdma.destination(&(SPI0->D), 0, 8); |
mjr | 32:6e9902f06f48 | 153 | sdma.trigger(Trigger_SPI0_TX); |
mjr | 32:6e9902f06f48 | 154 | sdma.attach(this, &TLC5940::dmaDone); |
mjr | 32:6e9902f06f48 | 155 | |
mjr | 32:6e9902f06f48 | 156 | // Enable DMA on SPI0. SimpleDMA doesn't do this for us; we have to |
mjr | 32:6e9902f06f48 | 157 | // do it explicitly. This is just a matter of setting bit 5 (TXDMAE) |
mjr | 32:6e9902f06f48 | 158 | // in the SPI controllers Control Register 2 (C2). |
mjr | 32:6e9902f06f48 | 159 | SPI0->C2 |= 0x20; // set bit 5 = 0x20 = TXDMAE in SPI0 control register 2 |
mjr | 32:6e9902f06f48 | 160 | |
mjr | 32:6e9902f06f48 | 161 | // Configure the GSCLK output's frequency |
mjr | 28:cb71c4af2912 | 162 | gsclk.period(1.0/GSCLK_SPEED); |
mjr | 32:6e9902f06f48 | 163 | } |
mjr | 31:582472d0bc57 | 164 | |
mjr | 32:6e9902f06f48 | 165 | // Start the clock running |
mjr | 31:582472d0bc57 | 166 | void start() |
mjr | 31:582472d0bc57 | 167 | { |
mjr | 28:cb71c4af2912 | 168 | // Set up the first call to the reset function, which asserts BLANK to |
mjr | 28:cb71c4af2912 | 169 | // end the PWM cycle and handles new grayscale data output and latching. |
mjr | 28:cb71c4af2912 | 170 | // The original version of this library uses a timer to call reset |
mjr | 28:cb71c4af2912 | 171 | // periodically, but that approach is somewhat problematic because the |
mjr | 28:cb71c4af2912 | 172 | // reset function itself takes a small amount of time to run, so the |
mjr | 28:cb71c4af2912 | 173 | // *actual* cycle is slightly longer than what we get from counting |
mjr | 28:cb71c4af2912 | 174 | // GS clocks. Running reset on a timer therefore causes the calls to |
mjr | 28:cb71c4af2912 | 175 | // slip out of phase with the actual full cycles, which causes |
mjr | 28:cb71c4af2912 | 176 | // premature blanking that shows up as visible flicker. To get the |
mjr | 28:cb71c4af2912 | 177 | // reset cycle to line up exactly with a full PWM cycle, it works |
mjr | 28:cb71c4af2912 | 178 | // better to set up a new timer on each cycle, *after* we've finished |
mjr | 28:cb71c4af2912 | 179 | // with the somewhat unpredictable overhead of the interrupt handler. |
mjr | 28:cb71c4af2912 | 180 | // This ensures that we'll get much closer to exact alignment of the |
mjr | 28:cb71c4af2912 | 181 | // cycle phase, and in any case the worst that happens is that some |
mjr | 28:cb71c4af2912 | 182 | // cycles are very slightly too long or short (due to imperfections |
mjr | 28:cb71c4af2912 | 183 | // in the timer clock vs the PWM clock that determines the GSCLCK |
mjr | 28:cb71c4af2912 | 184 | // output to the TLC5940), which is far less noticeable than a |
mjr | 28:cb71c4af2912 | 185 | // constantly rotating phase misalignment. |
mjr | 28:cb71c4af2912 | 186 | reset_timer.attach(this, &TLC5940::reset, (1.0/GSCLK_SPEED)*4096.0); |
mjr | 28:cb71c4af2912 | 187 | } |
mjr | 28:cb71c4af2912 | 188 | |
mjr | 28:cb71c4af2912 | 189 | ~TLC5940() |
mjr | 28:cb71c4af2912 | 190 | { |
mjr | 28:cb71c4af2912 | 191 | delete [] gs; |
mjr | 32:6e9902f06f48 | 192 | delete [] dmabuf; |
mjr | 28:cb71c4af2912 | 193 | } |
mjr | 28:cb71c4af2912 | 194 | |
mjr | 28:cb71c4af2912 | 195 | /** |
mjr | 28:cb71c4af2912 | 196 | * Set the next chunk of grayscale data to be sent |
mjr | 28:cb71c4af2912 | 197 | * @param data - Array of 16 bit shorts containing 16 12 bit grayscale data chunks per TLC5940 |
mjr | 28:cb71c4af2912 | 198 | * @note These must be in intervals of at least (1/GSCLK_SPEED) * 4096 to be sent |
mjr | 28:cb71c4af2912 | 199 | */ |
mjr | 28:cb71c4af2912 | 200 | void set(int idx, unsigned short data) |
mjr | 28:cb71c4af2912 | 201 | { |
mjr | 28:cb71c4af2912 | 202 | // store the data, and flag the pending update for the interrupt handler to carry out |
mjr | 28:cb71c4af2912 | 203 | gs[idx] = data; |
mjr | 32:6e9902f06f48 | 204 | newGSData = true; |
mjr | 28:cb71c4af2912 | 205 | } |
mjr | 28:cb71c4af2912 | 206 | |
mjr | 28:cb71c4af2912 | 207 | private: |
mjr | 28:cb71c4af2912 | 208 | // current level for each output |
mjr | 28:cb71c4af2912 | 209 | unsigned short *gs; |
mjr | 28:cb71c4af2912 | 210 | |
mjr | 32:6e9902f06f48 | 211 | // Simple DMA interface object |
mjr | 32:6e9902f06f48 | 212 | SimpleDMA sdma; |
mjr | 32:6e9902f06f48 | 213 | |
mjr | 32:6e9902f06f48 | 214 | // DMA transfer buffer. Each time we have data to transmit to the TLC5940 chips, |
mjr | 32:6e9902f06f48 | 215 | // we format the data into this buffer exactly as it will go across the wire, then |
mjr | 32:6e9902f06f48 | 216 | // hand the buffer to the DMA controller to move through the SPI port. |
mjr | 32:6e9902f06f48 | 217 | char *dmabuf; |
mjr | 32:6e9902f06f48 | 218 | |
mjr | 28:cb71c4af2912 | 219 | // SPI port - only MOSI and SCK are used |
mjr | 28:cb71c4af2912 | 220 | SPI spi; |
mjr | 28:cb71c4af2912 | 221 | |
mjr | 28:cb71c4af2912 | 222 | // use a PWM out for the grayscale clock - this provides a stable |
mjr | 28:cb71c4af2912 | 223 | // square wave signal without consuming CPU |
mjr | 28:cb71c4af2912 | 224 | FastPWM gsclk; |
mjr | 28:cb71c4af2912 | 225 | |
mjr | 28:cb71c4af2912 | 226 | // Digital out pins used for the TLC5940 |
mjr | 28:cb71c4af2912 | 227 | DigitalOut blank; |
mjr | 28:cb71c4af2912 | 228 | DigitalOut xlat; |
mjr | 28:cb71c4af2912 | 229 | |
mjr | 28:cb71c4af2912 | 230 | // number of daisy-chained TLC5940s we're controlling |
mjr | 28:cb71c4af2912 | 231 | int nchips; |
mjr | 28:cb71c4af2912 | 232 | |
mjr | 28:cb71c4af2912 | 233 | // Timeout to end each PWM cycle. This is a one-shot timer that we reset |
mjr | 28:cb71c4af2912 | 234 | // on each cycle. |
mjr | 28:cb71c4af2912 | 235 | Timeout reset_timer; |
mjr | 28:cb71c4af2912 | 236 | |
mjr | 28:cb71c4af2912 | 237 | // Has new GS/DC data been loaded? |
mjr | 28:cb71c4af2912 | 238 | volatile bool newGSData; |
mjr | 28:cb71c4af2912 | 239 | |
mjr | 28:cb71c4af2912 | 240 | // Function to reset the display and send the next chunks of data |
mjr | 28:cb71c4af2912 | 241 | void reset() |
mjr | 28:cb71c4af2912 | 242 | { |
mjr | 32:6e9902f06f48 | 243 | // start the blanking cycle |
mjr | 32:6e9902f06f48 | 244 | startBlank(); |
mjr | 28:cb71c4af2912 | 245 | |
mjr | 28:cb71c4af2912 | 246 | // If we have new GS data, send it now |
mjr | 32:6e9902f06f48 | 247 | if (true) |
mjr | 28:cb71c4af2912 | 248 | { |
mjr | 28:cb71c4af2912 | 249 | // Send the new grayscale data. |
mjr | 28:cb71c4af2912 | 250 | // |
mjr | 28:cb71c4af2912 | 251 | // Note that ideally, we'd do this during the new PWM cycle |
mjr | 28:cb71c4af2912 | 252 | // rather than during the blanking interval. The TLC5940 is |
mjr | 28:cb71c4af2912 | 253 | // specifically designed to allow this. However, in my testing, |
mjr | 28:cb71c4af2912 | 254 | // I found that sending new data during the PWM cycle was |
mjr | 28:cb71c4af2912 | 255 | // unreliable - it seemed to cause a fair amount of glitching, |
mjr | 28:cb71c4af2912 | 256 | // which as far as I can tell is signal noise coming from |
mjr | 28:cb71c4af2912 | 257 | // crosstalk between the grayscale clock signal and the |
mjr | 28:cb71c4af2912 | 258 | // SPI signal. This seems to be a common problem with |
mjr | 28:cb71c4af2912 | 259 | // daisy-chained TLC5940s. It can in principle be solved with |
mjr | 28:cb71c4af2912 | 260 | // careful high-speed circuit design (good ground planes, |
mjr | 28:cb71c4af2912 | 261 | // short leads, decoupling capacitors), and indeed I was able |
mjr | 28:cb71c4af2912 | 262 | // to improve stability to some extent with circuit tweaks, |
mjr | 28:cb71c4af2912 | 263 | // but I wasn't able to eliminate it entirely. Moving the |
mjr | 28:cb71c4af2912 | 264 | // data refresh into the blanking interval, on the other |
mjr | 28:cb71c4af2912 | 265 | // hand, seems to entirely eliminate any instability. |
mjr | 28:cb71c4af2912 | 266 | // |
mjr | 32:6e9902f06f48 | 267 | // update() will format the current grayscale data into our |
mjr | 32:6e9902f06f48 | 268 | // DMA transfer buffer and kick off the DMA transfer, then |
mjr | 32:6e9902f06f48 | 269 | // return. At that point we can return from the interrupt, |
mjr | 32:6e9902f06f48 | 270 | // but WITHOUT ending the blanking cycle - we want to keep |
mjr | 32:6e9902f06f48 | 271 | // blanking the outputs until the DMA transfer finishes. When |
mjr | 32:6e9902f06f48 | 272 | // the transfer is complete, the DMA controller will fire an |
mjr | 32:6e9902f06f48 | 273 | // interrupt that will trigger our dmaDone() callback, at |
mjr | 32:6e9902f06f48 | 274 | // which point we'll finally complete the blanking cycle and |
mjr | 32:6e9902f06f48 | 275 | // start a new grayscale cycle. |
mjr | 28:cb71c4af2912 | 276 | update(); |
mjr | 28:cb71c4af2912 | 277 | |
mjr | 28:cb71c4af2912 | 278 | // the chips are now in sync with our data, so we have no more |
mjr | 28:cb71c4af2912 | 279 | // pending update |
mjr | 28:cb71c4af2912 | 280 | newGSData = false; |
mjr | 32:6e9902f06f48 | 281 | } |
mjr | 32:6e9902f06f48 | 282 | else |
mjr | 32:6e9902f06f48 | 283 | { |
mjr | 32:6e9902f06f48 | 284 | // no new grayscale data - just end the blanking cycle without |
mjr | 32:6e9902f06f48 | 285 | // a new XLAT |
mjr | 32:6e9902f06f48 | 286 | endBlank(false); |
mjr | 32:6e9902f06f48 | 287 | } |
mjr | 32:6e9902f06f48 | 288 | } |
mjr | 32:6e9902f06f48 | 289 | |
mjr | 32:6e9902f06f48 | 290 | void startBlank() |
mjr | 32:6e9902f06f48 | 291 | { |
mjr | 32:6e9902f06f48 | 292 | // turn off the grayscale clock, and assert BLANK to end the grayscale cycle |
mjr | 32:6e9902f06f48 | 293 | gsclk.write(0); |
mjr | 32:6e9902f06f48 | 294 | blank = 1; |
mjr | 32:6e9902f06f48 | 295 | } |
mjr | 28:cb71c4af2912 | 296 | |
mjr | 32:6e9902f06f48 | 297 | void endBlank(bool needxlat) |
mjr | 32:6e9902f06f48 | 298 | { |
mjr | 32:6e9902f06f48 | 299 | if (needxlat) |
mjr | 32:6e9902f06f48 | 300 | { |
mjr | 28:cb71c4af2912 | 301 | // latch the new data while we're still blanked |
mjr | 28:cb71c4af2912 | 302 | xlat = 1; |
mjr | 28:cb71c4af2912 | 303 | xlat = 0; |
mjr | 28:cb71c4af2912 | 304 | } |
mjr | 28:cb71c4af2912 | 305 | |
mjr | 28:cb71c4af2912 | 306 | // end the blanking interval and restart the grayscale clock |
mjr | 28:cb71c4af2912 | 307 | blank = 0; |
mjr | 28:cb71c4af2912 | 308 | gsclk.write(.5); |
mjr | 28:cb71c4af2912 | 309 | |
mjr | 28:cb71c4af2912 | 310 | // set up the next blanking interrupt |
mjr | 28:cb71c4af2912 | 311 | reset_timer.attach(this, &TLC5940::reset, (1.0/GSCLK_SPEED)*4096.0); |
mjr | 28:cb71c4af2912 | 312 | } |
mjr | 28:cb71c4af2912 | 313 | |
mjr | 28:cb71c4af2912 | 314 | void update() |
mjr | 28:cb71c4af2912 | 315 | { |
mjr | 32:6e9902f06f48 | 316 | // Send new grayscale data to the TLC5940 chips. |
mjr | 32:6e9902f06f48 | 317 | // |
mjr | 32:6e9902f06f48 | 318 | // To do this, we set up our DMA buffer with the bytes formatted exactly |
mjr | 32:6e9902f06f48 | 319 | // as they will go across the wire, then kick off the transfer request with |
mjr | 32:6e9902f06f48 | 320 | // the DMA controller. We can then return from the interrupt and continue |
mjr | 32:6e9902f06f48 | 321 | // with other tasks while the DMA hardware handles the transfer for us. |
mjr | 32:6e9902f06f48 | 322 | // When the transfer is completed, the DMA controller will fire an |
mjr | 32:6e9902f06f48 | 323 | // interrupt, which will call our interrupt handler, which will finish |
mjr | 32:6e9902f06f48 | 324 | // the blanking cycle. |
mjr | 32:6e9902f06f48 | 325 | // |
mjr | 32:6e9902f06f48 | 326 | // The serial format orders the outputs from last to first (output #15 on |
mjr | 32:6e9902f06f48 | 327 | // the last chip in the daisy-chain to output #0 on the first chip). For |
mjr | 32:6e9902f06f48 | 328 | // each output, we send 12 bits containing the grayscale level (0 = fully |
mjr | 32:6e9902f06f48 | 329 | // off, 0xFFF = fully on). Bit order is most significant bit first. |
mjr | 28:cb71c4af2912 | 330 | // |
mjr | 28:cb71c4af2912 | 331 | // The KL25Z SPI can only send in 8-bit increments, so we need to divvy up |
mjr | 28:cb71c4af2912 | 332 | // the 12-bit outputs into 8-bit bytes. Each pair of 12-bit outputs adds up |
mjr | 28:cb71c4af2912 | 333 | // to 24 bits, which divides evenly into 3 bytes, so send each pairs of |
mjr | 28:cb71c4af2912 | 334 | // outputs as three bytes: |
mjr | 28:cb71c4af2912 | 335 | // |
mjr | 28:cb71c4af2912 | 336 | // [ element i+1 bits ] [ element i bits ] |
mjr | 28:cb71c4af2912 | 337 | // 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 | 338 | // [ first byte ] [ second byte ] [ third byte ] |
mjr | 32:6e9902f06f48 | 339 | for (int i = (16 * nchips) - 2, dst = 0 ; i >= 0 ; i -= 2) |
mjr | 28:cb71c4af2912 | 340 | { |
mjr | 28:cb71c4af2912 | 341 | // first byte - element i+1 bits 4-11 |
mjr | 32:6e9902f06f48 | 342 | dmabuf[dst++] = (((gs[i+1] & 0xFF0) >> 4) & 0xff); |
mjr | 28:cb71c4af2912 | 343 | |
mjr | 28:cb71c4af2912 | 344 | // second byte - element i+1 bits 0-3, then element i bits 8-11 |
mjr | 32:6e9902f06f48 | 345 | dmabuf[dst++] = ((((gs[i+1] & 0x00F) << 4) | ((gs[i] & 0xF00) >> 8)) & 0xFF); |
mjr | 28:cb71c4af2912 | 346 | |
mjr | 28:cb71c4af2912 | 347 | // third byte - element i bits 0-7 |
mjr | 32:6e9902f06f48 | 348 | dmabuf[dst++] = (gs[i] & 0x0FF); |
mjr | 28:cb71c4af2912 | 349 | } |
mjr | 32:6e9902f06f48 | 350 | |
mjr | 32:6e9902f06f48 | 351 | // Start the DMA transfer |
mjr | 32:6e9902f06f48 | 352 | sdma.start(nchips*24); |
mjr | 28:cb71c4af2912 | 353 | } |
mjr | 32:6e9902f06f48 | 354 | |
mjr | 32:6e9902f06f48 | 355 | // Interrupt handler for DMA completion. The DMA controller calls this |
mjr | 32:6e9902f06f48 | 356 | // when it finishes with the transfer request we set up above. When the |
mjr | 32:6e9902f06f48 | 357 | // transfer is done, we simply end the blanking cycle and start a new |
mjr | 32:6e9902f06f48 | 358 | // grayscale cycle. |
mjr | 32:6e9902f06f48 | 359 | void dmaDone() |
mjr | 32:6e9902f06f48 | 360 | { |
mjr | 32:6e9902f06f48 | 361 | // when the DMA transfer is finished, start the next grayscale cycle |
mjr | 32:6e9902f06f48 | 362 | endBlank(true); |
mjr | 32:6e9902f06f48 | 363 | } |
mjr | 32:6e9902f06f48 | 364 | |
mjr | 28:cb71c4af2912 | 365 | }; |
mjr | 28:cb71c4af2912 | 366 | |
mjr | 28:cb71c4af2912 | 367 | #endif |