The port of adafruit dotstar library for arduino https://github.com/adafruit/Adafruit_DotStar
Revision 1:d09c288b8eb3, committed 2016-04-06
- Comitter:
- hideakitai
- Date:
- Wed Apr 06 08:49:21 2016 +0000
- Parent:
- 0:bae97ff743a6
- Commit message:
- support SPI, PortOut
Changed in this revision
DotStar.cpp | Show annotated file Show diff for this revision Revisions of this file |
DotStar.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r bae97ff743a6 -r d09c288b8eb3 DotStar.cpp --- a/DotStar.cpp Fri Mar 25 07:41:51 2016 +0000 +++ b/DotStar.cpp Wed Apr 06 08:49:21 2016 +0000 @@ -24,35 +24,106 @@ License along with DotStar. If not, see <http://www.gnu.org/licenses/>. ------------------------------------------------------------------------*/ +#include "mbed.h" #include "DotStar.h" -// #include <SPI.h> - -SPI spi(p5, p6, p7); // mosi, miso, sclk // Constructor for hardware SPI -- must connect to MOSI, SCK pins -Adafruit_DotStar::Adafruit_DotStar(uint16_t n, uint8_t o) : - numLEDs(n), dataPin(p5), brightness(0), pixels(NULL), - rOffset(o & 3), gOffset((o >> 2) & 3), bOffset((o >> 4) & 3) +Adafruit_DotStar::Adafruit_DotStar(uint16_t n, PinName miso, PinName mosi, PinName sclk, int hz, uint8_t o) +: numLEDs(n) +, dataPin(USE_HW_SPI) +, clockPin(USE_HW_SPI) +, spi(NULL) +, miso_(miso) +, mosi_(mosi) +, sclk_(sclk) +, data_out(NULL) +, sclk_out(NULL) +, port(PortA) +, port_out(NULL) +, d_mask(0) +, c_mask(0) +, b_use_port(false) +, brightness(0) +, pixels(NULL) +, rOffset(o & 3) +, gOffset((o >> 2) & 3) +, bOffset((o >> 4) & 3) { - updateLength(n); + updateLength(n); } // Constructor for 'soft' (bitbang) SPI -- any two pins can be used -Adafruit_DotStar::Adafruit_DotStar(uint16_t n, uint8_t data, uint8_t clock, - uint8_t o) : - dataPin(data), clockPin(clock), brightness(0), pixels(NULL), - rOffset(o & 3), gOffset((o >> 2) & 3), bOffset((o >> 4) & 3) +Adafruit_DotStar::Adafruit_DotStar(uint16_t n, PinName data, PinName clock, uint8_t o) +: dataPin(data) +, clockPin(clock) +, spi(NULL) +, miso_(NC) +, mosi_(NC) +, sclk_(NC) +, data_out(NULL) +, sclk_out(NULL) +, port(PortA) +, port_out(NULL) +, d_mask(0) +, c_mask(0) +, b_use_port(false) +, brightness(0) +, pixels(NULL) +, rOffset(o & 3) +, gOffset((o >> 2) & 3) +, bOffset((o >> 4) & 3) { - updateLength(n); + updateLength(n); +} + +// Constructor for 'soft' (bitbang) SPI -- any two pins can be used +Adafruit_DotStar::Adafruit_DotStar(uint16_t n, PortName p, int dmask, int cmask, uint8_t o) +: dataPin(NC) +, clockPin(NC) +, spi(NULL) +, miso_(NC) +, mosi_(NC) +, sclk_(NC) +, data_out(NULL) +, sclk_out(NULL) +, port(p) +, port_out(NULL) +, d_mask(dmask) +, c_mask(cmask) +, b_use_port(true) +, brightness(0) +, pixels(NULL) +, rOffset(o & 3) +, gOffset((o >> 2) & 3) +, bOffset((o >> 4) & 3) +{ + updateLength(n); } Adafruit_DotStar::~Adafruit_DotStar(void) { // Destructor - if(pixels) free(pixels); - hw_spi_end(); + if(pixels) free(pixels); + if(dataPin == USE_HW_SPI) hw_spi_end(); + else sw_spi_end(); } void Adafruit_DotStar::begin(void) { // Initialize SPI - hw_spi_init(); + if(dataPin == USE_HW_SPI) hw_spi_init(); + else sw_spi_init(); +} + +// Change to hardware SPI -- must connect to MOSI, SCK pins +void Adafruit_DotStar::updatePins(void) { + sw_spi_end(); + dataPin = USE_HW_SPI; + hw_spi_init(); +} + +// Change to 'soft' (bitbang) SPI -- any two pins can be used +void Adafruit_DotStar::updatePins(PinName data, PinName clock) { + hw_spi_end(); + dataPin = data; + clockPin = clock; + sw_spi_init(); } // Length can be changed post-constructor for similar reasons (sketch @@ -60,41 +131,61 @@ // all that reallocation is likely to fragment and eventually fail. // Instead, set length once to longest strip. void Adafruit_DotStar::updateLength(uint16_t n) { - if(pixels) free(pixels); - uint16_t bytes = (rOffset == gOffset) ? - n + ((n + 3) / 4) : // MONO: 10 bits/pixel, round up to next byte - n * 3; // COLOR: 3 bytes/pixel - if((pixels = (uint8_t *)malloc(bytes))) { - numLEDs = n; - clear(); - } else { - numLEDs = 0; - } + if(pixels) free(pixels); + uint16_t bytes = (rOffset == gOffset) ? + n + ((n + 3) / 4) : // MONO: 10 bits/pixel, round up to next byte + n * 3; // COLOR: 3 bytes/pixel + if((pixels = (uint8_t *)malloc(bytes))) { + numLEDs = n; + clear(); + } else { + numLEDs = 0; + } } // SPI STUFF --------------------------------------------------------------- void Adafruit_DotStar::hw_spi_init(void) { // Initialize hardware SPI - // SPI.begin(); - // SPI.setClockDivider((F_CPU + 4000000L) / 8000000L); // 8-ish MHz on Due - // SPI.setBitOrder(MSBFIRST); - // SPI.setDataMode(SPI_MODE0); // Clock Polarity: 0, Clock Phase: 0 - - spi.format(8, 0); // 8bit, mode:0 - spi.frequency(8000000); // Hz - + spi = new SPI(mosi_, miso_, sclk_); // mosi, miso, sclk + spi->format(8, 0); // 8bit, mode:0 + spi->frequency(8000000); // Hz } void Adafruit_DotStar::hw_spi_end(void) { // Stop hardware SPI - // SPI.end(); + if (spi) delete spi; +} + +void Adafruit_DotStar::sw_spi_init(void) { // Init 'soft' (bitbang) SPI + if (b_use_port) { + port_out = new PortOut(port, (d_mask | c_mask)); + } else { + data_out = new DigitalOut(dataPin); + sclk_out = new DigitalOut(clockPin); + } } - -// All other boards have full-featured hardware support for SPI +void Adafruit_DotStar::sw_spi_end() { // Stop 'soft' SPI + if (port_out) delete(port_out); + if (data_out) delete(data_out); + if (sclk_out) delete(sclk_out); +} -//#define spi_out(n) (void)spi.write(n) -// Pipelining reads next byte while current byte is clocked out - +//inline void Adafruit_DotStar::sw_spi_out(uint8_t n) { // Bitbang SPI write +// if (b_use_port) { +// for(uint8_t i=8; i--; n <<= 1) { +// int mask = (n & 0x80) ? (d_mask | c_mask) : c_mask; +// *port_out = mask; +// *port_out = (mask & ~c_mask); +// } +// } else { +// for(uint8_t i=8; i--; n <<= 1) { +// if(n & 0x80) *data_out = 1; +// else *data_out = 0; +// *sclk_out = 1; +// *sclk_out = 0; +// } +// } +//} /* ISSUE DATA TO LED STRIP ------------------------------------------------- @@ -109,76 +200,95 @@ own use, but any pull requests for this will NOT be merged, nuh uh! */ -void Adafruit_DotStar::show(void) { - if(!pixels) return; - - uint8_t *ptr = pixels; // -> LED data - uint16_t n = numLEDs; // Counter - uint16_t b16 = (uint16_t)brightness; // Type-convert for fixed-point math +//inline void Adafruit_DotStar::show() { +// if(!pixels) return; +// +// uint8_t *ptr = pixels; // -> LED data +// uint16_t n = numLEDs; // Counter +// uint16_t b16 = (uint16_t)brightness; // Type-convert for fixed-point math +// +// if(dataPin == USE_HW_SPI) { +// +// for(size_t i=0; i<4; i++) spi->write(0x00); // 4 byte start-frame marker +// if(brightness) { // Scale pixel brightness on output +// for (size_t i=n; i>0; i--) { +// spi->write(0xFF); // Pixel start +// for(size_t j=0; j<3; j++) spi->write((*ptr++ * b16) >> 8); // Scale, write RGB +// } +// } else { // Full brightness (no scaling) +// for (size_t i=n; i>0; i--) { +// spi->write(0xFF); // Pixel start +// for(size_t j=0; j<3; j++) spi->write(*ptr++); // Write R,G,B +// } +// } +// // Four end-frame bytes are seemingly indistinguishable from a white +// // pixel, and empirical testing suggests it can be left out...but it's +// // always a good idea to follow the datasheet, in case future hardware +// // revisions are more strict (e.g. might mandate use of end-frame +// // before start-frame marker). i.e. let's not remove this. +// for(size_t i=0; i<4; i++) spi->write(0xFF); +// +// } else { // Soft (bitbang) SPI +// +// for(size_t i=0; i<4; i++) sw_spi_out(0); // Start-frame marker +// if(brightness) { // Scale pixel brightness on output +// do { // For each pixel... +// sw_spi_out(0xFF); // Pixel start +// for(size_t i=0; i<3; i++) sw_spi_out((*ptr++ * b16) >> 8); // Scale, write +// } while(--n); +// } else { // Full brightness (no scaling) +// do { // For each pixel... +// sw_spi_out(0xFF); // Pixel start +// for(size_t i=0; i<3; i++) sw_spi_out(*ptr++); // R,G,B +// } while(--n); +// } +// for(size_t i=0; i<4; i++) sw_spi_out(0xFF); // End-frame marker (see note above) +// } +//} - for(size_t i=0; i<4; i++) spi.write(0x00); // 4 byte start-frame marker - if(brightness) { // Scale pixel brightness on output - for (size_t i=n; i>0; i--) { - spi.write(0xFF); // Pixel start - for(size_t j=0; j<3; j++) spi.write((*ptr++ * b16) >> 8); // Scale, write RGB - } - } else { // Full brightness (no scaling) - for (size_t i=n; i>0; i--) { - spi.write(0xFF); // Pixel start - for(size_t j=0; j<3; j++) spi.write(*ptr++); // Write R,G,B - } - } - // Four end-frame bytes are seemingly indistinguishable from a white - // pixel, and empirical testing suggests it can be left out...but it's - // always a good idea to follow the datasheet, in case future hardware - // revisions are more strict (e.g. might mandate use of end-frame - // before start-frame marker). i.e. let's not remove this. - for(size_t i=0; i<4; i++) spi.write(0xFF); -} - -void Adafruit_DotStar::clear() { // Write 0s (off) to full pixel buffer - memset(pixels, 0, (rOffset == gOffset) ? - numLEDs + ((numLEDs + 3) / 4) : // MONO: 10 bits/pixel - numLEDs * 3); // COLOR: 3 bytes/pixel +inline void Adafruit_DotStar::clear() { // Write 0s (off) to full pixel buffer + memset(pixels, 0, (rOffset == gOffset) ? + numLEDs + ((numLEDs + 3) / 4) : // MONO: 10 bits/pixel + numLEDs * 3); // COLOR: 3 bytes/pixel } // Set pixel color, separate R,G,B values (0-255 ea.) -void Adafruit_DotStar::setPixelColor( - uint16_t n, uint8_t r, uint8_t g, uint8_t b) { - if(n < numLEDs) { - uint8_t *p = &pixels[n * 3]; - p[rOffset] = r; - p[gOffset] = g; - p[bOffset] = b; - } -} +//inline void Adafruit_DotStar::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) +//{ +// if(n < numLEDs) { +// uint8_t *p = &pixels[n * 3]; +// p[rOffset] = r; +// p[gOffset] = g; +// p[bOffset] = b; +// } +//} // Set pixel color, 'packed' RGB value (0x000000 - 0xFFFFFF) -void Adafruit_DotStar::setPixelColor(uint16_t n, uint32_t c) { - if(n < numLEDs) { - uint8_t *p = &pixels[n * 3]; - p[rOffset] = (uint8_t)(c >> 16); - p[gOffset] = (uint8_t)(c >> 8); - p[bOffset] = (uint8_t)c; - } +inline void Adafruit_DotStar::setPixelColor(uint16_t n, uint32_t c) { + if(n < numLEDs) { + uint8_t *p = &pixels[n * 3]; + p[rOffset] = (uint8_t)(c >> 16); + p[gOffset] = (uint8_t)(c >> 8); + p[bOffset] = (uint8_t)c; + } } // Convert separate R,G,B to packed value -uint32_t Adafruit_DotStar::Color(uint8_t r, uint8_t g, uint8_t b) { - return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; +inline uint32_t Adafruit_DotStar::Color(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; } // Read color from previously-set pixel, returns packed RGB value. -uint32_t Adafruit_DotStar::getPixelColor(uint16_t n) const { - if(n >= numLEDs) return 0; - uint8_t *p = &pixels[n * 3]; - return ((uint32_t)p[rOffset] << 16) | - ((uint32_t)p[gOffset] << 8) | - (uint32_t)p[bOffset]; +inline uint32_t Adafruit_DotStar::getPixelColor(uint16_t n) const { + if(n >= numLEDs) return 0; + uint8_t *p = &pixels[n * 3]; + return ((uint32_t)p[rOffset] << 16) | + ((uint32_t)p[gOffset] << 8) | + (uint32_t)p[bOffset]; } -uint16_t Adafruit_DotStar::numPixels(void) { // Ret. strip length - return numLEDs; +inline uint16_t Adafruit_DotStar::numPixels(void) { // Ret. strip length + return numLEDs; } // Set global strip brightness. This does not have an immediate effect; @@ -188,23 +298,23 @@ // in this library is 'non destructive' -- it's applied as color data is // being issued to the strip, not during setPixel(), and also means that // getPixelColor() returns the exact value originally stored. -void Adafruit_DotStar::setBrightness(uint8_t b) { - // Stored brightness value is different than what's passed. This - // optimizes the actual scaling math later, allowing a fast 8x8-bit - // multiply and taking the MSB. 'brightness' is a uint8_t, adding 1 - // here may (intentionally) roll over...so 0 = max brightness (color - // values are interpreted literally; no scaling), 1 = min brightness - // (off), 255 = just below max brightness. - brightness = b + 1; +inline void Adafruit_DotStar::setBrightness(uint8_t b) { + // Stored brightness value is different than what's passed. This + // optimizes the actual scaling math later, allowing a fast 8x8-bit + // multiply and taking the MSB. 'brightness' is a uint8_t, adding 1 + // here may (intentionally) roll over...so 0 = max brightness (color + // values are interpreted literally; no scaling), 1 = min brightness + // (off), 255 = just below max brightness. + brightness = b + 1; } -uint8_t Adafruit_DotStar::getBrightness(void) const { - return brightness - 1; // Reverse above operation +inline uint8_t Adafruit_DotStar::getBrightness(void) const { + return brightness - 1; // Reverse above operation } // Return pointer to the library's pixel data buffer. Use carefully, // much opportunity for mayhem. It's mostly for code that needs fast // transfers, e.g. SD card to LEDs. Color data is in BGR order. -uint8_t *Adafruit_DotStar::getPixels(void) const { - return pixels; +inline uint8_t *Adafruit_DotStar::getPixels(void) const { + return pixels; }
diff -r bae97ff743a6 -r d09c288b8eb3 DotStar.h --- a/DotStar.h Fri Mar 25 07:41:51 2016 +0000 +++ b/DotStar.h Wed Apr 06 08:49:21 2016 +0000 @@ -30,51 +30,130 @@ #define DOTSTAR_BGR (2 | (1 << 2) | (0 << 4)) #define DOTSTAR_MONO 0 // Single-color strip WIP DO NOT USE YET +#define USE_HW_SPI NC // Assign this to dataPin to indicate 'hard' SPI + + class Adafruit_DotStar { - public: +public: - Adafruit_DotStar(uint16_t n, uint8_t o=DOTSTAR_BRG); - Adafruit_DotStar(uint16_t n, uint8_t d, uint8_t c, uint8_t o=DOTSTAR_BRG); - ~Adafruit_DotStar(void); - void - begin(void), // Prime pins/SPI for output - clear(), // Set all pixel data to zero - setBrightness(uint8_t), // Set global brightness 0-255 - setPixelColor(uint16_t n, uint32_t c), - setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b), - show(void), // Issue color data to strip - // updatePins(void), // Change pin assignments (HW) - // updatePins(uint8_t d, uint8_t c), // Change pin assignments (SW) - updateLength(uint16_t n); // Change length - uint32_t - Color(uint8_t r, uint8_t g, uint8_t b), // R,G,B to 32-bit color - getPixelColor(uint16_t n) const; // Return 32-bit pixel color - uint16_t - numPixels(void); // Return number of pixels - uint8_t - getBrightness(void) const, // Return global brightness - *getPixels(void) const; // Return pixel data pointer + Adafruit_DotStar(uint16_t n, PinName miso, PinName mosi, PinName sclk, int hz = 8000000, uint8_t o=DOTSTAR_BRG); + Adafruit_DotStar(uint16_t n, PinName d, PinName c, uint8_t o=DOTSTAR_BRG); + Adafruit_DotStar(uint16_t n, PortName p, int d_mask, int c_mask, uint8_t o=DOTSTAR_BRG); + ~Adafruit_DotStar(void); + + void begin(void); // Prime pins/SPI for output + void clear(); // Set all pixel data to zero + void setBrightness(uint8_t); // Set global brightness 0-255 + void setPixelColor(uint16_t n, uint32_t c); + void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) + { + if(n < numLEDs) { + uint8_t *p = &pixels[n * 3]; + p[rOffset] = r; + p[gOffset] = g; + p[bOffset] = b; + } + } + void show(void) // Issue color data to strip + { + if(!pixels) return; + + uint8_t *ptr = pixels; // -> LED data + uint16_t n = numLEDs; // Counter + uint16_t b16 = (uint16_t)brightness; // Type-convert for fixed-point math + + if(dataPin == USE_HW_SPI) { + + for(size_t i=0; i<4; i++) spi->write(0x00); // 4 byte start-frame marker + if(brightness) { // Scale pixel brightness on output + for (size_t i=n; i>0; i--) { + spi->write(0xFF); // Pixel start + for(size_t j=0; j<3; j++) spi->write((*ptr++ * b16) >> 8); // Scale, write RGB + } + } else { // Full brightness (no scaling) + for (size_t i=n; i>0; i--) { + spi->write(0xFF); // Pixel start + for(size_t j=0; j<3; j++) spi->write(*ptr++); // Write R,G,B + } + } + // Four end-frame bytes are seemingly indistinguishable from a white + // pixel, and empirical testing suggests it can be left out...but it's + // always a good idea to follow the datasheet, in case future hardware + // revisions are more strict (e.g. might mandate use of end-frame + // before start-frame marker). i.e. let's not remove this. + for(size_t i=0; i<4; i++) spi->write(0xFF); + + } else { // Soft (bitbang) SPI + + for(size_t i=0; i<4; i++) sw_spi_out(0); // Start-frame marker + if(brightness) { // Scale pixel brightness on output + do { // For each pixel... + sw_spi_out(0xFF); // Pixel start + for(size_t i=0; i<3; i++) sw_spi_out((*ptr++ * b16) >> 8); // Scale, write + } while(--n); + } else { // Full brightness (no scaling) + do { // For each pixel... + sw_spi_out(0xFF); // Pixel start + for(size_t i=0; i<3; i++) sw_spi_out(*ptr++); // R,G,B + } while(--n); + } + for(size_t i=0; i<4; i++) sw_spi_out(0xFF); // End-frame marker (see note above) + } + } + void updatePins(void); // Change pin assignments (HW) + void updatePins(PinName d, PinName c); // Change pin assignments (SW) + void updateLength(uint16_t n); // Change length + uint32_t Color(uint8_t r, uint8_t g, uint8_t b); // R,G,B to 32-bit color + uint32_t getPixelColor(uint16_t n) const; // Return 32-bit pixel color + uint16_t numPixels(void); // Return number of pixels + uint8_t getBrightness(void) const; // Return global brightness + uint8_t* getPixels(void) const; // Return pixel data pointer private: - uint16_t - numLEDs; // Number of pixels - uint8_t - dataPin, // If soft SPI, data pin # - clockPin, // If soft SPI, clock pin # - brightness, // Global brightness setting - *pixels, // LED RGB values (3 bytes ea.) - rOffset, // Index of red in 3-byte pixel - gOffset, // Index of green byte - bOffset; // Index of blue byte - void - hw_spi_init(void), // Start hardware SPI - hw_spi_end(void); // Stop hardware SPI - // sw_spi_init(void), // Start bitbang SPI - // sw_spi_out(uint8_t n), // Bitbang SPI write - // sw_spi_end(void); // Stop bitbang SPI - + uint16_t numLEDs; // Number of pixels + PinName dataPin; // If soft SPI, data pin # + PinName clockPin; // If soft SPI, clock pin # + SPI* spi; + PinName miso_; + PinName mosi_; + PinName sclk_; + DigitalOut* data_out; + DigitalOut* sclk_out; + PortName port; + PortOut* port_out; + int d_mask; + int c_mask; + bool b_use_port; + uint8_t brightness; // Global brightness setting + uint8_t* pixels; // LED RGB values (3 bytes ea.) + uint8_t rOffset; // Index of red in 3-byte pixel + uint8_t gOffset; // Index of green byte + uint8_t bOffset; // Index of blue byte + + void hw_spi_init(void); // Start hardware SPI + void hw_spi_end(void); // Stop hardware SPI + void sw_spi_init(void); // Start bitbang SPI + void sw_spi_end(void); // Stop bitbang SPI + inline void sw_spi_out(uint8_t n) // Bitbang SPI write + { + if (b_use_port) { + for(uint8_t i=8; i--; n <<= 1) { + int mask = (n & 0x80) ? (d_mask | c_mask) : c_mask; + *port_out = mask; + *port_out = (mask & ~c_mask); + } + } else { + for(uint8_t i=8; i--; n <<= 1) { + if(n & 0x80) *data_out = 1; + else *data_out = 0; + *sclk_out = 1; + *sclk_out = 0; + } + } + } + }; #endif // _ADAFRUIT_DOT_STAR_H_