diff --git a/examples/DotStarTest/DotStarTest.ino b/examples/DotStarTest/DotStarTest.ino new file mode 100644 index 0000000..45201ab --- /dev/null +++ b/examples/DotStarTest/DotStarTest.ino @@ -0,0 +1,88 @@ +// DotStarTest +// This example will cycle between showing four pixels as Red, Green, Blue, White +// and then showing those pixels as Black. +// +// There is serial output of the current state so you can confirm and follow along +// + +#include + +const uint16_t PixelCount = 4; // this example assumes 4 pixels, making it smaller will cause a failure + +// make sure to set this to the correct pins +const uint8_t DotClockPin = 2; +const uint8_t DotDataPin = 3; + +#define colorSaturation 128 + +// for software bit bang +NeoPixelBus strip(PixelCount, DotClockPin, DotDataPin); + +// for hardware SPI (best performance but must use hardware pins) +//NeoPixelBus strip(PixelCount); + +// DotStars that support RGB color and a overall luminance/brightness value +// NeoPixelBus strip(PixelCount, DotClockPin, DotDataPin); +// DotStars that support RGBW color with a seperate white element +//NeoPixelBus strip(PixelCount, DotClockPin, DotDataPin); + +RgbColor red(colorSaturation, 0, 0); +RgbColor green(0, colorSaturation, 0); +RgbColor blue(0, 0, colorSaturation); +RgbColor white(colorSaturation); +RgbColor black(0); + +// for use with RGB DotStars when using the luminance/brightness global value +// note that its range is only 0 - 31 (31 is full bright) and +// also note that it is not useful for POV displays as it will cause more flicker +RgbwColor redL(colorSaturation, 0, 0, 31); // use white value to store luminance +RgbwColor greenL(0, colorSaturation, 0, 31); // use white value to store luminance +RgbwColor blueL(0, 0, colorSaturation, 31); // use white value to store luminance +RgbwColor whiteL(255, 255, 255, colorSaturation / 8); // luminance is only 0-31 + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + Serial.flush(); + + // this resets all the neopixels to an off state + strip.Begin(); + strip.ClearTo(black); + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + + +void loop() +{ + delay(5000); + + Serial.println("Colors R, G, B, W..."); + + // set the colors, + strip.SetPixelColor(0, red); + strip.SetPixelColor(1, green); + strip.SetPixelColor(2, blue); + strip.SetPixelColor(3, white); + strip.Show(); + + + delay(5000); + + Serial.println("Off ..."); + + // turn off the pixels + strip.SetPixelColor(0, black); + strip.SetPixelColor(1, black); + strip.SetPixelColor(2, black); + strip.SetPixelColor(3, black); + strip.Show(); + +} + diff --git a/src/NeoPixelBus.h b/src/NeoPixelBus.h index 13d00b3..bda023f 100644 --- a/src/NeoPixelBus.h +++ b/src/NeoPixelBus.h @@ -36,6 +36,7 @@ License along with NeoPixel. If not, see #include "internal/RgbwColor.h" #include "internal/NeoColorFeatures.h" +#include "internal/DotStarColorFeatures.h" #include "internal/Layouts.h" #include "internal/NeoTopology.h" @@ -52,19 +53,29 @@ License along with NeoPixel. If not, see #include "internal/NeoGamma.h" #if defined(ARDUINO_ARCH_ESP8266) + #include "internal/NeoEsp8266DmaMethod.h" #include "internal/NeoEsp8266UartMethod.h" #include "internal/NeoEsp8266BitBangMethod.h" +#include "internal/DotStarGenericMethod.h" + #elif defined(__arm__) // must be before ARDUINO_ARCH_AVR due to Teensy incorrectly having it set + #include "internal/NeoArmMethod.h" +#include "internal/DotStarGenericMethod.h" + #elif defined(ARDUINO_ARCH_AVR) + #include "internal/NeoAvrMethod.h" +#include "internal/DotStarAvrMethod.h" + #else #error "Platform Currently Not Supported, please add an Issue at Github/Makuna/NeoPixelBus" #endif - - +#if !defined(__AVR_ATtiny85__) +#include "internal/DotStarSpiMethod.h" +#endif // '_state' flags for internal state #define NEO_DIRTY 0x80 // a change was made to pixel data that requires a show @@ -81,9 +92,20 @@ public: { } + NeoPixelBus(uint16_t countPixels, uint8_t pinClock, uint8_t pinData) : + _countPixels(countPixels), + _method(pinClock, pinData, countPixels, T_COLOR_FEATURE::PixelSize) + { + } + + NeoPixelBus(uint16_t countPixels) : + _countPixels(countPixels), + _method(countPixels, T_COLOR_FEATURE::PixelSize) + { + } + ~NeoPixelBus() { - } operator NeoBufferContext() diff --git a/src/internal/DotStarAvrMethod.h b/src/internal/DotStarAvrMethod.h new file mode 100644 index 0000000..66bd00b --- /dev/null +++ b/src/internal/DotStarAvrMethod.h @@ -0,0 +1,153 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for DotStars on AVR (APA102). + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set +#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__) + +class DotStarAvrMethod +{ +public: + DotStarAvrMethod(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize) : + _pinClock(pinClock), + _pinData(pinData), + _sizePixels(pixelCount * elementSize) + { + pinMode(pinClock, OUTPUT); + pinMode(pinData, OUTPUT); + + _pixels = (uint8_t*)malloc(_sizePixels); + memset(_pixels, 0, _sizePixels); + + _portClock = portOutputRegister(digitalPinToPort(_pinClock)); + _pinMaskClock = digitalPinToBitMask(_pinClock); + _portData = portOutputRegister(digitalPinToPort(_pinData)); + _pinMaskData = digitalPinToBitMask(_pinData); + } + + ~DotStarAvrMethod() + { + pinMode(_pinClock, INPUT); + pinMode(_pinData, INPUT); + + free(_pixels); + } + + bool IsReadyToUpdate() const + { + return true; // dot stars don't have a required delay + } + + void Initialize() + { + digitalWrite(_pinClock, LOW); + digitalWrite(_pinData, LOW); + } + + void Update() + { + // start frame + for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++) + { + _transmitByte(0x00); + } + + // data + uint8_t* data = _pixels; + const uint8_t* endData = _pixels + _sizePixels; + while (data < endData) + { + _transmitByte(*data++); + } + + // end frame + // one bit for every two pixels with no less than 1 byte + const uint16_t countEndFrameBytes = ((_sizePixels / 4) + 15) / 16; + for (uint16_t endFrameByte = 0; endFrameByte < countEndFrameBytes; endFrameByte++) + { + _transmitByte(0xff); + } + + // set clock and data back to low between updates + digitalWrite(_pinData, LOW); + } + + uint8_t* getPixels() const + { + return _pixels; + }; + + size_t getPixelsSize() const + { + return _sizePixels; + }; + +private: + const uint8_t _pinClock; // output pin number for clock line + const uint8_t _pinData; // output pin number for data line + const size_t _sizePixels; // Size of '_pixels' buffer below + + uint8_t* _pixels; // Holds LED color values + + volatile uint8_t* _portData; // Output PORT register + uint8_t _pinMaskData; // Output PORT bitmask + volatile uint8_t* _portClock; // Output PORT register + uint8_t _pinMaskClock; // Output PORT bitmask + + void _transmitByte(uint8_t data) + { + for (int bit = 7; bit >= 0; bit--) + { + // set data bit on pin + // digitalWrite(_pinData, (data & 0x80) == 0x80 ? HIGH : LOW); + if (data & 0x80) + { + *_portData |= _pinMaskData; + } + else + { + *_portData &= ~_pinMaskData; + } + + + // set clock high as data is ready + // digitalWrite(_pinClock, HIGH); + *_portClock |= _pinMaskClock; + + // done between clock toggle to give a little time + data <<= 1; + + // set clock low as data pin is changed + // digitalWrite(_pinClock, LOW); + *_portClock &= ~_pinMaskClock; + } + } +}; + +typedef DotStarAvrMethod DotStarMethod; + +#endif diff --git a/src/internal/DotStarColorFeatures.h b/src/internal/DotStarColorFeatures.h new file mode 100644 index 0000000..3895844 --- /dev/null +++ b/src/internal/DotStarColorFeatures.h @@ -0,0 +1,216 @@ +/*------------------------------------------------------------------------- +DotStarColorFeatures provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class DotStar4Elements +{ +public: + static const size_t PixelSize = 4; + + static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + static const uint8_t* getPixelAddress(const uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + + static void replicatePixel(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + while (pPixelDest < pEnd) + { + *pPixelDest++ = pPixelSrc[0]; + *pPixelDest++ = pPixelSrc[1]; + *pPixelDest++ = pPixelSrc[2]; + *pPixelDest++ = pPixelSrc[3]; + } + } + + static void movePixelsInc(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + while (pPixelDest < pEnd) + { + *pPixelDest++ = *pPixelSrc++; + *pPixelDest++ = *pPixelSrc++; + *pPixelDest++ = *pPixelSrc++; + *pPixelDest++ = *pPixelSrc++; + } + } + + static void movePixelsInc_P(uint8_t* pPixelDest, PGM_VOID_P pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + const uint8_t* pSrc = (const uint8_t*)pPixelSrc; + while (pPixelDest < pEnd) + { + *pPixelDest++ = pgm_read_byte(pSrc++); + *pPixelDest++ = pgm_read_byte(pSrc++); + *pPixelDest++ = pgm_read_byte(pSrc++); + *pPixelDest++ = pgm_read_byte(pSrc++); + } + } + + static void movePixelsDec(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pDestBack = pPixelDest + (count * PixelSize); + const uint8_t* pSrcBack = pPixelSrc + (count * PixelSize); + while (pDestBack > pPixelDest) + { + *--pDestBack = *--pSrcBack; + *--pDestBack = *--pSrcBack; + *--pDestBack = *--pSrcBack; + *--pDestBack = *--pSrcBack; + } + } + + typedef RgbwColor ColorObject; +}; + +class DotStarBgrFeature : public DotStar4Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.B; + *p++ = color.G; + *p = color.R; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.B = *p++; + color.G = *p++; + color.R = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } + +}; + +class DotStarLbgrFeature : public DotStar4Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | min(color.W, 31); // upper three bits are always 111 + *p++ = color.B; + *p++ = color.G; + *p = color.R; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.B = *p++; + color.G = *p++; + color.R = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } + +}; + +class DotStarWbgrFeature : public DotStar4Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.W; + *p++ = color.B; + *p++ = color.G; + *p = color.R; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = *p++; + color.B = *p++; + color.G = *p++; + color.R = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } +}; diff --git a/src/internal/DotStarGenericMethod.h b/src/internal/DotStarGenericMethod.h new file mode 100644 index 0000000..d59d0e0 --- /dev/null +++ b/src/internal/DotStarGenericMethod.h @@ -0,0 +1,128 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for DotStars using general Pins (APA102). + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +class DotStarGenericMethod +{ +public: + DotStarGenericMethod(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize) : + _pinClock(pinClock), + _pinData(pinData), + _sizePixels(pixelCount * elementSize) + { + pinMode(pinClock, OUTPUT); + pinMode(pinData, OUTPUT); + + _pixels = (uint8_t*)malloc(_sizePixels); + memset(_pixels, 0, _sizePixels); + } + + ~DotStarGenericMethod() + { + pinMode(_pinClock, INPUT); + pinMode(_pinData, INPUT); + + free(_pixels); + } + + bool IsReadyToUpdate() const + { + return true; // dot stars don't have a required delay + } + + void Initialize() + { + digitalWrite(_pinClock, LOW); + digitalWrite(_pinData, LOW); + } + + void Update() + { + // start frame + for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++) + { + _transmitByte(0x00); + } + + // data + uint8_t* data = _pixels; + const uint8_t* endData = _pixels + _sizePixels; + while (data < endData) + { + _transmitByte(*data++); + } + + // end frame + // one bit for every two pixels with no less than 1 byte + const uint16_t countEndFrameBytes = ((_sizePixels / 4) + 15) / 16; + for (uint16_t endFrameByte = 0; endFrameByte < countEndFrameBytes; endFrameByte++) + { + _transmitByte(0xff); + } + + // set clock and data back to low between updates + digitalWrite(_pinData, LOW); + } + + uint8_t* getPixels() const + { + return _pixels; + }; + + size_t getPixelsSize() const + { + return _sizePixels; + }; + +private: + const uint8_t _pinClock; // output pin number for clock line + const uint8_t _pinData; // output pin number for data line + const size_t _sizePixels; // Size of '_pixels' buffer below + + uint8_t* _pixels; // Holds LED color values + + void _transmitByte(uint8_t data) + { + for (int bit = 7; bit >= 0; bit--) + { + // set data bit on pin + digitalWrite(_pinData, (data & 0x80) == 0x80 ? HIGH : LOW); + + // set clock high as data is ready + digitalWrite(_pinClock, HIGH); + + data <<= 1; + + // set clock low as data pin is changed + digitalWrite(_pinClock, LOW); + } + } +}; + +typedef DotStarGenericMethod DotStarMethod; + + diff --git a/src/internal/DotStarSpiMethod.h b/src/internal/DotStarSpiMethod.h new file mode 100644 index 0000000..40a0acf --- /dev/null +++ b/src/internal/DotStarSpiMethod.h @@ -0,0 +1,110 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for DotStars using SPI hardware (APA102). + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#include + +class DotStarSpiMethod +{ +public: + DotStarSpiMethod(uint16_t pixelCount, size_t elementSize) : + _sizePixels(pixelCount * elementSize) + { + _pixels = (uint8_t*)malloc(_sizePixels); + memset(_pixels, 0, _sizePixels); + } + + ~DotStarSpiMethod() + { + SPI.end(); + free(_pixels); + } + + bool IsReadyToUpdate() const + { + return true; // dot stars don't have a required delay + } + + void Initialize() + { + SPI.begin(); + +#if defined(ARDUINO_ARCH_ESP8266) + SPI.setFrequency(8000000L); +#elif defined(ARDUINO_ARCH_AVR) + SPI.setClockDivider(SPI_CLOCK_DIV2); // 8 MHz (6 MHz on Pro Trinket 3V) +#else + SPI.setClockDivider((F_CPU + 4000000L) / 8000000L); // 8-ish MHz on Due +#endif + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + } + + void Update() + { + // start frame + for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++) + { + SPI.transfer(0x00); + } + + // data + uint8_t* data = _pixels; + const uint8_t* endData = _pixels + _sizePixels; + while (data < endData) + { + SPI.transfer(*data++); + } + + // end frame + // one bit for every two pixels with no less than 1 byte + const uint16_t countEndFrameBytes = ((_sizePixels / 4) + 15) / 16; + for (uint16_t endFrameByte = 0; endFrameByte < countEndFrameBytes; endFrameByte++) + { + SPI.transfer(0xff); + } + } + + uint8_t* getPixels() const + { + return _pixels; + }; + + size_t getPixelsSize() const + { + return _sizePixels; + }; + +private: + const size_t _sizePixels; // Size of '_pixels' buffer below + + uint8_t* _pixels; // Holds LED color values +}; + + + + diff --git a/src/internal/HtmlColor.h b/src/internal/HtmlColor.h index e7cf273..a40d622 100644 --- a/src/internal/HtmlColor.h +++ b/src/internal/HtmlColor.h @@ -90,7 +90,7 @@ struct HtmlColor // ------------------------------------------------------------------------ HtmlColor(const RgbColor& color) { - Color = color.R << 16 | color.G << 8 | color.B; + Color = (uint32_t)color.R << 16 | (uint32_t)color.G << 8 | (uint32_t)color.B; } // ------------------------------------------------------------------------ diff --git a/src/internal/NeoPixelEsp8266.c b/src/internal/NeoPixelEsp8266.c index 73e0366..07e1e20 100644 --- a/src/internal/NeoPixelEsp8266.c +++ b/src/internal/NeoPixelEsp8266.c @@ -60,7 +60,6 @@ void ICACHE_RAM_ATTR bitbang_send_pixels_800(uint8_t* pixels, uint8_t* end, uint // do the checks here while we are waiting on time to pass uint32_t cyclesBit = ((subpix & mask)) ? CYCLES_800_T1H : CYCLES_800_T0H; uint32_t cyclesNext = cyclesStart; - uint32_t delta; // after we have done as much work as needed for this next bit // now wait for the HIGH