DMA-enabled high data rate driver for Heroic Robotics LED strips.
FastPixelLPD8806.cpp@10:5b3be78ce6bd, 2014-05-31 (annotated)
- Committer:
- heroic
- Date:
- Sat May 31 18:41:56 2014 +0000
- Revision:
- 10:5b3be78ce6bd
Initial commit of FastPixelLPD8806 open source driver.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
heroic | 10:5b3be78ce6bd | 1 | /***************************************************************************/ |
heroic | 10:5b3be78ce6bd | 2 | // DMA-enabled high data rate driver for Heroic Robotics FastPixel strips. |
heroic | 10:5b3be78ce6bd | 3 | // Written by Jas Strong <jasmine@heroicrobotics.com> |
heroic | 10:5b3be78ce6bd | 4 | // Copyright (c) 2013-2014 Heroic Robotics, Inc. |
heroic | 10:5b3be78ce6bd | 5 | // |
heroic | 10:5b3be78ce6bd | 6 | // FastPixel is a family of high performance LED strips available from |
heroic | 10:5b3be78ce6bd | 7 | // Heroic Robotics, Inc. http://heroicrobotics.com/ - check them out! |
heroic | 10:5b3be78ce6bd | 8 | // |
heroic | 10:5b3be78ce6bd | 9 | // These strips are capable of supporting 18 Mbit/sec when run on PixelPusher |
heroic | 10:5b3be78ce6bd | 10 | // hardware; your performance on mbed is likely to be somewhat lower due to |
heroic | 10:5b3be78ce6bd | 11 | // the lack of high performance driver chips on the SPI lines. |
heroic | 10:5b3be78ce6bd | 12 | // |
heroic | 10:5b3be78ce6bd | 13 | // Heroic Robotics also sells a family of dedicated LED controllers called |
heroic | 10:5b3be78ce6bd | 14 | // PixelPusher- almost certainly the best LED controller in the world. |
heroic | 10:5b3be78ce6bd | 15 | // |
heroic | 10:5b3be78ce6bd | 16 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
heroic | 10:5b3be78ce6bd | 17 | // of this software and associated documentation files (the "Software"), to deal |
heroic | 10:5b3be78ce6bd | 18 | // in the Software without restriction, including without limitation the rights |
heroic | 10:5b3be78ce6bd | 19 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
heroic | 10:5b3be78ce6bd | 20 | // copies of the Software, and to permit persons to whom the Software is |
heroic | 10:5b3be78ce6bd | 21 | // furnished to do so, subject to the following conditions: |
heroic | 10:5b3be78ce6bd | 22 | // |
heroic | 10:5b3be78ce6bd | 23 | // The above copyright notice and this permission notice shall be included in |
heroic | 10:5b3be78ce6bd | 24 | // all copies or substantial portions of the Software. |
heroic | 10:5b3be78ce6bd | 25 | // |
heroic | 10:5b3be78ce6bd | 26 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
heroic | 10:5b3be78ce6bd | 27 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
heroic | 10:5b3be78ce6bd | 28 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
heroic | 10:5b3be78ce6bd | 29 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
heroic | 10:5b3be78ce6bd | 30 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
heroic | 10:5b3be78ce6bd | 31 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
heroic | 10:5b3be78ce6bd | 32 | // THE SOFTWARE. |
heroic | 10:5b3be78ce6bd | 33 | // |
heroic | 10:5b3be78ce6bd | 34 | // |
heroic | 10:5b3be78ce6bd | 35 | // Some code originally from: |
heroic | 10:5b3be78ce6bd | 36 | // Mbed library to control LPD8806-based RGB LED Strips |
heroic | 10:5b3be78ce6bd | 37 | // (c) 2011 Jelmer Tiete |
heroic | 10:5b3be78ce6bd | 38 | // That library is ported from the Arduino implementation of Adafruit Industries |
heroic | 10:5b3be78ce6bd | 39 | // found at: http://github.com/adafruit/LPD8806 |
heroic | 10:5b3be78ce6bd | 40 | // and their strips: http://www.adafruit.com/products/306 |
heroic | 10:5b3be78ce6bd | 41 | // Released under the MIT License: http://mbed.org/license/mit |
heroic | 10:5b3be78ce6bd | 42 | // |
heroic | 10:5b3be78ce6bd | 43 | // Parameterized and modified to use soft SPI. |
heroic | 10:5b3be78ce6bd | 44 | // Jas Strong <jasmine@electronpusher.org> |
heroic | 10:5b3be78ce6bd | 45 | // Then remonstered to use hardware SPI for blast mode. |
heroic | 10:5b3be78ce6bd | 46 | // |
heroic | 10:5b3be78ce6bd | 47 | /*****************************************************************************/ |
heroic | 10:5b3be78ce6bd | 48 | |
heroic | 10:5b3be78ce6bd | 49 | #include "LedStrip.h" |
heroic | 10:5b3be78ce6bd | 50 | #include "FastPixelLPD8806.h" |
heroic | 10:5b3be78ce6bd | 51 | |
heroic | 10:5b3be78ce6bd | 52 | #include "MODDMA.h" |
heroic | 10:5b3be78ce6bd | 53 | |
heroic | 10:5b3be78ce6bd | 54 | #define LATCHNUMBER 32 |
heroic | 10:5b3be78ce6bd | 55 | #define FAST_PIXEL_MHZ 18 |
heroic | 10:5b3be78ce6bd | 56 | |
heroic | 10:5b3be78ce6bd | 57 | // DMA controller and frequency setting |
heroic | 10:5b3be78ce6bd | 58 | extern MODDMA dma; |
heroic | 10:5b3be78ce6bd | 59 | extern int current_strip_fill; |
heroic | 10:5b3be78ce6bd | 60 | |
heroic | 10:5b3be78ce6bd | 61 | // when the transfer is complete, kill the channel. |
heroic | 10:5b3be78ce6bd | 62 | void dma_complete_callback() { |
heroic | 10:5b3be78ce6bd | 63 | MODDMA_Config *config = dma.getConfig(); |
heroic | 10:5b3be78ce6bd | 64 | dma.Disable( (MODDMA::CHANNELS)config->channelNum() ); |
heroic | 10:5b3be78ce6bd | 65 | |
heroic | 10:5b3be78ce6bd | 66 | // clear the IRQ flags |
heroic | 10:5b3be78ce6bd | 67 | if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); |
heroic | 10:5b3be78ce6bd | 68 | if (dma.irqType() == MODDMA::ErrIrq) dma.clearErrIrq(); |
heroic | 10:5b3be78ce6bd | 69 | } |
heroic | 10:5b3be78ce6bd | 70 | |
heroic | 10:5b3be78ce6bd | 71 | void dma_err_callback() { |
heroic | 10:5b3be78ce6bd | 72 | MODDMA_Config *config = dma.getConfig(); |
heroic | 10:5b3be78ce6bd | 73 | dma.Disable( (MODDMA::CHANNELS)config->channelNum() ); |
heroic | 10:5b3be78ce6bd | 74 | |
heroic | 10:5b3be78ce6bd | 75 | // clear the IRQ flags |
heroic | 10:5b3be78ce6bd | 76 | if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); |
heroic | 10:5b3be78ce6bd | 77 | if (dma.irqType() == MODDMA::ErrIrq) dma.clearErrIrq(); |
heroic | 10:5b3be78ce6bd | 78 | } |
heroic | 10:5b3be78ce6bd | 79 | |
heroic | 10:5b3be78ce6bd | 80 | FastPixelLPD8806::FastPixelLPD8806(PinName dataPin, PinName clockPin, int n) : |
heroic | 10:5b3be78ce6bd | 81 | _spi(dataPin, NC, clockPin) |
heroic | 10:5b3be78ce6bd | 82 | { |
heroic | 10:5b3be78ce6bd | 83 | // Allocate 3 bytes per pixel plus latch bytes |
heroic | 10:5b3be78ce6bd | 84 | numLEDs = n; |
heroic | 10:5b3be78ce6bd | 85 | pixels = (uint8_t *)malloc(numLEDs * 3 + ((numLEDs + LATCHNUMBER-1) / LATCHNUMBER)); |
heroic | 10:5b3be78ce6bd | 86 | if (pixels) { |
heroic | 10:5b3be78ce6bd | 87 | memset(pixels, 0x80, numLEDs * 3); // Init to RGB 'off' state |
heroic | 10:5b3be78ce6bd | 88 | memset(pixels+numLEDs*3, 0x0, (numLEDs + LATCHNUMBER-1) / LATCHNUMBER); // latch data |
heroic | 10:5b3be78ce6bd | 89 | } |
heroic | 10:5b3be78ce6bd | 90 | |
heroic | 10:5b3be78ce6bd | 91 | _spi.format(8,0); |
heroic | 10:5b3be78ce6bd | 92 | _spi.frequency(FAST_PIXEL_MHZ*1000000); |
heroic | 10:5b3be78ce6bd | 93 | |
heroic | 10:5b3be78ce6bd | 94 | // remember which strip we are |
heroic | 10:5b3be78ce6bd | 95 | strip_num = current_strip_fill; |
heroic | 10:5b3be78ce6bd | 96 | |
heroic | 10:5b3be78ce6bd | 97 | // make a new DMA config |
heroic | 10:5b3be78ce6bd | 98 | dma_config = new MODDMA_Config; |
heroic | 10:5b3be78ce6bd | 99 | } |
heroic | 10:5b3be78ce6bd | 100 | |
heroic | 10:5b3be78ce6bd | 101 | inline void FastPixelLPD8806::write(uint8_t byte) { |
heroic | 10:5b3be78ce6bd | 102 | _spi.write(byte); |
heroic | 10:5b3be78ce6bd | 103 | } |
heroic | 10:5b3be78ce6bd | 104 | |
heroic | 10:5b3be78ce6bd | 105 | |
heroic | 10:5b3be78ce6bd | 106 | void FastPixelLPD8806::begin(void) { |
heroic | 10:5b3be78ce6bd | 107 | |
heroic | 10:5b3be78ce6bd | 108 | blank(); |
heroic | 10:5b3be78ce6bd | 109 | show(); |
heroic | 10:5b3be78ce6bd | 110 | show(); |
heroic | 10:5b3be78ce6bd | 111 | } |
heroic | 10:5b3be78ce6bd | 112 | |
heroic | 10:5b3be78ce6bd | 113 | uint16_t FastPixelLPD8806::numPixels(void) { |
heroic | 10:5b3be78ce6bd | 114 | return numLEDs; |
heroic | 10:5b3be78ce6bd | 115 | } |
heroic | 10:5b3be78ce6bd | 116 | |
heroic | 10:5b3be78ce6bd | 117 | void FastPixelLPD8806::blank(void) { |
heroic | 10:5b3be78ce6bd | 118 | memset(pixels, 0x80, numLEDs * 3); |
heroic | 10:5b3be78ce6bd | 119 | } |
heroic | 10:5b3be78ce6bd | 120 | |
heroic | 10:5b3be78ce6bd | 121 | // Set up the DMA controller; we only have two SSP peripherals, so only two strips. |
heroic | 10:5b3be78ce6bd | 122 | |
heroic | 10:5b3be78ce6bd | 123 | void FastPixelLPD8806::show(void) { |
heroic | 10:5b3be78ce6bd | 124 | if (strip_num == 0) { |
heroic | 10:5b3be78ce6bd | 125 | dma_config -> channelNum (MODDMA::Channel_0) |
heroic | 10:5b3be78ce6bd | 126 | -> srcMemAddr ( (uint32_t) pixels ) |
heroic | 10:5b3be78ce6bd | 127 | -> dstMemAddr ( MODDMA::SSP0_Tx ) |
heroic | 10:5b3be78ce6bd | 128 | -> transferSize (numLEDs * 3 + ((numLEDs + LATCHNUMBER-1) / LATCHNUMBER)) |
heroic | 10:5b3be78ce6bd | 129 | -> transferType (MODDMA::m2p) |
heroic | 10:5b3be78ce6bd | 130 | -> dstConn (MODDMA::SSP0_Tx) |
heroic | 10:5b3be78ce6bd | 131 | -> attach_tc (&dma_complete_callback) |
heroic | 10:5b3be78ce6bd | 132 | -> attach_err (&dma_err_callback); |
heroic | 10:5b3be78ce6bd | 133 | LPC_SSP0->DMACR = (1<<1)|(1<<0); // TX,RXDMAE |
heroic | 10:5b3be78ce6bd | 134 | } else { |
heroic | 10:5b3be78ce6bd | 135 | dma_config -> channelNum (MODDMA::Channel_1) |
heroic | 10:5b3be78ce6bd | 136 | -> srcMemAddr ( (uint32_t) pixels ) |
heroic | 10:5b3be78ce6bd | 137 | -> dstMemAddr ( MODDMA::SSP1_Tx ) |
heroic | 10:5b3be78ce6bd | 138 | -> transferSize (numLEDs * 3 + ((numLEDs +LATCHNUMBER-1) / LATCHNUMBER)) |
heroic | 10:5b3be78ce6bd | 139 | -> transferType (MODDMA::m2p) |
heroic | 10:5b3be78ce6bd | 140 | -> dstConn (MODDMA::SSP1_Tx) |
heroic | 10:5b3be78ce6bd | 141 | -> attach_tc (&dma_complete_callback) |
heroic | 10:5b3be78ce6bd | 142 | -> attach_err (&dma_err_callback); |
heroic | 10:5b3be78ce6bd | 143 | LPC_SSP1->DMACR = (1<<1)|(1<<0); // TX,RXDMAE |
heroic | 10:5b3be78ce6bd | 144 | } |
heroic | 10:5b3be78ce6bd | 145 | while (dma.Enabled(dma_config->channelNum())) { |
heroic | 10:5b3be78ce6bd | 146 | __nop(); |
heroic | 10:5b3be78ce6bd | 147 | } |
heroic | 10:5b3be78ce6bd | 148 | dma.Setup(dma_config); |
heroic | 10:5b3be78ce6bd | 149 | dma.Enable(dma_config); |
heroic | 10:5b3be78ce6bd | 150 | } |
heroic | 10:5b3be78ce6bd | 151 | |
heroic | 10:5b3be78ce6bd | 152 | // LPD8806 is 7-bit, so we shift to normalize to 256 PWM steps. |
heroic | 10:5b3be78ce6bd | 153 | uint32_t FastPixelLPD8806::total_luminance(void) { |
heroic | 10:5b3be78ce6bd | 154 | uint32_t running_total; |
heroic | 10:5b3be78ce6bd | 155 | running_total = 0; |
heroic | 10:5b3be78ce6bd | 156 | for (int i=0; i<numLEDs*3; i++) |
heroic | 10:5b3be78ce6bd | 157 | running_total += ((pixels[i] & 0x7f) <<1); |
heroic | 10:5b3be78ce6bd | 158 | return running_total; |
heroic | 10:5b3be78ce6bd | 159 | } |
heroic | 10:5b3be78ce6bd | 160 | |
heroic | 10:5b3be78ce6bd | 161 | // Convert R,G,B to combined 32-bit color |
heroic | 10:5b3be78ce6bd | 162 | uint32_t FastPixelLPD8806::Color(uint8_t r, uint8_t g, uint8_t b) { |
heroic | 10:5b3be78ce6bd | 163 | // Take the lowest 7 bits of each value and append them end to end |
heroic | 10:5b3be78ce6bd | 164 | // We have the top bit set high (its a 'parity-like' bit in the protocol |
heroic | 10:5b3be78ce6bd | 165 | // and must be set!) |
heroic | 10:5b3be78ce6bd | 166 | return 0x808080 | ((uint32_t)(g>>1) << 16) | ((uint32_t)(r>>1) << 8) | (uint32_t)(b>>1); |
heroic | 10:5b3be78ce6bd | 167 | } |
heroic | 10:5b3be78ce6bd | 168 | |
heroic | 10:5b3be78ce6bd | 169 | // store the rgb component in our array |
heroic | 10:5b3be78ce6bd | 170 | void FastPixelLPD8806::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { |
heroic | 10:5b3be78ce6bd | 171 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
heroic | 10:5b3be78ce6bd | 172 | |
heroic | 10:5b3be78ce6bd | 173 | pixels[n*3 ] = (g>>1) | 0x80; |
heroic | 10:5b3be78ce6bd | 174 | pixels[n*3+1] = (r>>1) | 0x80; |
heroic | 10:5b3be78ce6bd | 175 | pixels[n*3+2] = (b>>1) | 0x80; |
heroic | 10:5b3be78ce6bd | 176 | } |
heroic | 10:5b3be78ce6bd | 177 | |
heroic | 10:5b3be78ce6bd | 178 | void FastPixelLPD8806::setPixelR(uint16_t n, uint8_t r) { |
heroic | 10:5b3be78ce6bd | 179 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
heroic | 10:5b3be78ce6bd | 180 | |
heroic | 10:5b3be78ce6bd | 181 | pixels[n*3+1] = (r>>1) | 0x80; |
heroic | 10:5b3be78ce6bd | 182 | } |
heroic | 10:5b3be78ce6bd | 183 | |
heroic | 10:5b3be78ce6bd | 184 | void FastPixelLPD8806::setPixelG(uint16_t n, uint8_t g) { |
heroic | 10:5b3be78ce6bd | 185 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
heroic | 10:5b3be78ce6bd | 186 | |
heroic | 10:5b3be78ce6bd | 187 | pixels[n*3] = (g>>1) | 0x80; |
heroic | 10:5b3be78ce6bd | 188 | } |
heroic | 10:5b3be78ce6bd | 189 | |
heroic | 10:5b3be78ce6bd | 190 | void FastPixelLPD8806::setPixelB(uint16_t n, uint8_t b) { |
heroic | 10:5b3be78ce6bd | 191 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
heroic | 10:5b3be78ce6bd | 192 | |
heroic | 10:5b3be78ce6bd | 193 | pixels[n*3+2] = (b>>1) | 0x80; |
heroic | 10:5b3be78ce6bd | 194 | } |
heroic | 10:5b3be78ce6bd | 195 | |
heroic | 10:5b3be78ce6bd | 196 | // Set all in one function call, assuming GRB ordering |
heroic | 10:5b3be78ce6bd | 197 | void FastPixelLPD8806::setPackedPixels(uint8_t * buffer, uint32_t n) { |
heroic | 10:5b3be78ce6bd | 198 | if (n >= numLEDs) return; |
heroic | 10:5b3be78ce6bd | 199 | for (int i=0; i<n*3; i++) |
heroic | 10:5b3be78ce6bd | 200 | buffer[i] = (buffer[i] >> 1) | 0x80; |
heroic | 10:5b3be78ce6bd | 201 | memcpy(pixels, buffer, (size_t) (n*3)); |
heroic | 10:5b3be78ce6bd | 202 | } |
heroic | 10:5b3be78ce6bd | 203 | |
heroic | 10:5b3be78ce6bd | 204 | void FastPixelLPD8806::setPixelColor(uint16_t n, uint32_t c) { |
heroic | 10:5b3be78ce6bd | 205 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
heroic | 10:5b3be78ce6bd | 206 | |
heroic | 10:5b3be78ce6bd | 207 | pixels[n*3 ] = (c >> 16) | 0x80; |
heroic | 10:5b3be78ce6bd | 208 | pixels[n*3+1] = (c >> 8) | 0x80; |
heroic | 10:5b3be78ce6bd | 209 | pixels[n*3+2] = c | 0x80; |
heroic | 10:5b3be78ce6bd | 210 | } |