DMA-enabled high data rate driver for Heroic Robotics LED strips.

Dependents:   FastPixelDemo

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?

UserRevisionLine numberNew 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 }