DMA-enabled high data rate driver for Heroic Robotics LED strips.
Diff: FastPixelLPD8806.cpp
- Revision:
- 10:5b3be78ce6bd
--- /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; +}