DMA-enabled high data rate driver for Heroic Robotics LED strips.
Revision 10:5b3be78ce6bd, committed 2014-05-31
- Comitter:
- heroic
- Date:
- Sat May 31 18:41:56 2014 +0000
- Parent:
- 9:9038105d14bc
- Commit message:
- Initial commit of FastPixelLPD8806 open source driver.
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FastPixelLPD8806.cpp Sat May 31 18:41:56 2014 +0000 @@ -0,0 +1,210 @@ +/***************************************************************************/ +// DMA-enabled high data rate driver for Heroic Robotics FastPixel strips. +// Written by Jas Strong <jasmine@heroicrobotics.com> +// Copyright (c) 2013-2014 Heroic Robotics, Inc. +// +// FastPixel is a family of high performance LED strips available from +// Heroic Robotics, Inc. http://heroicrobotics.com/ - check them out! +// +// These strips are capable of supporting 18 Mbit/sec when run on PixelPusher +// hardware; your performance on mbed is likely to be somewhat lower due to +// the lack of high performance driver chips on the SPI lines. +// +// Heroic Robotics also sells a family of dedicated LED controllers called +// PixelPusher- almost certainly the best LED controller in the world. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// +// Some code originally from: +// Mbed library to control LPD8806-based RGB LED Strips +// (c) 2011 Jelmer Tiete +// That library is ported from the Arduino implementation of Adafruit Industries +// found at: http://github.com/adafruit/LPD8806 +// and their strips: http://www.adafruit.com/products/306 +// Released under the MIT License: http://mbed.org/license/mit +// +// Parameterized and modified to use soft SPI. +// Jas Strong <jasmine@electronpusher.org> +// Then remonstered to use hardware SPI for blast mode. +// +/*****************************************************************************/ + +#include "LedStrip.h" +#include "FastPixelLPD8806.h" + +#include "MODDMA.h" + +#define LATCHNUMBER 32 +#define FAST_PIXEL_MHZ 18 + +// DMA controller and frequency setting +extern MODDMA dma; +extern int current_strip_fill; + + // when the transfer is complete, kill the channel. +void dma_complete_callback() { + MODDMA_Config *config = dma.getConfig(); + dma.Disable( (MODDMA::CHANNELS)config->channelNum() ); + + // clear the IRQ flags + if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); + if (dma.irqType() == MODDMA::ErrIrq) dma.clearErrIrq(); +} + +void dma_err_callback() { + MODDMA_Config *config = dma.getConfig(); + dma.Disable( (MODDMA::CHANNELS)config->channelNum() ); + + // clear the IRQ flags + if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); + if (dma.irqType() == MODDMA::ErrIrq) dma.clearErrIrq(); +} + +FastPixelLPD8806::FastPixelLPD8806(PinName dataPin, PinName clockPin, int n) : + _spi(dataPin, NC, clockPin) + { + // Allocate 3 bytes per pixel plus latch bytes + numLEDs = n; + pixels = (uint8_t *)malloc(numLEDs * 3 + ((numLEDs + LATCHNUMBER-1) / LATCHNUMBER)); + if (pixels) { + memset(pixels, 0x80, numLEDs * 3); // Init to RGB 'off' state + memset(pixels+numLEDs*3, 0x0, (numLEDs + LATCHNUMBER-1) / LATCHNUMBER); // latch data + } + + _spi.format(8,0); + _spi.frequency(FAST_PIXEL_MHZ*1000000); + + // remember which strip we are + strip_num = current_strip_fill; + + // make a new DMA config + dma_config = new MODDMA_Config; +} + +inline void FastPixelLPD8806::write(uint8_t byte) { + _spi.write(byte); +} + + +void FastPixelLPD8806::begin(void) { + + blank(); + show(); + show(); +} + +uint16_t FastPixelLPD8806::numPixels(void) { + return numLEDs; +} + +void FastPixelLPD8806::blank(void) { + memset(pixels, 0x80, numLEDs * 3); +} + +// Set up the DMA controller; we only have two SSP peripherals, so only two strips. + +void FastPixelLPD8806::show(void) { + if (strip_num == 0) { + dma_config -> channelNum (MODDMA::Channel_0) + -> srcMemAddr ( (uint32_t) pixels ) + -> dstMemAddr ( MODDMA::SSP0_Tx ) + -> transferSize (numLEDs * 3 + ((numLEDs + LATCHNUMBER-1) / LATCHNUMBER)) + -> transferType (MODDMA::m2p) + -> dstConn (MODDMA::SSP0_Tx) + -> attach_tc (&dma_complete_callback) + -> attach_err (&dma_err_callback); + LPC_SSP0->DMACR = (1<<1)|(1<<0); // TX,RXDMAE + } else { + dma_config -> channelNum (MODDMA::Channel_1) + -> srcMemAddr ( (uint32_t) pixels ) + -> dstMemAddr ( MODDMA::SSP1_Tx ) + -> transferSize (numLEDs * 3 + ((numLEDs +LATCHNUMBER-1) / LATCHNUMBER)) + -> transferType (MODDMA::m2p) + -> dstConn (MODDMA::SSP1_Tx) + -> attach_tc (&dma_complete_callback) + -> attach_err (&dma_err_callback); + LPC_SSP1->DMACR = (1<<1)|(1<<0); // TX,RXDMAE + } + while (dma.Enabled(dma_config->channelNum())) { + __nop(); + } + dma.Setup(dma_config); + dma.Enable(dma_config); +} + +// LPD8806 is 7-bit, so we shift to normalize to 256 PWM steps. +uint32_t FastPixelLPD8806::total_luminance(void) { + uint32_t running_total; + running_total = 0; + for (int i=0; i<numLEDs*3; i++) + running_total += ((pixels[i] & 0x7f) <<1); + return running_total; +} + +// Convert R,G,B to combined 32-bit color +uint32_t FastPixelLPD8806::Color(uint8_t r, uint8_t g, uint8_t b) { + // Take the lowest 7 bits of each value and append them end to end + // We have the top bit set high (its a 'parity-like' bit in the protocol + // and must be set!) + return 0x808080 | ((uint32_t)(g>>1) << 16) | ((uint32_t)(r>>1) << 8) | (uint32_t)(b>>1); +} + +// store the rgb component in our array +void FastPixelLPD8806::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { + if (n >= numLEDs) return; // '>=' because arrays are 0-indexed + + pixels[n*3 ] = (g>>1) | 0x80; + pixels[n*3+1] = (r>>1) | 0x80; + pixels[n*3+2] = (b>>1) | 0x80; +} + +void FastPixelLPD8806::setPixelR(uint16_t n, uint8_t r) { + if (n >= numLEDs) return; // '>=' because arrays are 0-indexed + + pixels[n*3+1] = (r>>1) | 0x80; +} + +void FastPixelLPD8806::setPixelG(uint16_t n, uint8_t g) { + if (n >= numLEDs) return; // '>=' because arrays are 0-indexed + + pixels[n*3] = (g>>1) | 0x80; +} + +void FastPixelLPD8806::setPixelB(uint16_t n, uint8_t b) { + if (n >= numLEDs) return; // '>=' because arrays are 0-indexed + + pixels[n*3+2] = (b>>1) | 0x80; +} + +// Set all in one function call, assuming GRB ordering +void FastPixelLPD8806::setPackedPixels(uint8_t * buffer, uint32_t n) { + if (n >= numLEDs) return; + for (int i=0; i<n*3; i++) + buffer[i] = (buffer[i] >> 1) | 0x80; + memcpy(pixels, buffer, (size_t) (n*3)); +} + +void FastPixelLPD8806::setPixelColor(uint16_t n, uint32_t c) { + if (n >= numLEDs) return; // '>=' because arrays are 0-indexed + + pixels[n*3 ] = (c >> 16) | 0x80; + pixels[n*3+1] = (c >> 8) | 0x80; + pixels[n*3+2] = c | 0x80; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FastPixelLPD8806.h Sat May 31 18:41:56 2014 +0000 @@ -0,0 +1,89 @@ +/***************************************************************************/ +// DMA-enabled high data rate driver for Heroic Robotics FastPixel strips. +// Written by Jas Strong <jasmine@heroicrobotics.com> +// Copyright (c) 2013-2014 Heroic Robotics, Inc. +// +// FastPixel is a family of high performance LED strips available from +// Heroic Robotics, Inc. http://heroicrobotics.com/ - check them out! +// +// These strips are capable of supporting 18 Mbit/sec when run on PixelPusher +// hardware; your performance on mbed is likely to be somewhat lower due to +// the lack of high performance driver chips on the SPI lines. +// +// Heroic Robotics also sells a family of dedicated LED controllers called +// PixelPusher- almost certainly the best LED controller in the world. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// Some portions originally from: +// +// Mbed library to control LPD8806-based RGB LED Strips +// (c) 2011 Jelmer Tiete +// This library is ported from the Arduino implementation of Adafruit Industries +// found at: http://github.com/adafruit/LPD8806 +// and their strips: http://www.adafruit.com/products/306 +// Released under the MIT License: http://mbed.org/license/mit +// +// Parameterized and modified to use soft SPI. +// Jas Strong <jasmine@electronpusher.org> +// Then remonstered to use hardware SPI for blast mode. +/*****************************************************************************/ + +// Heavily modified by Jas Strong, 2012-10-04 +// Changed to use a virtual base class and to use software SPI. + +#include "mbed.h" +#include "LedStrip.h" +#include "MODDMA.h" + +#ifndef MBED_FastPixelLPD8806_H +#define MBED_FastPixelLPD8806_H + +class FastPixelLPD8806 : public LedStrip { + + public: + + FastPixelLPD8806(PinName dataPin, PinName clockPin, int n); + virtual void begin(void); + virtual void show(void); + virtual void blank(void); + virtual void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); + virtual void setPackedPixels(uint8_t * buffer, uint32_t n); + virtual void setPixelB(uint16_t n, uint8_t b); + virtual void setPixelG(uint16_t n, uint8_t g); + virtual void setPixelR(uint16_t n, uint8_t r); + virtual void setPixelColor(uint16_t n, uint32_t c); + virtual uint16_t numPixels(void); + virtual uint32_t Color(uint8_t, uint8_t, uint8_t); + virtual uint32_t total_luminance(void); + + private: + SPI _spi; + void write(uint8_t byte); + uint8_t *pixels; // Holds LED color values + uint16_t numLEDs; // Number of RGB LEDs in strand + MODDMA_Config * dma_config; + int strip_num; + + + uint32_t clock_mask; + uint32_t data_mask; + +}; +#endif \ No newline at end of file
--- a/LPD8806_fast.cpp Sun Mar 09 22:34:04 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,216 +0,0 @@ -// Mbed library to control LPD8806-based RGB LED Strips -// (c) 2011 Jelmer Tiete -// This library is ported from the Arduino implementation of Adafruit Industries -// found at: http://github.com/adafruit/LPD8806 -// and their strips: http://www.adafruit.com/products/306 -// Released under the MIT License: http://mbed.org/license/mit -// -// Parameterized and modified to use soft SPI. -// Jas Strong <jasmine@electronpusher.org> -// Then remonstered to use hardware SPI for blast mode. -// -/*****************************************************************************/ - -#include "LedStrip.h" -#include "LPD8806_fast.h" -#include "board_variant.h" - -#include "MODDMA.h" - -#define LATCHNUMBER 32 - -// DMA controller and frequency setting -extern MODDMA dma; -extern int turbo_mode; -extern int current_strip_fill; - -/* - PixelPusher 3 pin names: - - STRIP1C = P2_9, - STRIP1D = P4_29, - STRIP2C = P0_18, - STRIP2D = P2_1, - STRIP3C = P2_6, - STRIP3D = P0_8, - STRIP4C = P2_8, - STRIP4D = P0_7, - STRIP5C = P0_15, - STRIP5D = P2_0, - STRIP6C = P0_17, - STRIP6D = P2_4, - STRIP7C = P2_5, - STRIP7D = P0_9, - STRIP8C = P2_7, - STRIP8D = P0_6, - -*/ - -/* - * STRIP7D is P0[9] which is SSP1 MOSI - * STRIP4D is P0[7] which is SSP1 SCK - * - * STRIP2C is P0[18] which is SSP0 MOSI - * STRIP5C is P0[15] which is SSP0 SCK - */ - - // when the transfer is complete, kill the channel. -void dma_complete_callback() { - MODDMA_Config *config = dma.getConfig(); - dma.Disable( (MODDMA::CHANNELS)config->channelNum() ); - - // clear the IRQ flags - if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); - if (dma.irqType() == MODDMA::ErrIrq) dma.clearErrIrq(); -} - -void dma_err_callback() { - MODDMA_Config *config = dma.getConfig(); - dma.Disable( (MODDMA::CHANNELS)config->channelNum() ); - - // clear the IRQ flags - if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); - if (dma.irqType() == MODDMA::ErrIrq) dma.clearErrIrq(); -} - -LPD8806_fast::LPD8806_fast(PinName dataPin, PinName clockPin, int n) : - _spi(dataPin, NC, clockPin) - { - // Allocate 3 bytes per pixel plus latch bytes - numLEDs = n; - pixels = (uint8_t *)malloc(numLEDs * 3 + ((numLEDs + LATCHNUMBER-1) / LATCHNUMBER)); - if (pixels) { - memset(pixels, 0x80, numLEDs * 3); // Init to RGB 'off' state - memset(pixels+numLEDs*3, 0x0, (numLEDs + LATCHNUMBER-1) / LATCHNUMBER); // latch data - } - - _spi.format(8,0); - _spi.frequency(turbo_mode*1000000); - - // remember which strip we are - strip_num = current_strip_fill; - - // make a new DMA config - dma_config = new MODDMA_Config; -} - -inline void LPD8806_fast::write(uint8_t byte) { - _spi.write(byte); -} - -/* -inline void LPD8806_fast::write(uint8_t byte) { - for (int i=0; i<8; i++) { - clk = 0; - dat = (byte & 0x80); - clk = 1; - byte <<= 1; - } - clk = 0; -}*/ - -void LPD8806_fast::begin(void) { - - blank(); - show(); - show(); -} - -uint16_t LPD8806_fast::numPixels(void) { - return numLEDs; -} - -void LPD8806_fast::blank(void) { - memset(pixels, 0x80, numLEDs * 3); -} - -// Set up the DMA controller; we only have two SSP peripherals, so only two strips. - -void LPD8806_fast::show(void) { - if (strip_num == 0) { - dma_config -> channelNum (MODDMA::Channel_0) - -> srcMemAddr ( (uint32_t) pixels ) - -> dstMemAddr ( MODDMA::SSP0_Tx ) - -> transferSize (numLEDs * 3 + ((numLEDs + LATCHNUMBER-1) / LATCHNUMBER)) - -> transferType (MODDMA::m2p) - -> dstConn (MODDMA::SSP0_Tx) - -> attach_tc (&dma_complete_callback) - -> attach_err (&dma_err_callback); - LPC_SSP0->DMACR = (1<<1)|(1<<0); // TX,RXDMAE - } else { - dma_config -> channelNum (MODDMA::Channel_1) - -> srcMemAddr ( (uint32_t) pixels ) - -> dstMemAddr ( MODDMA::SSP1_Tx ) - -> transferSize (numLEDs * 3 + ((numLEDs +LATCHNUMBER-1) / LATCHNUMBER)) - -> transferType (MODDMA::m2p) - -> dstConn (MODDMA::SSP1_Tx) - -> attach_tc (&dma_complete_callback) - -> attach_err (&dma_err_callback); - LPC_SSP1->DMACR = (1<<1)|(1<<0); // TX,RXDMAE - } - while (dma.Enabled(dma_config->channelNum())) { - __nop(); - } - dma.Setup(dma_config); - dma.Enable(dma_config); -} - -// LPD8806 is 7-bit, so we shift to normalize to 256 PWM steps. -uint32_t LPD8806_fast::total_luminance(void) { - uint32_t running_total; - running_total = 0; - for (int i=0; i<numLEDs*3; i++) - running_total += ((pixels[i] & 0x7f) <<1); - return running_total; -} - -// Convert R,G,B to combined 32-bit color -uint32_t LPD8806_fast::Color(uint8_t r, uint8_t g, uint8_t b) { - // Take the lowest 7 bits of each value and append them end to end - // We have the top bit set high (its a 'parity-like' bit in the protocol - // and must be set!) - return 0x808080 | ((uint32_t)(g>>1) << 16) | ((uint32_t)(r>>1) << 8) | (uint32_t)(b>>1); -} - -// store the rgb component in our array -void LPD8806_fast::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - - pixels[n*3 ] = (g>>1) | 0x80; - pixels[n*3+1] = (r>>1) | 0x80; - pixels[n*3+2] = (b>>1) | 0x80; -} - -void LPD8806_fast::setPixelR(uint16_t n, uint8_t r) { - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - - pixels[n*3+1] = (r>>1) | 0x80; -} - -void LPD8806_fast::setPixelG(uint16_t n, uint8_t g) { - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - - pixels[n*3] = (g>>1) | 0x80; -} - -void LPD8806_fast::setPixelB(uint16_t n, uint8_t b) { - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - - pixels[n*3+2] = (b>>1) | 0x80; -} - -// Set all in one function call, assuming GRB ordering -void LPD8806_fast::setPackedPixels(uint8_t * buffer, uint32_t n) { - if (n >= numLEDs) return; - for (int i=0; i<n*3; i++) - buffer[i] = (buffer[i] >> 1) | 0x80; - memcpy(pixels, buffer, (size_t) (n*3)); -} - -void LPD8806_fast::setPixelColor(uint16_t n, uint32_t c) { - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - - pixels[n*3 ] = (c >> 16) | 0x80; - pixels[n*3+1] = (c >> 8) | 0x80; - pixels[n*3+2] = c | 0x80; -}
--- a/LPD8806_fast.h Sun Mar 09 22:34:04 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -// Mbed library to control LPD8806-based RGB LED Strips -// (c) 2011 Jelmer Tiete -// This library is ported from the Arduino implementation of Adafruit Industries -// found at: http://github.com/adafruit/LPD8806 -// and their strips: http://www.adafruit.com/products/306 -// Released under the MIT License: http://mbed.org/license/mit -// -// Parameterized and modified to use soft SPI. -// Jas Strong <jasmine@electronpusher.org> -// Then remonstered to use hardware SPI for blast mode. -/*****************************************************************************/ - -// Heavily modified by Jas Strong, 2012-10-04 -// Changed to use a virtual base class and to use software SPI. - -#include "mbed.h" -#include "LedStrip.h" -#include "MODDMA.h" - -#ifndef MBED_LPD8806_fast_H -#define MBED_LPD8806_fast_H - -class LPD8806_fast : public LedStrip { - - public: - - LPD8806_fast(PinName dataPin, PinName clockPin, int n); - virtual void begin(void); - virtual void show(void); - virtual void blank(void); - virtual void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); - virtual void setPackedPixels(uint8_t * buffer, uint32_t n); - virtual void setPixelB(uint16_t n, uint8_t b); - virtual void setPixelG(uint16_t n, uint8_t g); - virtual void setPixelR(uint16_t n, uint8_t r); - virtual void setPixelColor(uint16_t n, uint32_t c); - virtual uint16_t numPixels(void); - virtual uint32_t Color(uint8_t, uint8_t, uint8_t); - virtual uint32_t total_luminance(void); - - private: - SPI _spi; - void write(uint8_t byte); - uint8_t *pixels; // Holds LED color values - uint16_t numLEDs; // Number of RGB LEDs in strand - MODDMA_Config * dma_config; - int strip_num; - - - uint32_t clock_mask; - uint32_t data_mask; - -}; -#endif \ No newline at end of file