├── LICENSE
├── README.md
├── examples
├── a-rainbow
│ └── a-rainbow.cpp
├── extra-examples
│ └── extra-examples.cpp
└── rgbw-strandtest
│ └── rgbw-strandtest.cpp
├── library.properties
└── src
├── neopixel.cpp
├── neopixel.h
└── neopixel
└── neopixel.h
/LICENSE:
--------------------------------------------------------------------------------
1 | This package is free software; you can redistribute it and/or modify it
2 | under the terms of the GNU Lesser General Public License as published by
3 | the Free Software Foundation; either version 3 of the License, or (at
4 | your option) any later version.
5 |
6 | This package is distributed in the hope that it will be useful, but
7 | WITHOUT ANY WARRANTY; without even the implied warranty of
8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
9 | General Public License for more details.
10 |
11 | The complete text of the GNU Lesser General
12 | Public License can be found at .
13 |
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Particle-NeoPixel
2 |
3 | A library for manipulating NeoPixel RGB LEDs for the:
4 |
5 | Particle Core, Photon, P1, Electron, Argon, Boron, Xenon and RedBear Duo
6 |
7 | Implementation based on Adafruit's NeoPixel Library.
8 |
9 | ## Supported Pixel Types
10 | - 800 KHz WS2812, WS2812B, WS2813 and 400kHz bitstream and WS2811
11 | - 800 KHz bitstream SK6812RGBW (NeoPixel RGBW pixel strips)
12 | (use 'SK6812RGBW' as PIXEL_TYPE)
13 |
14 | The most common kinds are WS2812/WS2813 (6-pin part), WS2812B (4-pin part) and SK6812RGBW (3 colors + white).
15 |
16 | #### Also supports these less common pixels
17 |
18 | - Radio Shack Tri-Color LED Strip with TM1803 controller 400kHz bitstream.
19 | - TM1829 pixels, many [details here.](https://community.particle.io/t/neopixel-library-for-tm1829-controller-resolved/5363)
20 | - Some functions from the [MessageTorch library](https://github.com/plan44/messagetorch/blob/master/messagetorch.cpp#L58-L134) have been added.
21 | - SK6812MINI "NeoPixel Mini" (use 'WS2812B' as `PIXEL_TYPE`)
22 |
23 | ## Usage
24 |
25 | Set up the hardware:
26 |
27 | - A NeoPixel digital RGB LED (get at [adafruit.com](https://www.adafruit.com))
28 | - or a Radio Shack Tri-Color LED Strip (get at [radioshack.com](https://www.radioshack.com))
29 | - A power supply or breakout board to supply NeoPixel's with 5V
30 |
31 | Flash the [rainbow example](examples/a-rainbow/a-rainbow.cpp). With the
32 | [Particle CLI](https://docs.particle.io/guide/tools-and-features/cli/)
33 | do `particle flash examples/a-rainbow`
34 |
35 | Adapt it to your needs while keeping this general structure:
36 |
37 | ```cpp
38 | Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
39 | void setup() {
40 | strip.begin();
41 | strip.show();
42 | }
43 | void loop() {
44 | // change your pixel colors and call strip.show() again
45 | }
46 | ```
47 |
48 | ## Documentation
49 |
50 | ### `Adafruit_NeoPixel`
51 |
52 | ```
53 | // IMPORTANT: Set pixel COUNT, PIN and TYPE
54 | #define PIXEL_COUNT 10
55 | #define PIXEL_PIN D2
56 | #define PIXEL_TYPE WS2812B
57 | Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
58 | ```
59 |
60 | Creates an object to interact wth a NeoPixel strip.
61 |
62 | `PIXEL_COUNT` is the number of pixels in strip.
63 |
64 | _Note: for some stripes like those with the TM1829, you need to count the number of segments, i.e. the number of controllers in your stripe, not the number of individual LEDs!_
65 |
66 | `PIXEL_PIN` is the pin number where your NeoPixel are connected (A0-A7, D0-D7, etc). If omitted, D2 is used.
67 |
68 | On Photon, Electron, P1, Core and Duo, any pin can be used for Neopixel.
69 |
70 | On the Argon, Boron and Xenon, only these pins can be used for Neopixel:
71 | - D2, D3, A4, A5
72 | - D4, D6, D7, D8
73 | - A0, A1, A2, A3
74 |
75 | In addition on the Argon/Boron/Xenon, only one pin per group can be used at a time. So it's OK to have one Adafruit_NeoPixel
76 | instance on pin D2 and another one on pin A2, but it's not possible to have one on pin A0 and another
77 | one on pin A1.
78 |
79 | `PIXEL_TYPE` is the type of LED, one of WS2811, WS2812, WS2812B, WS2812B2, WS2813, TM1803, TM1829, SK6812RGBW. If omitted, WS2812B is used.
80 |
81 | _Note: For legacy 50us reset pulse timing on WS2812/WS2812B or WS2812B2, select WS2812B_FAST or WS2812B2_FAST respectively. Otherwise, 300us timing will be used._
82 |
83 | _Note: RGB order is automatically applied to WS2811, WS2812/WS2812B/WS2812B2/WS2813/TM1803 is GRB order._
84 |
85 | ### `begin`
86 |
87 | `strip.begin();`
88 |
89 | Sets up the pin used for the NeoPixel strip.
90 |
91 | ### `setPixelColor`
92 | ### `setColor`
93 |
94 | ```
95 | strip.setPixelColor(num, red, green, blue);
96 | strip.setPixelColor(num, red, green, blue, white);
97 | strip.setPixelColor(num, color);
98 | strip.setColor(num, red, green, blue);
99 | strip.setColor(num, red, green, blue, white);
100 | ```
101 |
102 | Set the color of LED number `num` (0 to `PIXEL_COUNT-1`). `red`,
103 | `green`, `blue`, `white` are between 0 and 255. White is only used for
104 | RGBW type pixels. `color` is a color returned from [`Color`](#color).
105 |
106 | The brightness set with `setBrightness` will modify the color before it
107 | is applied to the LED.
108 |
109 | ### `show`
110 |
111 | `strip.show();`
112 |
113 | Displays the colors on the NeoPixel strip that were set with `setPixelColor` and other calls that change the color of LEDs.
114 |
115 | This function takes some time to run (more time the more LEDs you have) and disables interrupts while running.
116 |
117 | ### `clear`
118 |
119 | `strip.clear();`
120 |
121 | Set all LED color to off. Will take effect on next `show()`.
122 |
123 | ### `setBrightness`
124 |
125 | `strip.setBrightness(brightness);`
126 |
127 | Make the LED less bright. `brightness` is from 0 (off) to 255 (max brightness) and defaults to 255.
128 |
129 | This factor is not linear: 128 is not visibly half as bright as 255 but almost as bright.
130 |
131 | ### `getBrightness`
132 |
133 | `uint8_t brightness = strip.getBrightness();`
134 |
135 | Get the current brightness.
136 |
137 | ### `setColorScaled`
138 |
139 | ```
140 | strip.setColorScaled(num, red, green, blue, scaling);
141 | strip.setColorScaled(num, red, green, blue, white, scaling);
142 | ```
143 |
144 | Set the color of LED number `num` and scale that color non-linearly according to the `scaling` parameter (0 to 255).
145 |
146 | ### `setColorDimmed`
147 |
148 | ```
149 | strip.setColorDimmed(num, red, green, blue, brightness);
150 | strip.setColorDimmed(num, red, green, blue, white, brightness);
151 | ```
152 |
153 | Set the color of LED number `num` and dim that color linearly according to the `brightness` parameter (0 to 255). In this case 128 should look half as bright as 255.
154 |
155 | ### `Color`
156 |
157 | ```
158 | uint32_t color = strip.Color(red, green, blue);
159 | uint32_t color = strip.Color(red, green, blue, white);
160 | ```
161 |
162 | Make a color from component colors. Useful if you want to store colors in a variable or pass them as function arguments.
163 |
164 | ### `getPixelColor`
165 |
166 | `uint32_t color = strip.getPixelColor();`
167 |
168 | Get the current color of an LED in the same format as [`Color`](#color).
169 |
170 | ### `setPin`
171 |
172 | `strip.setPin(pinNumber);`
173 |
174 | Change the pin used for the NeoPixel strip.
175 |
176 | ### `updateLength`
177 |
178 | `strip.updateLength(n);`
179 |
180 | Change the number of LEDs in the NeoPixel strip.
181 |
182 | ### `getPixels`
183 |
184 | `uint8_t *pixels = strip.getPixels();`
185 |
186 | Get the raw color data for the LEDs.
187 |
188 | ### `getNumLeds`
189 | ### `numPixels`
190 |
191 | ```
192 | uint16_t n = strip.getNumLeds();
193 | uint16_t n = strip.numPixels();
194 | ```
195 |
196 | Get the number of LEDs in the NeoPixel strip. `numPixels` is an alias for `getNumLeds`.
197 |
198 | ## Nuances
199 |
200 | - Make sure get the # of pixels, pin number, type of pixels correct
201 | - NeoPixels require 5V logic level inputs and the Spark Core and Photon only have 3.3V logic level digital outputs. Level shifting from 3.3V to 5V is necessary, the Particle Shield Shield has the [TXB0108PWR](http://www.digikey.com/product-search/en?pv7=2&k=TXB0108PWR) 3.3V to 5V level shifter built in (but has been known to oscillate at 50MHz with wire length longer than 6"), alternatively you can wire up your own with a [SN74HCT245N](http://www.digikey.com/product-detail/en/SN74HCT245N/296-1612-5-ND/277258), or [SN74HCT125N](http://www.digikey.com/product-detail/en/SN74HCT125N/296-8386-5-ND/376860). These are rock solid.
202 | - To reduce NeoPixel burnout risk, add 1000 uF capacitor across pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input and minimize distance between device and first pixel. Avoid connecting on a live circuit. If you must, connect GND first.
203 | - Don't use `getPixelColor()` to move pixel data around when you are also using `setBrightness()`. When the brightness is set, all `setPixelColor()` calls will end up scaling colors to dim them before they are stored in memory. When using `getPixelColor()` the stored dimmed color is rescaled back up to the original color. However, due to some loss of precision with the math, it is not possible to recreate this color data perfectly. This is especially true with low brightness values. If you `get` and `set` color data repeatedly with a dimmed pixel, it will eventually continue to decrease in value until it is equal to zero.
204 | - When changing the brightness, always call `setPixelColor()` first with fresh un-dimmed color data, then call `setBrightness()`, and finally `show()`.
205 |
206 | ## References
207 |
208 | - NeoPixel Guide: https://learn.adafruit.com/adafruit-neopixel-uberguide
209 | - Quad Level Shifter IC: [SN74ACHT125N](https://www.adafruit.com/product/1787) (Adafruit)
210 | - Quad Level Shifter IC: [SN74HCT125N](http://www.digikey.com/product-detail/en/SN74HCT125N/296-8386-5-ND/376860) (Digikey)
211 | - Quad Level Shifter IC: [SN74AHCT125N](http://www.digikey.com/product-detail/en/SN74AHCT125N/296-4655-5-ND/375798) (Digikey)
212 |
213 | ## License
214 | Copyright 2014-2018 Technobly, Julien Vanier, Cullen Shane, Phil Burgess
215 |
216 | Released under the LGPL license
217 |
--------------------------------------------------------------------------------
/examples/a-rainbow/a-rainbow.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a minimal example, see extra-examples.cpp for a version
3 | * with more explantory documentation, example routines, how to
4 | * hook up your pixels and all of the pixel types that are supported.
5 | *
6 | * On Photon, Electron, P1, Core and Duo, any pin can be used for Neopixel.
7 | *
8 | * On the Argon, Boron and Xenon, only these pins can be used for Neopixel:
9 | * - D2, D3, A4, A5
10 | * - D4, D6, D7, D8
11 | * - A0, A1, A2, A3
12 | *
13 | * On Photon 2 / P2, only SPI(MOSI) or SPI1(D2) can be used for Neopixel,
14 | * and only PIXEL_TYPE's WS2812, WS2812B, WS2813 are supported.
15 | * - MISO (D12), SCK (D13) and SS (D8) can be used as GPIO when using SPI
16 | * - MISO1 (D3), SCK1 (D4) and SS1 (D5) can be used and GPIO when using SPI1
17 | * note: You may want to call System.disableFeature(FEATURE_ETHERNET_DETECTION);
18 | * to disable automatic Ethernet driver detection, which will cause some
19 | * glitches to SPI1 pins D2,D3,D4.
20 | *
21 | * In addition on the Argon/Boron/Xenon, only one pin per group can be used at a time.
22 | * So it's OK to have one Adafruit_NeoPixel instance on pin D2 and another one on pin
23 | * A2, but it's not possible to have one on pin A0 and another one on pin A1.
24 | */
25 |
26 | #include "Particle.h"
27 | #include "neopixel.h"
28 |
29 | SYSTEM_MODE(AUTOMATIC);
30 |
31 | // IMPORTANT: Set pixel COUNT, PIN and TYPE
32 | #if (PLATFORM_ID == 32)
33 | // MOSI pin MO
34 | #define PIXEL_PIN SPI
35 | // MOSI pin D2
36 | // #define PIXEL_PIN SPI1
37 | #else // #if (PLATFORM_ID == 32)
38 | #define PIXEL_PIN D3
39 | #endif
40 | #define PIXEL_COUNT 11
41 | #define PIXEL_TYPE WS2812B
42 |
43 | Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
44 |
45 | // Prototypes for local build, ok to leave in for Build IDE
46 | void rainbow(uint8_t wait);
47 | uint32_t Wheel(byte WheelPos);
48 |
49 | void setup()
50 | {
51 | strip.begin();
52 | strip.show(); // Initialize all pixels to 'off'
53 | }
54 |
55 | void loop()
56 | {
57 | rainbow(20);
58 | }
59 |
60 | void rainbow(uint8_t wait) {
61 | uint16_t i, j;
62 |
63 | for(j=0; j<256; j++) {
64 | for(i=0; i> 8);
110 | }
111 | uint8_t green(uint32_t c) {
112 | return (c >> 16);
113 | }
114 | uint8_t blue(uint32_t c) {
115 | return (c);
116 | }
117 |
118 | // Fill the dots one after the other with a color
119 | void colorWipe(uint32_t c, uint8_t wait) {
120 | for(uint16_t i=0; i= 0 ; j--){
137 | for(uint16_t i=0; i 255 - fadeMax ) {
170 | fadeVal--;
171 | }
172 |
173 | strip.show();
174 | delay(wait);
175 | }
176 | }
177 |
178 | delay(500);
179 |
180 | for(int k = 0 ; k < whiteLoops ; k ++) {
181 | for(int j = 0; j < 256 ; j++) {
182 | for(uint16_t i=0; i < strip.numPixels(); i++) {
183 | strip.setPixelColor(i, strip.Color(0,0,0, gamma[j] ) );
184 | }
185 | strip.show();
186 | }
187 |
188 | delay(2000);
189 | for(int j = 255; j >= 0 ; j--) {
190 | for(uint16_t i=0; i < strip.numPixels(); i++) {
191 | strip.setPixelColor(i, strip.Color(0,0,0, gamma[j] ) );
192 | }
193 | strip.show();
194 | }
195 | }
196 |
197 | delay(500);
198 | }
199 |
200 | void whiteOverRainbow(uint8_t wait, uint8_t whiteSpeed, uint8_t whiteLength ) {
201 |
202 | if(whiteLength >= strip.numPixels()) whiteLength = strip.numPixels() - 1;
203 |
204 | int head = whiteLength - 1;
205 | int tail = 0;
206 | int loops = 3;
207 | int loopNum = 0;
208 | static unsigned long lastTime = 0;
209 |
210 | while(true) {
211 | for(int j=0; j<256; j++) {
212 | for(uint16_t i=0; i= tail && i <= head)
214 | || (tail > head && i >= tail)
215 | || (tail > head && i <= head) ) {
216 | strip.setPixelColor(i, strip.Color(0,0,0, 255 ) );
217 | } else {
218 | strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
219 | }
220 | }
221 |
222 | if(millis() - lastTime > whiteSpeed) {
223 | head++;
224 | tail++;
225 | if(head == strip.numPixels()) {
226 | loopNum++;
227 | }
228 | lastTime = millis();
229 | }
230 |
231 | if(loopNum == loops) return;
232 |
233 | head %= strip.numPixels();
234 | tail %= strip.numPixels();
235 | strip.show();
236 | delay(wait);
237 | }
238 | }
239 |
240 | }
241 |
242 | void fullWhite() {
243 | for(uint16_t i=0; i.
52 | -------------------------------------------------------------------------*/
53 |
54 | #include "neopixel.h"
55 |
56 | #if PLATFORM_ID == 0 // Core (0)
57 | #define pinLO(_pin) (PIN_MAP[_pin].gpio_peripheral->BRR = PIN_MAP[_pin].gpio_pin)
58 | #define pinHI(_pin) (PIN_MAP[_pin].gpio_peripheral->BSRR = PIN_MAP[_pin].gpio_pin)
59 | #elif (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
60 | #if SYSTEM_VERSION < SYSTEM_VERSION_ALPHA(5,0,0,2)
61 | STM32_Pin_Info* PIN_MAP2 = HAL_Pin_Map(); // Pointer required for highest access speed
62 | #else
63 | STM32_Pin_Info* PIN_MAP2 = hal_pin_map(); // Pointer required for highest access speed
64 | #endif // SYSTEM_VERSION < SYSTEM_VERSION_ALPHA(5,0,0,2)
65 | #define pinLO(_pin) (PIN_MAP2[_pin].gpio_peripheral->BSRRH = PIN_MAP2[_pin].gpio_pin)
66 | #define pinHI(_pin) (PIN_MAP2[_pin].gpio_peripheral->BSRRL = PIN_MAP2[_pin].gpio_pin)
67 | #elif HAL_PLATFORM_NRF52840 // Argon, Boron, Xenon, B SoM, B5 SoM, E SoM X, Tracker
68 | #include "nrf.h"
69 | #include "nrf_gpio.h"
70 | #include "pinmap_impl.h"
71 | #if SYSTEM_VERSION < SYSTEM_VERSION_ALPHA(5,0,0,2)
72 | NRF5x_Pin_Info* PIN_MAP2 = HAL_Pin_Map();
73 | #else
74 | NRF5x_Pin_Info* PIN_MAP2 = hal_pin_map();
75 | #endif // SYSTEM_VERSION < SYSTEM_VERSION_ALPHA(5,0,0,2)
76 | #define pinLO(_pin) (nrf_gpio_pin_clear(NRF_GPIO_PIN_MAP(PIN_MAP2[_pin].gpio_port, PIN_MAP2[_pin].gpio_pin)))
77 | #define pinHI(_pin) (nrf_gpio_pin_set(NRF_GPIO_PIN_MAP(PIN_MAP2[_pin].gpio_port, PIN_MAP2[_pin].gpio_pin)))
78 | #elif (PLATFORM_ID == 32) // HAL_PLATFORM_RTL872X
79 | // nothing extra needed for P2
80 | #else
81 | #error "*** PLATFORM_ID not supported by this library. PLATFORM should be Particle Core, Photon, Electron, Argon, Boron, Xenon, RedBear Duo, B SoM, B5 SoM, E SoM X, Tracker or P2 ***"
82 | #endif
83 | // fast pin access
84 | #define pinSet(_pin, _hilo) (_hilo ? pinHI(_pin) : pinLO(_pin))
85 |
86 | #if (PLATFORM_ID == 32)
87 | Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, SPIClass& spi, uint8_t t) :
88 | begun(false), type(t), brightness(0), pixels(NULL), endTime(0)
89 | {
90 | updateLength(n);
91 | spi_ = &spi;
92 | }
93 | #else
94 | Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint8_t p, uint8_t t) :
95 | begun(false), type(t), brightness(0), pixels(NULL), endTime(0)
96 | {
97 | updateLength(n);
98 | setPin(p);
99 | }
100 |
101 | #endif // #if (PLATFORM_ID == 32)
102 |
103 | Adafruit_NeoPixel::~Adafruit_NeoPixel() {
104 | if (pixels) free(pixels);
105 | #if (PLATFORM_ID == 32)
106 | spi_->end();
107 | #else
108 | if (begun) pinMode(pin, INPUT);
109 | #endif
110 | }
111 |
112 | uint8_t Adafruit_NeoPixel::getPin() const {
113 | return pin;
114 | }
115 |
116 | uint8_t Adafruit_NeoPixel::getType() const {
117 | return type;
118 | }
119 |
120 | void Adafruit_NeoPixel::updateLength(uint16_t n) {
121 | if (pixels) free(pixels); // Free existing data (if any)
122 |
123 | // Allocate new data -- note: ALL PIXELS ARE CLEARED
124 | numBytes = n * ((type == SK6812RGBW) ? 4 : 3);
125 | if ((pixels = (uint8_t *)malloc(numBytes))) {
126 | memset(pixels, 0, numBytes);
127 | numLEDs = n;
128 | } else {
129 | numLEDs = numBytes = 0;
130 | }
131 | }
132 |
133 | void Adafruit_NeoPixel::begin(void) {
134 | #if (PLATFORM_ID == 32)
135 | if (getType() == WS2812B) {
136 | if (spi_->interface() >= HAL_PLATFORM_SPI_NUM) {
137 | Log.error("SPI/SPI1 interface not defined!");
138 | return;
139 | }
140 |
141 | pin_t sckPin = SCK;
142 | pin_t misoPin = MISO;
143 | if (spi_->interface() == HAL_SPI_INTERFACE1) {
144 | sckPin = SCK;
145 | misoPin = MISO;
146 | } else if (spi_->interface() == HAL_SPI_INTERFACE2) {
147 | sckPin = SCK1;
148 | misoPin = MISO1;
149 | }
150 | PinMode sckPinMode = getPinMode(sckPin);
151 | PinMode misoPinMode = getPinMode(misoPin);
152 | int sckValue = (sckPinMode == OUTPUT) ? digitalRead(sckPin) : 0;
153 | int misoValue = (misoPinMode == OUTPUT) ? digitalRead(misoPin) : 0;
154 | // spi_->begin(PIN_INVALID); // PIN_INVALID will keep begin from taking over the default SS/SS1 pin as OUTPUT
155 | // Note: no Wiring API yet to configure SPI for MOSI ONLY
156 | hal_spi_config_t spi_config = {};
157 | spi_config.size = sizeof(spi_config);
158 | spi_config.version = HAL_SPI_CONFIG_VERSION;
159 | spi_config.flags = (uint32_t)HAL_SPI_CONFIG_FLAG_MOSI_ONLY;
160 | hal_spi_begin_ext(spi_->interface(), SPI_MODE_MASTER, PIN_INVALID, &spi_config);
161 | spi_->setClockSpeed(3125000); // DVOS 5.7.0 requires setClockSpeed() to be set after begin()
162 | // allow SCLK and MISO pin to be used as GPIO
163 | pinMode(sckPin, sckPinMode);
164 | pinMode(misoPin, misoPinMode);
165 | if (sckPinMode == OUTPUT) {
166 | digitalWrite(sckPin, sckValue);
167 | }
168 | if (misoPinMode == OUTPUT) {
169 | digitalWrite(misoPin, misoValue);
170 | }
171 | }
172 | #else
173 | pinMode(pin, OUTPUT);
174 | digitalWrite(pin, LOW);
175 | #endif // #if (PLATFORM_ID == 32)
176 | begun = true;
177 | }
178 |
179 | // Set the output pin number
180 | void Adafruit_NeoPixel::setPin(uint8_t p) {
181 | if (begun) {
182 | pinMode(pin, INPUT);
183 | }
184 | pin = p;
185 | if (begun) {
186 | pinMode(p, OUTPUT);
187 | digitalWrite(p, LOW);
188 | }
189 | }
190 |
191 | void Adafruit_NeoPixel::show(void) {
192 | if(!pixels) return;
193 |
194 | #if (PLATFORM_ID != 32)
195 | // Data latch = 24 or 50 microsecond pause in the output stream. Rather than
196 | // put a delay at the end of the function, the ending time is noted and
197 | // the function will simply hold off (if needed) on issuing the
198 | // subsequent round of data until the latch time has elapsed. This
199 | // allows the mainline code to start generating the next frame of data
200 | // rather than stalling for the latch.
201 | uint32_t wait_time; // wait time in microseconds.
202 | switch(type) {
203 | case TM1803: { // TM1803 = 24us reset pulse
204 | wait_time = 24L;
205 | } break;
206 | case SK6812RGBW: { // SK6812RGBW = 80us reset pulse
207 | wait_time = 80L;
208 | } break;
209 | case TM1829: { // TM1829 = 500us reset pulse
210 | wait_time = 500L;
211 | } break;
212 | case WS2812B: // WS2812, WS2812B & WS2813 = 300us reset pulse
213 | case WS2812B2: {
214 | wait_time = 300L;
215 | } break;
216 | case WS2811: // WS2811, WS2812B_FAST & WS2812B2_FAST = 50us reset pulse
217 | case WS2812B_FAST:
218 | case WS2812B2_FAST:
219 | default: { // default = 50us reset pulse
220 | wait_time = 50L;
221 | } break;
222 | }
223 | while((micros() - endTime) < wait_time);
224 | // endTime is a private member (rather than global var) so that multiple
225 | // instances on different pins can be quickly issued in succession (each
226 | // instance doesn't delay the next).
227 | #endif // (PLATFORM_ID != 32)
228 |
229 | #if (PLATFORM_ID == 0) || (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Core (0), Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
230 | __disable_irq(); // Need 100% focus on instruction timing
231 |
232 | volatile uint32_t
233 | c, // 24-bit/32-bit pixel color
234 | mask; // 1-bit mask
235 | volatile uint16_t i = numBytes; // Output loop counter
236 | volatile uint8_t
237 | j, // 8-bit inner loop counter
238 | *ptr = pixels, // Pointer to next byte
239 | g, // Current green byte value
240 | r, // Current red byte value
241 | b, // Current blue byte value
242 | w; // Current white byte value
243 |
244 | if(type == WS2812B || type == WS2812B_FAST) { // Same as WS2812 & WS2813, 800 KHz bitstream
245 | while(i) { // While bytes left... (3 bytes = 1 pixel)
246 | mask = 0x800000; // reset the mask
247 | i = i-3; // decrement bytes remaining
248 | g = *ptr++; // Next green byte value
249 | r = *ptr++; // Next red byte value
250 | b = *ptr++; // Next blue byte value
251 | c = ((uint32_t)g << 16) | ((uint32_t)r << 8) | b; // Pack the next 3 bytes to keep timing tight
252 | j = 0; // reset the 24-bit counter
253 | do {
254 | pinSet(pin, HIGH); // HIGH
255 | if (c & mask) { // if masked bit is high
256 | // WS2812 spec 700ns HIGH
257 | // Adafruit on Arduino (meas. 812ns)
258 | // This lib on Spark Core (meas. 804ns)
259 | // This lib on Photon (meas. 792ns)
260 | asm volatile(
261 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
262 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
263 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
264 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
265 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
266 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
267 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
268 | "nop" "\n\t" "nop" "\n\t"
269 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
270 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
271 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
272 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
273 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
274 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
275 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
276 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
277 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
278 | #endif
279 | ::: "r0", "cc", "memory");
280 | // WS2812 spec 600ns LOW
281 | // Adafruit on Arduino (meas. 436ns)
282 | // This lib on Spark Core (meas. 446ns)
283 | // This lib on Photon (meas. 434ns)
284 | pinSet(pin, LOW); // LOW
285 | asm volatile(
286 | "mov r0, r0" "\n\t"
287 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
288 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
289 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
290 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
291 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
292 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
293 | "nop" "\n\t"
294 | #endif
295 | ::: "r0", "cc", "memory");
296 | }
297 | else { // else masked bit is low
298 | // WS2812 spec 350ns HIGH
299 | // Adafruit on Arduino (meas. 312ns)
300 | // This lib on Spark Core (meas. 318ns)
301 | // This lib on Photon (meas. 308ns)
302 | asm volatile(
303 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
304 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
305 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
306 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
307 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
308 | "nop" "\n\t"
309 | #endif
310 | ::: "r0", "cc", "memory");
311 | // WS2812 spec 800ns LOW
312 | // Adafruit on Arduino (meas. 938ns)
313 | // This lib on Spark Core (meas. 944ns)
314 | // This lib on Photon (meas. 936ns)
315 | pinSet(pin, LOW); // LOW
316 | asm volatile(
317 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
318 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
319 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
320 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
321 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
322 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
323 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
324 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
325 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
326 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
327 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
328 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
329 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
330 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
331 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
332 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
333 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
334 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
335 | "nop" "\n\t" "nop" "\n\t"
336 | #endif
337 | ::: "r0", "cc", "memory");
338 | }
339 | mask >>= 1;
340 | } while ( ++j < 24 ); // ... pixel done
341 | } // end while(i) ... no more pixels
342 | }
343 | else if(type == SK6812RGBW) { // similar to WS2812, 800 KHz bitstream but with RGB+W components
344 | while(i) { // While bytes left... (4 bytes = 1 pixel)
345 | mask = 0x80000000; // reset the mask
346 | i = i-4; // decrement bytes remaining
347 | r = *ptr++; // Next red byte value
348 | g = *ptr++; // Next green byte value
349 | b = *ptr++; // Next blue byte value
350 | w = *ptr++; // Next white byte value
351 | c = ((uint32_t)r << 24) | ((uint32_t)g << 16) | ((uint32_t)b << 8) | w; // Pack the next 4 bytes to keep timing tight
352 | j = 0; // reset the 32-bit counter
353 | do {
354 | pinSet(pin, HIGH); // HIGH
355 | if (c & mask) { // if masked bit is high
356 | // SK6812RGBW spec 600ns HIGH
357 | // WS2812 spec 700ns HIGH
358 | // Adafruit on Arduino (meas. 812ns)
359 | // This lib on Spark Core (meas. 610ns)
360 | // This lib on Photon (meas. 608ns)
361 | asm volatile(
362 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
363 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
364 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
365 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
366 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
367 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
368 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
369 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
370 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
371 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
372 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
373 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
374 | "nop" "\n\t" "nop" "\n\t"
375 | #endif
376 | ::: "r0", "cc", "memory");
377 | // SK6812RGBW spec 600ns LOW
378 | // WS2812 spec 600ns LOW
379 | // Adafruit on Arduino (meas. 436ns)
380 | // This lib on Spark Core (meas. 598ns)
381 | // This lib on Photon (meas. 600ns)
382 | pinSet(pin, LOW); // LOW
383 | asm volatile(
384 | "mov r0, r0" "\n\t"
385 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
386 | "nop" "\n\t"
387 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
388 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
389 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
390 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
391 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
392 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
393 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
394 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
395 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
396 | #endif
397 | ::: "r0", "cc", "memory");
398 | }
399 | else { // else masked bit is low
400 | // SK6812RGBW spec 300ns HIGH
401 | // WS2812 spec 350ns HIGH
402 | // Adafruit on Arduino (meas. 312ns)
403 | // This lib on Spark Core (meas. 305ns)
404 | // This lib on Photon (meas. 308ns)
405 | asm volatile(
406 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
407 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
408 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
409 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
410 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
411 | "nop" "\n\t"
412 | #endif
413 | ::: "r0", "cc", "memory");
414 | // SK6812RGBW spec 900ns LOW
415 | // WS2812 spec 800ns LOW
416 | // Adafruit on Arduino (meas. 938ns)
417 | // This lib on Spark Core (meas. 904ns)
418 | // This lib on Photon (meas. 900ns)
419 | pinSet(pin, LOW); // LOW
420 | asm volatile(
421 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
422 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
423 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
424 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
425 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
426 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
427 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
428 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
429 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
430 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
431 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
432 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
433 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
434 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
435 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
436 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
437 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
438 | "nop" "\n\t"
439 | #endif
440 | ::: "r0", "cc", "memory");
441 | }
442 | mask >>= 1;
443 | } while ( ++j < 32 ); // ... pixel done
444 | } // end while(i) ... no more pixels
445 | }
446 | else if(type == WS2812B2 || type == WS2812B2_FAST) { // WS2812B with DWT timer
447 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
448 | #define CYCLES_800_T0H 25 // 312ns (meas. 300ns)
449 | #define CYCLES_800_T0L 70 // 938ns (meas. 940ns)
450 | #define CYCLES_800_T1H 80 // 812ns (meas. 792ns)
451 | #define CYCLES_800_T1L 8 // 436ns (meas. 425ns)
452 |
453 | volatile uint32_t cyc;
454 |
455 | while(i) { // While bytes left... (3 bytes = 1 pixel)
456 | mask = 0x800000; // reset the mask
457 | i = i-3; // decrement bytes remaining
458 | g = *ptr++; // Next green byte value
459 | r = *ptr++; // Next red byte value
460 | b = *ptr++; // Next blue byte value
461 | c = ((uint32_t)g << 16) | ((uint32_t)r << 8) | b; // Pack the next 3 bytes to keep timing tight
462 | j = 0; // reset the 24-bit counter
463 | do {
464 | cyc = DWT->CYCCNT;
465 | pinSet(pin, HIGH); // HIGH
466 | if (c & mask) { // if masked bit is high
467 | while(DWT->CYCCNT - cyc < CYCLES_800_T1H);
468 | pinSet(pin, LOW);
469 | cyc = DWT->CYCCNT;
470 | while(DWT->CYCCNT - cyc < CYCLES_800_T1L);
471 | }
472 | else { // else masked bit is low
473 | while(DWT->CYCCNT - cyc < CYCLES_800_T0H);
474 | pinSet(pin, LOW);
475 | cyc = DWT->CYCCNT;
476 | while(DWT->CYCCNT - cyc < CYCLES_800_T0L);
477 | }
478 | mask >>= 1;
479 | } while ( ++j < 24 ); // ... pixel done
480 | } // end while(i) ... no more pixels
481 | #endif
482 | }
483 | else if(type == WS2811) { // WS2811, 400 KHz bitstream
484 | while(i) { // While bytes left... (3 bytes = 1 pixel)
485 | mask = 0x800000; // reset the mask
486 | i = i-3; // decrement bytes remaining
487 | r = *ptr++; // Next red byte value
488 | g = *ptr++; // Next green byte value
489 | b = *ptr++; // Next blue byte value
490 | c = ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; // Pack the next 3 bytes to keep timing tight
491 | j = 0; // reset the 24-bit counter
492 | do {
493 | pinSet(pin, HIGH); // HIGH
494 | if (c & mask) { // if masked bit is high
495 | // WS2811 spec 1.20us HIGH
496 | // Adafruit on Arduino (meas. 1.25us)
497 | // This lib on Spark Core (meas. 1.25us)
498 | // This lib on Photon (meas. 1.25us)
499 | asm volatile(
500 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
501 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
502 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
503 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
504 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
505 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
506 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
507 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
508 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
509 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
510 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
511 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
512 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
513 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
514 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
515 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
516 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
517 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
518 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
519 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
520 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
521 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
522 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
523 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
524 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
525 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
526 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
527 | "nop" "\n\t" "nop" "\n\t"
528 | #endif
529 | ::: "r0", "cc", "memory");
530 | // WS2811 spec 1.30us LOW
531 | // Adafruit on Arduino (meas. 1.25us)
532 | // This lib on Spark Core (meas. 1.24us)
533 | // This lib on Photon (meas. 1.24us)
534 | pinSet(pin, LOW); // LOW
535 | asm volatile(
536 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
537 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
538 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
539 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
540 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
541 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
542 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
543 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
544 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
545 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
546 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
547 | "nop" "\n\t" "nop" "\n\t"
548 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
549 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
550 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
551 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
552 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
553 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
554 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
555 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
556 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
557 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
558 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
559 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
560 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
561 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
562 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
563 | #endif
564 | ::: "r0", "cc", "memory");
565 | }
566 | else { // else masked bit is low
567 | // WS2811 spec 500ns HIGH
568 | // Adafruit on Arduino (meas. 500ns)
569 | // This lib on Spark Core (meas. 500ns)
570 | // This lib on Photon (meas. 500ns)
571 | asm volatile(
572 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
573 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
574 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
575 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
576 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
577 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
578 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
579 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
580 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
581 | #endif
582 | "nop" "\n\t" "nop" "\n\t"
583 | ::: "r0", "cc", "memory");
584 | // WS2811 spec 2.000us LOW
585 | // Adafruit on Arduino (meas. 2.000us)
586 | // This lib on Spark Core (meas. 2.000us)
587 | // This lib on Photon (meas. 2.000us)
588 | pinSet(pin, LOW); // LOW
589 | asm volatile(
590 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
591 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
592 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
593 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
594 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
595 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
596 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
597 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
598 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
599 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
600 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
601 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
602 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
603 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
604 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
605 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
606 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
607 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
608 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
609 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
610 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
611 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
612 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
613 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
614 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
615 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
616 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
617 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
618 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
619 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
620 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
621 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
622 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
623 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
624 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
625 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
626 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
627 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
628 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
629 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
630 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
631 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
632 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
633 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
634 | #endif
635 | ::: "r0", "cc", "memory");
636 | }
637 | mask >>= 1;
638 | } while ( ++j < 24 ); // ... pixel done
639 | } // end while(i) ... no more pixels
640 | }
641 | else if(type == TM1803) { // TM1803 (Radio Shack Tri-Color Strip), 400 KHz bitstream
642 | while(i) { // While bytes left... (3 bytes = 1 pixel)
643 | mask = 0x800000; // reset the mask
644 | i = i-3; // decrement bytes remaining
645 | r = *ptr++; // Next red byte value
646 | g = *ptr++; // Next blue byte value
647 | b = *ptr++; // Next green byte value
648 | c = ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; // Pack the next 3 bytes to keep timing tight
649 | j = 0; // reset the 24-bit counter
650 | do {
651 | pinSet(pin, HIGH); // HIGH
652 | if (c & mask) { // if masked bit is high
653 | // TM1803 spec 1.36us HIGH
654 | // Pololu on Arduino (meas. 1.31us)
655 | // This lib on Spark Core (meas. 1.36us)
656 | // This lib on Photon (meas. 1.36us)
657 | asm volatile(
658 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
659 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
660 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
661 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
662 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
663 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
664 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
665 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
666 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
667 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
668 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
669 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
670 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
671 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
672 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
673 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
674 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
675 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
676 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
677 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
678 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
679 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
680 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
681 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
682 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
683 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
684 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
685 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
686 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
687 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
688 | "nop" "\n\t"
689 | #endif
690 | ::: "r0", "cc", "memory");
691 | // TM1803 spec 680ns LOW
692 | // Pololu on Arduino (meas. 1.024us)
693 | // This lib on Spark Core (meas. 680ns)
694 | // This lib on Photon (meas. 684ns)
695 | pinSet(pin, LOW); // LOW
696 | asm volatile(
697 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
698 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
699 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
700 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
701 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
702 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
703 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
704 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
705 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
706 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
707 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
708 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
709 | "nop" "\n\t"
710 | #endif
711 | ::: "r0", "cc", "memory");
712 | }
713 | else { // else masked bit is low
714 | // TM1803 spec 680ns HIGH
715 | // Pololu on Arduino (meas. 374ns)
716 | // This lib on Spark Core (meas. 680ns)
717 | // This lib on Photon (meas. 684ns)
718 | asm volatile(
719 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
720 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
721 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
722 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
723 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
724 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
725 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
726 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
727 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
728 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
729 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
730 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
731 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
732 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
733 | "nop" "\n\t"
734 | #endif
735 | ::: "r0", "cc", "memory");
736 | // TM1803 spec 1.36us LOW
737 | // Pololu on Arduino (meas. 2.00us)
738 | // This lib on Spark Core (meas. 1.36us)
739 | // This lib on Photon (meas. 1.36us)
740 | pinSet(pin, LOW); // LOW
741 | asm volatile(
742 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
743 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
744 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
745 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
746 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
747 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
748 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
749 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
750 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
751 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
752 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
753 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
754 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
755 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
756 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
757 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
758 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
759 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
760 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
761 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
762 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
763 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
764 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
765 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
766 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
767 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
768 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
769 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
770 | "nop" "\n\t"
771 | #endif
772 | ::: "r0", "cc", "memory");
773 | }
774 | mask >>= 1;
775 | } while ( ++j < 24 ); // ... pixel done
776 | } // end while(i) ... no more pixels
777 | }
778 | else { // must be only other option TM1829, 800 KHz bitstream
779 | while(i) { // While bytes left... (3 bytes = 1 pixel)
780 | mask = 0x800000; // reset the mask
781 | i = i-3; // decrement bytes remaining
782 | r = *ptr++; // Next red byte value
783 | b = *ptr++; // Next blue byte value
784 | g = *ptr++; // Next green byte value
785 | c = ((uint32_t)r << 16) | ((uint32_t)b << 8) | g; // Pack the next 3 bytes to keep timing tight
786 | j = 0; // reset the 24-bit counter
787 | pinSet(pin, LOW); // LOW
788 | for( ;; ) { // ... pixel done
789 | if (c & mask) { // if masked bit is high
790 | // TM1829 spec 800ns LOW
791 | // This lib on Spark Core (meas. 806ns)
792 | // This lib on Photon (meas. 792ns)
793 | mask >>= 1; // Do this task during the long delay of this bit
794 | asm volatile(
795 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
796 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
797 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
798 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
799 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
800 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
801 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
802 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
803 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
804 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
805 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
806 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
807 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
808 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
809 | "nop" "\n\t" "nop" "\n\t"
810 | #endif
811 | ::: "r0", "cc", "memory");
812 | j++;
813 | // TM1829 spec 300ns HIGH
814 | // This lib on Spark Core (meas. 305ns)
815 | // This lib on Photon (meas. 300ns)
816 | pinSet(pin, HIGH); // HIGH
817 | asm volatile(
818 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
819 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
820 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
821 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
822 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
823 | "nop" "\n\t"
824 | #endif
825 | ::: "r0", "cc", "memory");
826 | if(j==24) break;
827 | pinSet(pin, LOW); // LOW
828 | }
829 | else { // else masked bit is low
830 | // TM1829 spec 300ns LOW
831 | // This lib on Spark Core (meas. 390ns)
832 | // This lib on Photon (meas. 300ns)
833 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
834 | asm volatile(
835 | "mov r0, r0" "\n\t"
836 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
837 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
838 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
839 | "nop" "\n\t"
840 | ::: "r0", "cc", "memory");
841 | #endif
842 | // TM1829 spec 800ns HIGH
843 | // This lib on Spark Core (meas. 792ns)
844 | // This lib on Photon (meas. 800ns)
845 | pinSet(pin, HIGH); // HIGH
846 | j++;
847 | mask >>= 1; // Do this task during the long delay of this bit
848 | asm volatile(
849 | "mov r0, r0" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
850 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
851 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
852 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
853 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
854 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
855 | #if (PLATFORM_ID == 6) || (PLATFORM_ID == 8) || (PLATFORM_ID == 10) || (PLATFORM_ID == 88) // Photon (6), P1 (8), Electron (10) or Redbear Duo (88)
856 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
857 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
858 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
859 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
860 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
861 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
862 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
863 | "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t" "nop" "\n\t"
864 | #endif
865 | ::: "r0", "cc", "memory");
866 | if(j==24) break;
867 | pinSet(pin, LOW); // LOW
868 | }
869 | }
870 | } // end while(i) ... no more pixels
871 | }
872 |
873 | __enable_irq();
874 |
875 | #elif (PLATFORM_ID == 32)
876 | if (getType() != WS2812B) { // WS2812 WS2812B and WS2813 supported for P2
877 | Log.error("Pixel type not supported!");
878 | return;
879 | }
880 |
881 | constexpr uint8_t PIX_HI = 0b110;
882 | constexpr uint8_t PIX_LO = 0b100;
883 |
884 | uint16_t resetOff = 120; // 300us / (1/3125000Mhz) / 8bits_per_byte
885 | switch (type) {
886 | case WS2812B: { // WS2812, WS2812B & WS2813 = 300us reset pulse
887 | resetOff = 120;
888 | } break;
889 | case WS2812B_FAST: // WS2812B_FAST = 50us reset pulse
890 | default: { // default = 50us reset pulse
891 | resetOff = 20;
892 | } break;
893 | }
894 |
895 | constexpr uint8_t numBitsPerBit = 3; // How many SPI bits represent one neopixel bit
896 | uint32_t spiArraySize = (numBytes * numBitsPerBit) + resetOff + resetOff;
897 | uint8_t* spiArray = NULL;
898 | spiArray = (uint8_t*) malloc(spiArraySize);
899 |
900 | if (spiArray == NULL) {
901 | Log.error("Not enough memory available!");
902 | return;
903 | }
904 |
905 | memset(spiArray, 0, spiArraySize);
906 | // expand pixel data and pack into spi buffer
907 | for (int x = 0; x < numPixels(); x++) {
908 | for (int s = 0; s < 3; s++) {
909 | spiArray[(x*9)+(s*3)+0+resetOff] = ((0x80 & pixels[(x*3)+s])?(PIX_HI << 5):(PIX_LO << 5)) + ((0x40 & pixels[(x*3)+s])?(PIX_HI << 2):(PIX_LO << 2)) + ((0x20 & pixels[(x*3)+s])?(0b11):(0b10));
910 | spiArray[(x*9)+(s*3)+1+resetOff] = 0 /* bit 7 always 0 */ + ((0x10 & pixels[(x*3)+s])?(PIX_HI << 4):(PIX_LO << 4)) + ((0x08 & pixels[(x*3)+s])?(PIX_HI << 1):(PIX_LO << 1)) + 1 /* bit 0 always 1 */;
911 | spiArray[(x*9)+(s*3)+2+resetOff] = ((0x04 & pixels[(x*3)+s])?(0b10 << 6):(0b00 << 6)) + ((0x02 & pixels[(x*3)+s])?(PIX_HI << 3):(PIX_LO << 3)) + ((0x01 & pixels[(x*3)+s])?(PIX_HI):(PIX_LO));
912 | }
913 | }
914 |
915 | spi_->beginTransaction();
916 | spi_->transfer(spiArray, nullptr, spiArraySize, nullptr);
917 | spi_->endTransaction();
918 |
919 | free(spiArray);
920 |
921 | #elif HAL_PLATFORM_NRF52840 // Argon, Boron, Xenon, B SoM, B5 SoM, E SoM X, Tracker
922 | // [[[Begin of the Neopixel NRF52 EasyDMA implementation
923 | // by the Hackerspace San Salvador]]]
924 | // This technique uses the PWM peripheral on the NRF52. The PWM uses the
925 | // EasyDMA feature included on the chip. This technique loads the duty
926 | // cycle configuration for each cycle when the PWM is enabled. For this
927 | // to work we need to store a 16 bit configuration for each bit of the
928 | // RGB(W) values in the pixel buffer.
929 | // Comparator values for the PWM were hand picked and are guaranteed to
930 | // be 100% organic to preserve freshness and high accuracy. Current
931 | // parameters are:
932 | // * PWM Clock: 16Mhz
933 | // * Minimum step time: 62.5ns
934 | // * Time for zero in high (T0H): 0.31ms
935 | // * Time for one in high (T1H): 0.75ms
936 | // * Cycle time: 1.25us
937 | // * Frequency: 800Khz
938 | // For 400Khz we just double the calculated times.
939 | // ---------- BEGIN Constants for the EasyDMA implementation -----------
940 | // The PWM starts the duty cycle in LOW. To start with HIGH we
941 | // need to set the 15th bit on each register.
942 |
943 | // WS2812 (rev A) timing is 0.35 and 0.7us
944 | //#define MAGIC_T0H 5UL | (0x8000) // 0.3125us
945 | //#define MAGIC_T1H 12UL | (0x8000) // 0.75us
946 |
947 | // WS2812B (rev B) timing is 0.4 and 0.8 us
948 | #define MAGIC_T0H 6UL | (0x8000) // 0.375us
949 | #define MAGIC_T1H 13UL | (0x8000) // 0.8125us
950 |
951 | // WS2811 (400 khz) timing is 0.5 and 1.2
952 | #define MAGIC_T0H_400KHz 8UL | (0x8000) // 0.5us
953 | #define MAGIC_T1H_400KHz 19UL | (0x8000) // 1.1875us
954 |
955 | // For 400Khz, we double value of CTOPVAL
956 | #define CTOPVAL 20UL // 1.25us
957 | #define CTOPVAL_400KHz 40UL // 2.5us
958 |
959 | // ---------- END Constants for the EasyDMA implementation -------------
960 | //
961 | // If there is no device available an alternative cycle-counter
962 | // implementation is tried.
963 | // The nRF52832 runs with a fixed clock of 64Mhz. The alternative
964 | // implementation is the same as the one used for the Teensy 3.0/1/2 but
965 | // with the Nordic SDK HAL & registers syntax.
966 | // The number of cycles was hand picked and is guaranteed to be 100%
967 | // organic to preserve freshness and high accuracy.
968 | // ---------- BEGIN Constants for cycle counter implementation ---------
969 | #define CYCLES_800_T0H 18 // ~0.36 uS
970 | #define CYCLES_800_T1H 41 // ~0.76 uS
971 | #define CYCLES_800 71 // ~1.25 uS
972 |
973 | #define CYCLES_400_T0H 26 // ~0.50 uS
974 | #define CYCLES_400_T1H 70 // ~1.26 uS
975 | #define CYCLES_400 156 // ~2.50 uS
976 | // ---------- END of Constants for cycle counter implementation --------
977 |
978 | // To support both the SoftDevice + Neopixels we use the EasyDMA
979 | // feature from the NRF25. However this technique implies to
980 | // generate a pattern and store it on the memory. The actual
981 | // memory used in bytes corresponds to the following formula:
982 | // totalMem = numBytes*8*2+(2*2)
983 | // The two additional bytes at the end are needed to reset the
984 | // sequence.
985 | //
986 | // If there is not enough memory, we will fall back to cycle counter
987 | // using DWT
988 | uint32_t pattern_size = numBytes*8*sizeof(uint16_t)+2*sizeof(uint16_t);
989 | uint16_t* pixels_pattern = NULL;
990 |
991 | NRF_PWM_Type* pwm = NULL;
992 |
993 | // Try to find a free PWM device, which is not enabled
994 | // and has no connected pins
995 | NRF_PWM_Type* PWM[3] = {NRF_PWM0, NRF_PWM1, NRF_PWM2};
996 | for(int device = 0; device<3; device++) {
997 | if( (PWM[device]->ENABLE == 0) &&
998 | (PWM[device]->PSEL.OUT[0] & PWM_PSEL_OUT_CONNECT_Msk) &&
999 | (PWM[device]->PSEL.OUT[1] & PWM_PSEL_OUT_CONNECT_Msk) &&
1000 | (PWM[device]->PSEL.OUT[2] & PWM_PSEL_OUT_CONNECT_Msk) &&
1001 | (PWM[device]->PSEL.OUT[3] & PWM_PSEL_OUT_CONNECT_Msk)
1002 | ) {
1003 | pwm = PWM[device];
1004 | break;
1005 | }
1006 | }
1007 |
1008 | // only malloc if there is PWM device available
1009 | if ( pwm != NULL ) {
1010 | #ifdef ARDUINO_FEATHER52 // use thread-safe malloc
1011 | pixels_pattern = (uint16_t *) rtos_malloc(pattern_size);
1012 | #else
1013 | pixels_pattern = (uint16_t *) malloc(pattern_size);
1014 | #endif
1015 | }
1016 |
1017 | // Use the identified device to choose the implementation
1018 | // If a PWM device is available use DMA
1019 | if( (pixels_pattern != NULL) && (pwm != NULL) ) {
1020 | uint16_t pos = 0; // bit position
1021 |
1022 | for(uint16_t n=0; n0; mask >>= 1, i++) {
1026 | #ifdef NEO_KHZ400
1027 | if( !is800KHz ) {
1028 | pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H_400KHz : MAGIC_T0H_400KHz;
1029 | }else
1030 | #endif
1031 | {
1032 | pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H : MAGIC_T0H;
1033 | }
1034 |
1035 | pos++;
1036 | }
1037 | }
1038 |
1039 | // Zero padding to indicate the end of que sequence
1040 | pixels_pattern[++pos] = 0 | (0x8000); // Seq end
1041 | pixels_pattern[++pos] = 0 | (0x8000); // Seq end
1042 |
1043 | // Set the wave mode to count UP
1044 | pwm->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
1045 |
1046 | // Set the PWM to use the 16MHz clock
1047 | pwm->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos);
1048 |
1049 | // Setting of the maximum count
1050 | // but keeping it on 16Mhz allows for more granularity just
1051 | // in case someone wants to do more fine-tuning of the timing.
1052 | #ifdef NEO_KHZ400
1053 | if( !is800KHz ) {
1054 | pwm->COUNTERTOP = (CTOPVAL_400KHz << PWM_COUNTERTOP_COUNTERTOP_Pos);
1055 | }else
1056 | #endif
1057 | {
1058 | pwm->COUNTERTOP = (CTOPVAL << PWM_COUNTERTOP_COUNTERTOP_Pos);
1059 | }
1060 |
1061 | // Disable loops, we want the sequence to repeat only once
1062 | pwm->LOOP = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
1063 |
1064 | // On the "Common" setting the PWM uses the same pattern for the
1065 | // for supported sequences. The pattern is stored on half-word
1066 | // of 16bits
1067 | pwm->DECODER = (PWM_DECODER_LOAD_Common << PWM_DECODER_LOAD_Pos) |
1068 | (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
1069 |
1070 | // Pointer to the memory storing the patter
1071 | pwm->SEQ[0].PTR = (uint32_t)(pixels_pattern) << PWM_SEQ_PTR_PTR_Pos;
1072 |
1073 | // Calculation of the number of steps loaded from memory.
1074 | pwm->SEQ[0].CNT = (pattern_size/sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos;
1075 |
1076 | // The following settings are ignored with the current config.
1077 | pwm->SEQ[0].REFRESH = 0;
1078 | pwm->SEQ[0].ENDDELAY = 0;
1079 |
1080 | // The Neopixel implementation is a blocking algorithm. DMA
1081 | // allows for non-blocking operation. To "simulate" a blocking
1082 | // operation we enable the interruption for the end of sequence
1083 | // and block the execution thread until the event flag is set by
1084 | // the peripheral.
1085 | // pwm->INTEN |= (PWM_INTEN_SEQEND0_Enabled<PSEL.OUT[0] = NRF_GPIO_PIN_MAP(PIN_MAP2[pin].gpio_port, PIN_MAP2[pin].gpio_pin);
1089 |
1090 | // Enable the PWM
1091 | pwm->ENABLE = 1;
1092 |
1093 | // After all of this and many hours of reading the documentation
1094 | // we are ready to start the sequence...
1095 | pwm->EVENTS_SEQEND[0] = 0;
1096 | pwm->TASKS_SEQSTART[0] = 1;
1097 |
1098 | // But we have to wait for the flag to be set.
1099 | while(!pwm->EVENTS_SEQEND[0])
1100 | {
1101 | #ifdef ARDUINO_FEATHER52
1102 | yield();
1103 | #endif
1104 | }
1105 |
1106 | // Before leave we clear the flag for the event.
1107 | pwm->EVENTS_SEQEND[0] = 0;
1108 |
1109 | // We need to disable the device and disconnect
1110 | // all the outputs before leave or the device will not
1111 | // be selected on the next call.
1112 | // TODO: Check if disabling the device causes performance issues.
1113 | pwm->ENABLE = 0;
1114 |
1115 | pwm->PSEL.OUT[0] = 0xFFFFFFFFUL;
1116 |
1117 | #ifdef ARDUINO_FEATHER52 // use thread-safe free
1118 | rtos_free(pixels_pattern);
1119 | #else
1120 | free(pixels_pattern);
1121 | #endif
1122 | }// End of DMA implementation
1123 | // ---------------------------------------------------------------------
1124 | else{
1125 | // Fall back to DWT
1126 | #ifdef ARDUINO_FEATHER52
1127 | // Bluefruit Feather 52 uses freeRTOS
1128 | // Critical Section is used since it does not block SoftDevice execution
1129 | taskENTER_CRITICAL();
1130 | #elif defined(NRF52_DISABLE_INT)
1131 | // If you are using the Bluetooth SoftDevice we advise you to not disable
1132 | // the interrupts. Disabling the interrupts even for short periods of time
1133 | // causes the SoftDevice to stop working.
1134 | // Disable the interrupts only in cases where you need high performance for
1135 | // the LEDs and if you are not using the EasyDMA feature.
1136 | __disable_irq();
1137 | #endif
1138 |
1139 | uint32_t pinMask = 1UL << NRF_GPIO_PIN_MAP(PIN_MAP2[pin].gpio_port, PIN_MAP2[pin].gpio_pin);
1140 |
1141 |
1142 | uint32_t CYCLES_X00 = CYCLES_800;
1143 | uint32_t CYCLES_X00_T1H = CYCLES_800_T1H;
1144 | uint32_t CYCLES_X00_T0H = CYCLES_800_T0H;
1145 |
1146 | #ifdef NEO_KHZ400
1147 | if( !is800KHz )
1148 | {
1149 | CYCLES_X00 = CYCLES_400;
1150 | CYCLES_X00_T1H = CYCLES_400_T1H;
1151 | CYCLES_X00_T0H = CYCLES_400_T0H;
1152 | }
1153 | #endif
1154 |
1155 | // Enable DWT in debug core
1156 | CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
1157 | DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
1158 |
1159 | // Tries to re-send the frame if is interrupted by the SoftDevice.
1160 | while(1) {
1161 | uint8_t *p = pixels;
1162 |
1163 | uint32_t cycStart = DWT->CYCCNT;
1164 | uint32_t cyc = 0;
1165 |
1166 | for(uint16_t n=0; n>= 1) {
1170 | while(DWT->CYCCNT - cyc < CYCLES_X00);
1171 | cyc = DWT->CYCCNT;
1172 |
1173 | NRF_GPIO->OUTSET |= pinMask;
1174 |
1175 | if(pix & mask) {
1176 | while(DWT->CYCCNT - cyc < CYCLES_X00_T1H);
1177 | } else {
1178 | while(DWT->CYCCNT - cyc < CYCLES_X00_T0H);
1179 | }
1180 |
1181 | NRF_GPIO->OUTCLR |= pinMask;
1182 | }
1183 | }
1184 | while(DWT->CYCCNT - cyc < CYCLES_X00);
1185 |
1186 |
1187 | // If total time longer than 25%, resend the whole data.
1188 | // Since we are likely to be interrupted by SoftDevice
1189 | if ( (DWT->CYCCNT - cycStart) < ( 8*numBytes*((CYCLES_X00*5)/4) ) ) {
1190 | break;
1191 | }
1192 |
1193 | // re-send need 300us delay
1194 | delayMicroseconds(300);
1195 | }
1196 |
1197 | // Enable interrupts again
1198 | #ifdef ARDUINO_FEATHER52
1199 | taskEXIT_CRITICAL();
1200 | #elif defined(NRF52_DISABLE_INT)
1201 | __enable_irq();
1202 | #endif
1203 | }
1204 | // END of NRF52 implementation
1205 |
1206 |
1207 | #endif
1208 | endTime = micros(); // Save EOD time for latch on next call
1209 | }
1210 |
1211 | // Set pixel color from separate R,G,B components:
1212 | void Adafruit_NeoPixel::setPixelColor(
1213 | uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
1214 | if(n < numLEDs) {
1215 | if(brightness) { // See notes in setBrightness()
1216 | r = (r * brightness) >> 8;
1217 | g = (g * brightness) >> 8;
1218 | b = (b * brightness) >> 8;
1219 | }
1220 | uint8_t *p = &pixels[n * 3];
1221 | switch(type) {
1222 | case WS2812B: // WS2812, WS2812B & WS2813 is GRB order.
1223 | case WS2812B_FAST:
1224 | case WS2812B2:
1225 | case WS2812B2_FAST: {
1226 | *p++ = g;
1227 | *p++ = r;
1228 | *p = b;
1229 | } break;
1230 | case TM1829: { // TM1829 is special RBG order
1231 | if(r == 255) r = 254; // 255 on RED channel causes display to be in a special mode.
1232 | *p++ = r;
1233 | *p++ = b;
1234 | *p = g;
1235 | } break;
1236 | case WS2811: // WS2811 is RGB order
1237 | case TM1803: // TM1803 is RGB order
1238 | default: { // default is RGB order
1239 | *p++ = r;
1240 | *p++ = g;
1241 | *p = b;
1242 | } break;
1243 | }
1244 | }
1245 | }
1246 |
1247 | // Set pixel color from separate R,G,B,W components:
1248 | void Adafruit_NeoPixel::setPixelColor(
1249 | uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
1250 | if(n < numLEDs) {
1251 | if(brightness) { // See notes in setBrightness()
1252 | r = (r * brightness) >> 8;
1253 | g = (g * brightness) >> 8;
1254 | b = (b * brightness) >> 8;
1255 | w = (w * brightness) >> 8;
1256 | }
1257 | uint8_t *p = &pixels[n * (type==SK6812RGBW?4:3)];
1258 | switch(type) {
1259 | case WS2812B: // WS2812, WS2812B & WS2813 is GRB order.
1260 | case WS2812B_FAST:
1261 | case WS2812B2:
1262 | case WS2812B2_FAST: {
1263 | *p++ = g;
1264 | *p++ = r;
1265 | *p = b;
1266 | } break;
1267 | case TM1829: { // TM1829 is special RBG order
1268 | if(r == 255) r = 254; // 255 on RED channel causes display to be in a special mode.
1269 | *p++ = r;
1270 | *p++ = b;
1271 | *p = g;
1272 | } break;
1273 | case SK6812RGBW: { // SK6812RGBW is RGBW order
1274 | *p++ = r;
1275 | *p++ = g;
1276 | *p++ = b;
1277 | *p = w;
1278 | } break;
1279 | case WS2811: // WS2811 is RGB order
1280 | case TM1803: // TM1803 is RGB order
1281 | default: { // default is RGB order
1282 | *p++ = r;
1283 | *p++ = g;
1284 | *p = b;
1285 | } break;
1286 | }
1287 | }
1288 | }
1289 |
1290 | // Set pixel color from 'packed' 32-bit RGB color:
1291 | // If RGB+W color, order of bytes is WRGB in packed 32-bit form
1292 | void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) {
1293 | if(n < numLEDs) {
1294 | uint8_t
1295 | r = (uint8_t)(c >> 16),
1296 | g = (uint8_t)(c >> 8),
1297 | b = (uint8_t)c;
1298 | if(brightness) { // See notes in setBrightness()
1299 | r = (r * brightness) >> 8;
1300 | g = (g * brightness) >> 8;
1301 | b = (b * brightness) >> 8;
1302 | }
1303 | uint8_t *p = &pixels[n * (type==SK6812RGBW?4:3)];
1304 | switch(type) {
1305 | case WS2812B: // WS2812, WS2812B & WS2813 is GRB order.
1306 | case WS2812B_FAST:
1307 | case WS2812B2:
1308 | case WS2812B2_FAST: {
1309 | *p++ = g;
1310 | *p++ = r;
1311 | *p = b;
1312 | } break;
1313 | case TM1829: { // TM1829 is special RBG order
1314 | if(r == 255) r = 254; // 255 on RED channel causes display to be in a special mode.
1315 | *p++ = r;
1316 | *p++ = b;
1317 | *p = g;
1318 | } break;
1319 | case SK6812RGBW: { // SK6812RGBW is RGBW order
1320 | uint8_t w = (uint8_t)(c >> 24);
1321 | *p++ = r;
1322 | *p++ = g;
1323 | *p++ = b;
1324 | *p = brightness ? ((w * brightness) >> 8) : w;
1325 | } break;
1326 | case WS2811: // WS2811 is RGB order
1327 | case TM1803: // TM1803 is RGB order
1328 | default: { // default is RGB order
1329 | *p++ = r;
1330 | *p++ = g;
1331 | *p = b;
1332 | } break;
1333 | }
1334 | }
1335 | }
1336 |
1337 | void Adafruit_NeoPixel::setColor(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue) {
1338 | return setPixelColor(aLedNumber, (uint8_t) aRed, (uint8_t) aGreen, (uint8_t) aBlue);
1339 | }
1340 |
1341 | void Adafruit_NeoPixel::setColor(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aWhite) {
1342 | return setPixelColor(aLedNumber, (uint8_t) aRed, (uint8_t) aGreen, (uint8_t) aBlue, (uint8_t) aWhite);
1343 | }
1344 |
1345 | void Adafruit_NeoPixel::setColorScaled(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aScaling) {
1346 | // scale RGB with a common brightness parameter
1347 | setColor(aLedNumber, (aRed*aScaling)>>8, (aGreen*aScaling)>>8, (aBlue*aScaling)>>8);
1348 | }
1349 |
1350 | void Adafruit_NeoPixel::setColorScaled(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aWhite, byte aScaling) {
1351 | // scale RGB with a common brightness parameter
1352 | setColor(aLedNumber, (aRed*aScaling)>>8, (aGreen*aScaling)>>8, (aBlue*aScaling)>>8, (aWhite*aScaling)>>8);
1353 | }
1354 |
1355 | void Adafruit_NeoPixel::setColorDimmed(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aBrightness) {
1356 | setColorScaled(aLedNumber, aRed, aGreen, aBlue, brightnessToPWM(aBrightness));
1357 | }
1358 |
1359 | void Adafruit_NeoPixel::setColorDimmed(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aWhite, byte aBrightness) {
1360 | setColorScaled(aLedNumber, aRed, aGreen, aBlue, aWhite, brightnessToPWM(aBrightness));
1361 | }
1362 |
1363 | byte Adafruit_NeoPixel::brightnessToPWM(byte aBrightness) {
1364 | static const byte pwmLevels[16] = { 0, 1, 2, 3, 4, 6, 8, 12, 23, 36, 48, 70, 95, 135, 190, 255 };
1365 | return pwmLevels[aBrightness>>4];
1366 | }
1367 |
1368 | // Convert separate R,G,B into packed 32-bit RGB color.
1369 | // Packed format is always RGB, regardless of LED strand color order.
1370 | uint32_t Adafruit_NeoPixel::Color(uint8_t r, uint8_t g, uint8_t b) {
1371 | return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
1372 | }
1373 |
1374 | // Convert separate R,G,B,W into packed 32-bit WRGB color.
1375 | // Packed format is always WRGB, regardless of LED strand color order.
1376 | uint32_t Adafruit_NeoPixel::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
1377 | return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
1378 | }
1379 |
1380 | // Query color from previously-set pixel (returns packed 32-bit RGB value)
1381 | uint32_t Adafruit_NeoPixel::getPixelColor(uint16_t n) const {
1382 | if(n >= numLEDs) {
1383 | // Out of bounds, return no color.
1384 | return 0;
1385 | }
1386 |
1387 | uint8_t *p = &pixels[n * (type==SK6812RGBW?4:3)];
1388 | uint32_t c;
1389 |
1390 | switch(type) {
1391 | case WS2812B: // WS2812, WS2812B & WS2813 is GRB order.
1392 | case WS2812B_FAST:
1393 | case WS2812B2:
1394 | case WS2812B2_FAST: {
1395 | c = ((uint32_t)p[1] << 16) | ((uint32_t)p[0] << 8) | (uint32_t)p[2];
1396 | } break;
1397 | case TM1829: { // TM1829 is special RBG order
1398 | c = ((uint32_t)p[0] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[1];
1399 | } break;
1400 | case SK6812RGBW: { // SK6812RGBW is RGBW order, but returns packed WRGB color
1401 | c = ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
1402 | } break;
1403 | case WS2811: // WS2811 is RGB order
1404 | case TM1803: // TM1803 is RGB order
1405 | default: { // default is RGB order
1406 | c = ((uint32_t)p[0] << 16) | ((uint32_t)p[1] << 8) | (uint32_t)p[2];
1407 | } break;
1408 | }
1409 |
1410 | // Adjust this back up to the true color, as setting a pixel color will
1411 | // scale it back down again.
1412 | if(brightness) { // See notes in setBrightness()
1413 | //Cast the color to a byte array
1414 | uint8_t * c_ptr =reinterpret_cast(&c);
1415 | if (type == SK6812RGBW) {
1416 | c_ptr[3] = (c_ptr[3] << 8)/brightness;
1417 | }
1418 | c_ptr[0] = (c_ptr[0] << 8)/brightness;
1419 | c_ptr[1] = (c_ptr[1] << 8)/brightness;
1420 | c_ptr[2] = (c_ptr[2] << 8)/brightness;
1421 | }
1422 | return c; // Pixel # is out of bounds
1423 | }
1424 |
1425 | uint8_t *Adafruit_NeoPixel::getPixels(void) const {
1426 | return pixels;
1427 | }
1428 |
1429 | uint16_t Adafruit_NeoPixel::numPixels(void) const {
1430 | return numLEDs;
1431 | }
1432 |
1433 | uint16_t Adafruit_NeoPixel::getNumLeds(void) const {
1434 | return numPixels();
1435 | }
1436 |
1437 | // Adjust output brightness; 0=darkest (off), 255=brightest. This does
1438 | // NOT immediately affect what's currently displayed on the LEDs. The
1439 | // next call to show() will refresh the LEDs at this level. However,
1440 | // this process is potentially "lossy," especially when increasing
1441 | // brightness. The tight timing in the WS2811/WS2812 code means there
1442 | // aren't enough free cycles to perform this scaling on the fly as data
1443 | // is issued. So we make a pass through the existing color data in RAM
1444 | // and scale it (subsequent graphics commands also work at this
1445 | // brightness level). If there's a significant step up in brightness,
1446 | // the limited number of steps (quantization) in the old data will be
1447 | // quite visible in the re-scaled version. For a non-destructive
1448 | // change, you'll need to re-render the full strip data. C'est la vie.
1449 | void Adafruit_NeoPixel::setBrightness(uint8_t b) {
1450 | // Stored brightness value is different than what's passed.
1451 | // This simplifies the actual scaling math later, allowing a fast
1452 | // 8x8-bit multiply and taking the MSB. 'brightness' is a uint8_t,
1453 | // adding 1 here may (intentionally) roll over...so 0 = max brightness
1454 | // (color values are interpreted literally; no scaling), 1 = min
1455 | // brightness (off), 255 = just below max brightness.
1456 | uint8_t newBrightness = b + 1;
1457 | if(newBrightness != brightness) { // Compare against prior value
1458 | // Brightness has changed -- re-scale existing data in RAM
1459 | uint8_t c,
1460 | *ptr = pixels,
1461 | oldBrightness = brightness - 1; // De-wrap old brightness value
1462 | uint16_t scale;
1463 | if(oldBrightness == 0) scale = 0; // Avoid /0
1464 | else if(b == 255) scale = 65535 / oldBrightness;
1465 | else scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
1466 | for(uint16_t i=0; i> 8;
1469 | }
1470 | brightness = newBrightness;
1471 | }
1472 | }
1473 |
1474 | //Return the brightness value
1475 | uint8_t Adafruit_NeoPixel::getBrightness(void) const {
1476 | return brightness - 1;
1477 | }
1478 |
1479 | void Adafruit_NeoPixel::clear(void) {
1480 | memset(pixels, 0, numBytes);
1481 | }
1482 |
--------------------------------------------------------------------------------
/src/neopixel.h:
--------------------------------------------------------------------------------
1 | /*-------------------------------------------------------------------------
2 | Particle Core, Particle Photon, P1, Electron, Argon, Boron, Xenon, B SoM, B5 SoM, E SoM X, P2, Photon 2, Tracker and
3 | RedBear Duo library to control WS2811/WS2812/WS2813 based RGB LED
4 | devices such as Adafruit NeoPixel strips.
5 |
6 | Supports:
7 | - 800 KHz WS2812, WS2812B, WS2813 and 400kHz bitstream and WS2811
8 | - 800 KHz bitstream SK6812RGBW (NeoPixel RGBW pixel strips)
9 | (use 'SK6812RGBW' as PIXEL_TYPE)
10 |
11 | Also supports:
12 | - Radio Shack Tri-Color Strip with TM1803 controller 400kHz bitstream.
13 | - TM1829 pixels
14 |
15 | PLEASE NOTE that the NeoPixels require 5V level inputs
16 | and the supported microcontrollers only have 3.3V level outputs. Level
17 | shifting is necessary, but will require a fast device such as one of
18 | the following:
19 |
20 | [SN74HCT125N]
21 | http://www.digikey.com/product-detail/en/SN74HCT125N/296-8386-5-ND/376860
22 |
23 | [SN74HCT245N]
24 | http://www.digikey.com/product-detail/en/SN74HCT245N/296-1612-5-ND/277258
25 |
26 | Written by Phil Burgess / Paint Your Dragon for Adafruit Industries.
27 | Modified to work with Particle devices by Technobly.
28 | Contributions by PJRC and other members of the open source community.
29 |
30 | Adafruit invests time and resources providing this open source code,
31 | please support Adafruit and open-source hardware by purchasing products
32 | from Adafruit!
33 | --------------------------------------------------------------------*/
34 |
35 | /* ======================= neopixel.h ======================= */
36 | /*--------------------------------------------------------------------
37 | This file is part of the Adafruit NeoPixel library.
38 |
39 | NeoPixel is free software: you can redistribute it and/or modify
40 | it under the terms of the GNU Lesser General Public License as
41 | published by the Free Software Foundation, either version 3 of
42 | the License, or (at your option) any later version.
43 |
44 | NeoPixel is distributed in the hope that it will be useful,
45 | but WITHOUT ANY WARRANTY; without even the implied warranty of
46 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 | GNU Lesser General Public License for more details.
48 |
49 | You should have received a copy of the GNU Lesser General Public
50 | License along with NeoPixel. If not, see
51 | .
52 | --------------------------------------------------------------------*/
53 |
54 | #ifndef PARTICLE_NEOPIXEL_H
55 | #define PARTICLE_NEOPIXEL_H
56 |
57 | #include "Particle.h"
58 |
59 | // 'type' flags for LED pixels (third parameter to constructor):
60 | #define WS2811 0x00 // 400 KHz datastream (NeoPixel)
61 | #define WS2812 0x02 // 800 KHz datastream (NeoPixel)
62 | #define WS2812B 0x02 // 800 KHz datastream (NeoPixel)
63 | #define WS2813 0x02 // 800 KHz datastream (NeoPixel)
64 | #define TM1803 0x03 // 400 KHz datastream (Radio Shack Tri-Color Strip)
65 | #define TM1829 0x04 // 800 KHz datastream ()
66 | #define WS2812B2 0x05 // 800 KHz datastream (NeoPixel)
67 | #define SK6812RGBW 0x06 // 800 KHz datastream (NeoPixel RGBW)
68 | #define WS2812B_FAST 0x07 // 800 KHz datastream (NeoPixel)
69 | #define WS2812B2_FAST 0x08 // 800 KHz datastream (NeoPixel)
70 |
71 | class Adafruit_NeoPixel {
72 |
73 | public:
74 |
75 | // Constructor: number of LEDs, pin number, LED type
76 | #if (PLATFORM_ID == 32)
77 | Adafruit_NeoPixel(uint16_t n, SPIClass& spi, uint8_t t=WS2812B);
78 | #else
79 | Adafruit_NeoPixel(uint16_t n, uint8_t p=2, uint8_t t=WS2812B);
80 | #endif // #if (PLATFORM_ID == 32)
81 | ~Adafruit_NeoPixel();
82 |
83 | void
84 | begin(void),
85 | show(void) __attribute__((optimize("Ofast"))),
86 | setPin(uint8_t p),
87 | setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b),
88 | setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w),
89 | setPixelColor(uint16_t n, uint32_t c),
90 | setBrightness(uint8_t),
91 | setColor(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue),
92 | setColor(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aWhite),
93 | setColorScaled(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aScaling),
94 | setColorScaled(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aWhite, byte aScaling),
95 | setColorDimmed(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aBrightness),
96 | setColorDimmed(uint16_t aLedNumber, byte aRed, byte aGreen, byte aBlue, byte aWhite, byte aBrightness),
97 | updateLength(uint16_t n),
98 | clear(void);
99 | uint8_t
100 | *getPixels() const,
101 | getBrightness(void) const,
102 | getPin() const,
103 | getType() const;
104 | uint16_t
105 | numPixels(void) const,
106 | getNumLeds(void) const;
107 | static uint32_t
108 | Color(uint8_t r, uint8_t g, uint8_t b),
109 | Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w);
110 | uint32_t
111 | getPixelColor(uint16_t n) const;
112 | byte
113 | brightnessToPWM(byte aBrightness);
114 |
115 | private:
116 |
117 | bool
118 | begun; // true if begin() previously called
119 | uint16_t
120 | numLEDs, // Number of RGB LEDs in strip
121 | numBytes; // Size of 'pixels' buffer below
122 | const uint8_t
123 | type; // Pixel type flag (400 vs 800 KHz)
124 | uint8_t
125 | pin, // Output pin number
126 | brightness,
127 | *pixels; // Holds LED color values (3 bytes each)
128 | uint32_t
129 | endTime; // Latch timing reference
130 | #if (PLATFORM_ID == 32)
131 | SPIClass*
132 | spi_;
133 | #endif
134 | };
135 |
136 | #endif // PARTICLE_NEOPIXEL_H
137 |
--------------------------------------------------------------------------------
/src/neopixel/neopixel.h:
--------------------------------------------------------------------------------
1 | #include "../neopixel.h"
--------------------------------------------------------------------------------