Skip to content

Instantly share code, notes, and snippets.

@jstockdale
Created May 22, 2016 20:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jstockdale/856f8abf41f13141d3566477ea551907 to your computer and use it in GitHub Desktop.
Save jstockdale/856f8abf41f13141d3566477ea551907 to your computer and use it in GitHub Desktop.
SK6812RGBW for Particle Photon
/*-------------------------------------------------------------------------
Spark Core, Photon, P1 and Electron library to control WS2811/WS2812 based RGB
LED devices such as Adafruit NeoPixel strips.
Currently handles 800 KHz and 400kHz bitstream on Spark Core and Photon,
WS2812, WS2812B and WS2811.
Also supports:
- Radio Shack Tri-Color Strip with TM1803 controller 400kHz bitstream.
- TM1829 pixels
PLEASE NOTE that the NeoPixels require 5V level inputs
and the Spark Core, Photon, P1 and Electron only have 3.3V level outputs.
Level shifting is necessary, but will require a fast device such as one
of the following:
[SN74HCT125N]
http://www.digikey.com/product-detail/en/SN74HCT125N/296-8386-5-ND/376860
[SN74HCT245N]
http://www.digikey.com/product-detail/en/SN74HCT245N/296-1612-5-ND/277258
Written by Phil Burgess / Paint Your Dragon for Adafruit Industries.
Modified to work with Particle devices by Technobly.
Contributions by PJRC and other members of the open source community.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing products
from Adafruit!
--------------------------------------------------------------------*/
/* ======================= Adafruit_NeoPixel.cpp ======================= */
/*-------------------------------------------------------------------------
This file is part of the Adafruit NeoPixel library.
NeoPixel 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.
NeoPixel 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
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "neopixel.h"
#if PLATFORM_ID == 0 // Core
#define pinLO(_pin) (PIN_MAP[_pin].gpio_peripheral->BRR = PIN_MAP[_pin].gpio_pin)
#define pinHI(_pin) (PIN_MAP[_pin].gpio_peripheral->BSRR = PIN_MAP[_pin].gpio_pin)
#elif (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) // Photon (6) or P1 (8) or Electron (10)
//STM32_Pin_Info* PIN_MAP2 = HAL_Pin_Map(); // Pointer required for highest access speed
#define pinLO(_pin) pinResetFast(_pin)
#define pinHI(_pin) pinSetFast(_pin)
#else
#error "*** PLATFORM_ID not supported by this library. PLATFORM should be Core, Photon, or P1 ***"
#endif
// fast pin access
#define pinSet(_pin, _hilo) (_hilo ? pinHI(_pin) : pinLO(_pin))
Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint8_t p, uint8_t t) :
numLEDs(n), numBytes(n*4), type(t), pin(p), brightness(0), pixels(NULL), endTime(0)
{
if((pixels = (uint8_t *)malloc(numBytes))) {
memset(pixels, 0, numBytes);
}
}
Adafruit_NeoPixel::~Adafruit_NeoPixel() {
if(pixels) free(pixels);
pinMode(pin, INPUT);
}
void Adafruit_NeoPixel::begin(void) {
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
void Adafruit_NeoPixel::show(void) {
if(!pixels) return;
// Data latch = 24 or 50 microsecond pause in the output stream. Rather than
// put a delay at the end of the function, the ending time is noted and
// the function will simply hold off (if needed) on issuing the
// subsequent round of data until the latch time has elapsed. This
// allows the mainline code to start generating the next frame of data
// rather than stalling for the latch.
uint32_t wait_time; // wait time in microseconds.
switch(type) {
case SK6812RGBW: // SK6812RGBW = 80us reset pulse but 50us seems to work
default: // default = 50us reset pulse
wait_time = 50L;
break;
}
while((micros() - endTime) < wait_time);
// endTime is a private member (rather than global var) so that multiple
// instances on different pins can be quickly issued in succession (each
// instance doesn't delay the next).
__disable_irq(); // Need 100% focus on instruction timing
volatile uint32_t
c, // 24-bit pixel color
mask; // bit mask
volatile uint16_t i = numBytes; // Output loop counter
volatile uint8_t
j, // 8-bit inner loop counter
*ptr = pixels, // Pointer to next byte
r, // Current red byte value
g, // Current green byte value
b, // Current blue byte value
w; // Current white byte value
if(type == SK6812RGBW) { // same as WS2812, 800 KHz bitstream
while(i > 0) { // While bytes left... (3 bytes = 1 pixel)
mask = 0x80000000; // reset the mask
i = i-4; // decrement bytes remaining
w = *ptr++; // Next white byte value
r = *ptr++; // Next red byte value
g = *ptr++; // Next green byte value
b = *ptr++; // Next blue byte value
c = ((uint32_t)r << 16) | ((uint32_t)g << 24) | ((uint32_t)b << 8) | w; // Pack the next 4 bytes to keep timing tight
j = 0; // reset the 32-bit counter
do {
pinSetFast(pin); // HIGH
if (c & mask) { // if masked bit is high
// WS2812 spec 700ns HIGH
// Adafruit on Arduino (meas. 812ns)
// This lib on Spark Core (meas. 804ns)
// This lib on Photon (meas. 792ns)
asm volatile(
"mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t"
#if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) // Photon (6) or P1 (8) or Electron (10)
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
#endif
::: "r0", "cc", "memory");
// WS2812 spec 600ns LOW
// Adafruit on Arduino (meas. 436ns)
// This lib on Spark Core (meas. 446ns)
// This lib on Photon (meas. 434ns)
pinResetFast(pin); // LOW
asm volatile(
"mov r0, r0" "\n\t"
#if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) // Photon (6) or P1 (8) or Electron (10)
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t"
#endif
::: "r0", "cc", "memory");
}
else { // else masked bit is low
// WS2812 spec 350ns HIGH
// Adafruit on Arduino (meas. 312ns)
// This lib on Spark Core (meas. 318ns)
// This lib on Photon (meas. 308ns)
asm volatile(
"mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
#if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) // Photon (6) or P1 (8) or Electron (10)
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t"
#endif
::: "r0", "cc", "memory");
// WS2812 spec 800ns LOW
// Adafruit on Arduino (meas. 938ns)
// This lib on Spark Core (meas. 944ns)
// This lib on Photon (meas. 936ns)
pinResetFast(pin); // LOW
asm volatile(
"mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
#if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) // Photon (6) or P1 (8) or Electron (10)
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
"nop" "\n\t" "nop" "\n\t"
#endif
::: "r0", "cc", "memory");
}
mask >>= 1;
} while ( ++j < 32 ); // ... pixel done
} // end while(i) ... no more pixels
}
__enable_irq();
endTime = micros(); // Save EOD time for latch on next call
}
// Set the output pin number
void Adafruit_NeoPixel::setPin(uint8_t p) {
pinMode(pin, INPUT);
pin = p;
pinMode(p, OUTPUT);
digitalWrite(p, LOW);
}
// Set pixel color from separate R,G,B components:
void Adafruit_NeoPixel::setPixelColor(
uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
setPixelColor(n,r,g,b,0);
}
void Adafruit_NeoPixel::setPixelColor(
uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
if(n < numLEDs) {
if(brightness) { // See notes in setBrightness()
r = (r * brightness) >> 8;
g = (g * brightness) >> 8;
b = (b * brightness) >> 8;
w = (w * brightness) >> 8;
}
uint8_t *p;
p = &pixels[n * 4]; // 4 bytes per pixel
p[0] = w; // Store W
p[1] = r; // Store R,G,B
p[2] = g;
p[3] = b;
}
}
// Set pixel color from 'packed' 32-bit RGB color:
void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) {
if(n < numLEDs) {
uint8_t
w = (uint8_t)(c >> 24),
r = (uint8_t)(c >> 16),
g = (uint8_t)(c >> 8),
b = (uint8_t)c;
if(brightness) { // See notes in setBrightness()
r = (r * brightness) >> 8;
g = (g * brightness) >> 8;
b = (b * brightness) >> 8;
w = (w * brightness) >> 8;
}
uint8_t *p = &pixels[n * 4];
switch(type) {
case SK6812RGBW:
*p++ = w;
*p++ = r;
*p++ = g;
*p = b;
break;
}
}
}
void Adafruit_NeoPixel::setColor(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue) {
return setPixelColor(aLedNumber, (uint8_t) aRed, (uint8_t) aGreen, (uint8_t) aBlue);
}
void Adafruit_NeoPixel::setColorScaled(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aScaling) {
// scale RGB with a common brightness parameter
setColor(aLedNumber, (aRed*aScaling)>>8, (aGreen*aScaling)>>8, (aBlue*aScaling)>>8);
}
byte Adafruit_NeoPixel::brightnessToPWM(byte aBrightness) {
static const byte pwmLevels[16] = { 0, 1, 2, 3, 4, 6, 8, 12, 23, 36, 48, 70, 95, 135, 190, 255 };
return pwmLevels[aBrightness>>4];
}
void Adafruit_NeoPixel::setColorDimmed(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aBrightness) {
setColorScaled(aLedNumber, aRed, aGreen, aBlue, brightnessToPWM(aBrightness));
}
// Convert separate R,G,B into packed 32-bit RGB color.
// Packed format is always RGB, regardless of LED strand color order.
uint32_t Adafruit_NeoPixel::Color(uint8_t r, uint8_t g, uint8_t b) {
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
// Convert separate R,G,B,W into packed 32-bit WRGB color.
// Packed format is always WRGB, regardless of LED strand color order.
uint32_t Adafruit_NeoPixel::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
// Query color from previously-set pixel (returns packed 32-bit RGB value)
uint32_t Adafruit_NeoPixel::getPixelColor(uint16_t n) const {
if(n >= numLEDs) {
// Out of bounds, return no color.
return 0;
}
uint8_t *p = &pixels[n * 4];
uint32_t c;
switch(type) {
case SK6812RGBW:
c = ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | ((uint32_t)p[3] << 0);
break;
}
// Adjust this back up to the true color, as setting a pixel color will
// scale it back down again.
if(brightness) { // See notes in setBrightness()
//Cast the color to a byte array
uint8_t * c_ptr =reinterpret_cast<uint8_t*>(&c);
c_ptr[0] = (c_ptr[0] << 8)/brightness;
c_ptr[1] = (c_ptr[1] << 8)/brightness;
c_ptr[2] = (c_ptr[2] << 8)/brightness;
c_ptr[3] = (c_ptr[3] << 8)/brightness;
}
return c; // Pixel # is out of bounds
}
uint8_t *Adafruit_NeoPixel::getPixels(void) const {
return pixels;
}
uint16_t Adafruit_NeoPixel::numPixels(void) const {
return numLEDs;
}
uint16_t Adafruit_NeoPixel::getNumLeds(void) const {
return numPixels();
}
// Adjust output brightness; 0=darkest (off), 255=brightest. This does
// NOT immediately affect what's currently displayed on the LEDs. The
// next call to show() will refresh the LEDs at this level. However,
// this process is potentially "lossy," especially when increasing
// brightness. The tight timing in the WS2811/WS2812 code means there
// aren't enough free cycles to perform this scaling on the fly as data
// is issued. So we make a pass through the existing color data in RAM
// and scale it (subsequent graphics commands also work at this
// brightness level). If there's a significant step up in brightness,
// the limited number of steps (quantization) in the old data will be
// quite visible in the re-scaled version. For a non-destructive
// change, you'll need to re-render the full strip data. C'est la vie.
void Adafruit_NeoPixel::setBrightness(uint8_t b) {
// Stored brightness value is different than what's passed.
// This simplifies 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.
uint8_t newBrightness = b + 1;
if(newBrightness != brightness) { // Compare against prior value
// Brightness has changed -- re-scale existing data in RAM
uint8_t c,
*ptr = pixels,
oldBrightness = brightness - 1; // De-wrap old brightness value
uint16_t scale;
if(oldBrightness == 0) scale = 0; // Avoid /0
else if(b == 255) scale = 65535 / oldBrightness;
else scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
for(uint16_t i=0; i<numBytes; i++) {
c = *ptr;
*ptr++ = (c * scale) >> 8;
}
brightness = newBrightness;
}
}
//Return the brightness value
uint8_t Adafruit_NeoPixel::getBrightness(void) const {
return brightness - 1;
}
void Adafruit_NeoPixel::clear(void) {
memset(pixels, 0, numBytes);
}
/*-------------------------------------------------------------------------
Spark Core, Photon, P1 and Electron library to control WS2811/WS2812 based RGB
LED devices such as Adafruit NeoPixel strips.
Currently handles 800 KHz and 400kHz bitstream on Spark Core and Photon,
WS2812, WS2812B and WS2811.
Also supports:
- Radio Shack Tri-Color Strip with TM1803 controller 400kHz bitstream.
- TM1829 pixels
PLEASE NOTE that the NeoPixels require 5V level inputs
and the Spark Core, Photon, P1 and Electron only have 3.3V level outputs.
Level shifting is necessary, but will require a fast device such as one
of the following:
[SN74HCT125N]
http://www.digikey.com/product-detail/en/SN74HCT125N/296-8386-5-ND/376860
[SN74HCT245N]
http://www.digikey.com/product-detail/en/SN74HCT245N/296-1612-5-ND/277258
Written by Phil Burgess / Paint Your Dragon for Adafruit Industries.
Modified to work with Particle devices by Technobly.
Contributions by PJRC and other members of the open source community.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing products
from Adafruit!
--------------------------------------------------------------------*/
/* ======================= Adafruit_NeoPixel.h ======================= */
/*--------------------------------------------------------------------
This file is part of the Adafruit NeoPixel library.
NeoPixel 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.
NeoPixel 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
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------*/
#ifndef SPARK_NEOPIXEL_H
#define SPARK_NEOPIXEL_H
#include "application.h"
// 'type' flags for LED pixels (third parameter to constructor):
#define SK6812RGBW 0x06 // 800 KHz datastream (NeoPixel)
#define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3))
#define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2))
#define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2))
class Adafruit_NeoPixel {
public:
// Constructor: number of LEDs, pin number, LED type
Adafruit_NeoPixel(uint16_t n, uint8_t p=2, uint8_t t=SK6812RGBW);
~Adafruit_NeoPixel();
void
begin(void),
show(void) __attribute__((optimize("Ofast"))),
setPin(uint8_t p),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w),
setPixelColor(uint16_t n, uint32_t c),
setBrightness(uint8_t),
setColor(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue),
setColorScaled(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aScaling),
setColorDimmed(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aBrightness),
clear(void);
uint8_t
*getPixels() const,
getBrightness(void) const;
uint16_t
numPixels(void) const,
getNumLeds(void) const;
static uint32_t
Color(uint8_t r, uint8_t g, uint8_t b),
Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w);
uint32_t
getPixelColor(uint16_t n) const;
byte
brightnessToPWM(byte aBrightness);
private:
const uint16_t
numLEDs, // Number of RGB LEDs in strip
numBytes; // Size of 'pixels' buffer below
const uint8_t
type; // Pixel type flag (400 vs 800 KHz)
uint8_t
pin, // Output pin number
brightness,
*pixels, // Holds LED color values (3 bytes each)
rOffset, // Index of red byte within each 3- or 4-byte pixel
gOffset, // Index of green byte
bOffset, // Index of blue byte
wOffset;
uint32_t
endTime; // Latch timing reference
};
#endif // ADAFRUIT_NEOPIXEL_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment