This commit is contained in:
Makuna
2016-02-28 22:09:31 -08:00
parent 34deb7f8ad
commit 62f699c120
35 changed files with 4765 additions and 1871 deletions

199
COPYING
View File

@@ -1,4 +1,3 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
@@ -621,174 +620,56 @@ copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
LGPL ADDENDUM:
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
This program 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 General Public License for more details.
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
0. Additional Definitions.
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

File diff suppressed because it is too large Load Diff

View File

@@ -1,137 +0,0 @@
/*--------------------------------------------------------------------
This file is a modification 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/>.
--------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
#include "RgbColor.h"
// '_flagsPixels' flags for LED _pixels (third parameter to constructor):
#define NEO_RGB 0x00 // Wired for RGB data order
#define NEO_GRB 0x01 // Wired for GRB data order
#define NEO_BRG 0x04
#define NEO_COLMASK 0x05
#define NEO_KHZ400 0x00 // 400 KHz datastream
#define NEO_KHZ800 0x02 // 800 KHz datastream
#define NEO_SPDMASK 0x02
#define NEO_DIRTY 0x80 // a change was made it _pixels that requires a show
// v1 NeoPixels aren't handled by default, include the following define before the
// NeoPixelBus library include to support the slower bus speeds
//#define INCLUDE_NEO_KHZ400_SUPPORT
class NeoPixelBus
{
public:
// Constructor: number of LEDs, pin number, LED type
NeoPixelBus(uint16_t n, uint8_t p, uint8_t t = NEO_GRB | NEO_KHZ800);
~NeoPixelBus();
inline uint16_t getPixelCount()
{
return _countPixels;
}
void Begin();
void Show();
inline bool CanShow(void)
{
return (micros() - _endTime) >= 50L;
}
void ClearTo(uint8_t r, uint8_t g, uint8_t b);
void ClearTo(RgbColor c)
{
ClearTo(c.R, c.G, c.B);
}
bool IsDirty()
{
return (_flagsPixels & NEO_DIRTY);
};
void Dirty()
{
_flagsPixels |= NEO_DIRTY;
};
void ResetDirty()
{
_flagsPixels &= ~NEO_DIRTY;
}
uint8_t* Pixels() const
{
return _pixels;
};
uint16_t PixelCount() const
{
return _countPixels;
};
void SetPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
void SetPixelColor(uint16_t n, RgbColor c)
{
SetPixelColor(n, c.R, c.G, c.B);
};
RgbColor GetPixelColor(uint16_t n) const;
void StartAnimating();
void UpdateAnimations();
bool IsAnimating() const
{
return _activeAnimations > 0;
}
void LinearFadePixelColor(uint16_t time, uint16_t n, RgbColor color);
void FadeTo(uint16_t time, RgbColor color);
private:
void setPin(uint8_t p);
void UpdatePixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
void UpdatePixelColor(uint16_t n, RgbColor c)
{
UpdatePixelColor(n, c.R, c.G, c.B);
};
const uint16_t _countPixels; // Number of RGB LEDs in strip
const uint16_t _sizePixels; // Size of '_pixels' buffer below
uint8_t _flagsPixels; // Pixel flags (400 vs 800 KHz, RGB vs GRB color)
uint8_t _pin; // Output pin number
uint8_t* _pixels; // Holds LED color values (3 bytes each)
uint32_t _endTime; // Latch timing reference
#ifdef __AVR__
const volatile uint8_t* _port; // Output PORT register
uint8_t _pinMask; // Output PORT bitmask
#endif
struct FadeAnimation
{
uint16_t time;
uint16_t remaining;
RgbColor target;
RgbColor origin;
};
uint16_t _activeAnimations;
FadeAnimation* _animations;
uint32_t _animationLastTick;
};

View File

@@ -4,86 +4,33 @@
Arduino NeoPixel library
ESP8266 CUSTOMERS PLEASE READ: While this branch does work with the esp8266, due to the latest SDK releases it will not function reliably when WiFi is being used. Therefore I suggest you use the DmaDriven or UartDriven branches, which both include solutions that will work with WiFi on. Further they contains enhancements that just can't be supported on AVR platform. Including HslColor object and an enhanced animator manager.
A library to control one wire protocol RGB and RGBW leds like SK6812, WS2811, and WS2812 that are commonly refered to as NeoPixels.
Supports most Arduino platforms.
This is the most funtional library for the Esp8266 as it provides solutions for all Esp8266 module types even when WiFi is used.
Please read this best practices link before connecting your NeoPixels, it will save you alot of time and effort.
[Adafruit NeoPixel Best Practices](https://learn.adafruit.com/adafruit-neopixel-uberguide/best-practices)
For quick questions jump on Gitter and ask away.
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Makuna/NeoPixelBus?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Clone this into your Arduino\Library folder
For bugs, make sure there isn't an active issue and then create one.
This library is a modification of the Adafruit NeoPixel library.
The Api is similiar, but it removes the overal brightness feature and adds animation support.
This new library supports a templatized model of defining which method gets used to send data and what order and size the pixel data is sent in. This new design creates the smallest code for each definition of features used. Further it allows for picking which method to send the data on the Esp8266 in an easy to change way.
Please see examples to become familiar with the new design.
Due to this design you will often realize over 500 bytes of more program storage for your sketch. Important for the smallest Arduinos project.
## Installing This Library
## Installing This Library (prefered)
Open the Library Manager and search for "NeoPixelBus by Makuna" and install
## Installing This Library From GitHub
Create a directory in your Arduino\Library folder named "NeoPixelBus"
Clone (Git) this project into that folder.
It should now show up in the import list.
It should now show up in the import list when you restart Arduino IDE.
## Samples
### NeoPixelTest
this is simple example that sets four neopixels to red, green, blue, and then white in order; and then flashes them. If the first pixel is green and the second is red, you need to pass the NEO_RGB flag into the NeoPixelBus constructor.
### NeoPixelFun
this is a more complex example, that includes code for three effects, and demonstrates animations.
## Documentation
[See Wiki](https://github.com/Makuna/NeoPixelBus/wiki)
## API Documentation
### RgbColor object
This represents a color and exposes useful methods to manipulate colors.
#### RgbColor(uint8_t r, uint8_t g, uint8_t b)
instantiates a RgbColor object with the given r, g, b values.
#### RgbColor(uint8_t brightness)
instantiates a RgbColor object with the given brightness. 0 is black, 128 is grey, 255 is white.
#### uint8_t CalculateBrightness()
returns the general brightness of the pixe, averaging color.
#### void Darken(uint8_t delta)
this will darken the color by the given amount, blending toward black. This method is destructive in that you can't expect to then call lighten and return to the original color.
#### void Lighten(uint8_t delta)
this will lighten the color by the given amount, blending toward white. This method is destructive in that you can't expect to then call darken and return to the original color.
#### static RgbColor LinearBlend(RgbColor left, RgbColor right, uint8_t progress)
this will return a color that is a blend between the given colors. The amount to blend is given by the value of progress, 0 will return the left value, 255 will return the right value, 128 will return the value between them.
NOTE: This is not an accurate "visible light" color blend but is fast and in most cases good enough.
### NeoPixelBus object
This represents a single NeoPixel Bus that is connected by a single pin. Please see Adafruit's documentation for details, but the differences are documented below.
#### NeoPixelBus(uint16_t n, uint8_t p = 6, uint8_t t = NEO_GRB | NEO_KHZ800);
instantiates a NewoPixelBus object, with n number of pixels on the bus, over the p pin, using the defined NeoPixel type.
There are some NeoPixels that address the color values differently, so if you set the green color but it displays as red, use the NEO_RGB type flag.
```
NeoPixelBus strip = NeoPixelBus(4, 8, NEO_RGB | NEO_KHZ800);
```
It is rare, but some older NeoPixels require a slower communications speed, to include this support you must include the following define before the NeoPixelBus library include and then include the NEO_KHZ400 type flag to enable this slower speed.
```
#define INCLUDE_NEO_KHZ400_SUPPORT
#include <NeoPixelBus.h>
NeoPixelBus strip = NeoPixelBus(4, 8, NEO_RGB | NEO_KHZ400);
```
#### void SetPixelColor(uint16_t n, RgbColor c)
This allows setting a pixel on the bus to a color as defined by a color object. If an animation is actively running on a pixel, it will be stopped.
#### RgbColor GetPixelColor(uint16_t n) const
this allows retrieving the current pixel color
#### void LinearFadePixelColor(uint16_t time, uint16_t n, RgbColor color)
this will setup an animation for a pixel to linear fade between the current color and the given color over the time given. The time is in milliseconds.
#### void StartAnimating()
this method will initialize the animation state. This should be called only if there are no active animations and new animations are started.
#### void UpdateAnimations()
this method will allow the animations to be processed and update the pixel color state.
NOTE: Show must still be called to push the color state to the physical NeoPixels.
#### bool IsAnimating() const
this method will return the current animation state. It will return false if there are no active animations.

View File

@@ -1,89 +0,0 @@
/*--------------------------------------------------------------------
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 "RgbColor.h"
uint8_t RgbColor::CalculateBrightness()
{
return (uint8_t)(((uint16_t)R + (uint16_t)G + (uint16_t)B) / 3);
}
void RgbColor::Darken(uint8_t delta)
{
if (R > delta)
{
R -= delta;
}
else
{
R = 0;
}
if (G > delta)
{
G -= delta;
}
else
{
G = 0;
}
if (B > delta)
{
B -= delta;
}
else
{
B = 0;
}
}
void RgbColor::Lighten(uint8_t delta)
{
if (R < 255 - delta)
{
R += delta;
}
else
{
R = 255;
}
if (G < 255 - delta)
{
G += delta;
}
else
{
G = 255;
}
if (B < 255 - delta)
{
B += delta;
}
else
{
B = 255;
}
}
RgbColor RgbColor::LinearBlend(RgbColor left, RgbColor right, uint8_t progress)
{
return RgbColor( left.R + ((right.R - left.R) * progress / 255),
left.G + ((right.G - left.G) * progress / 255),
left.B + ((right.B - left.B) * progress / 255));
}

View File

@@ -0,0 +1,226 @@
// NeoPixelAnimation
// This example will randomly pick a new color for each pixel and animate
// the current color to the new color over a random small amount of time, using
// a randomly selected animation curve.
// It will repeat this process once all pixels have finished the animation
//
// This will demonstrate the use of the NeoPixelAnimator extended time feature.
// This feature allows for different time scales to be used, allowing slow extended
// animations to be created.
//
// This will demonstrate the use of the NeoEase animation ease methods; that provide
// simulated acceleration to the animations.
//
// It also includes platform specific code for Esp8266 that demonstrates easy
// animation state and function definition inline. This is not available on AVR
// Arduinos; but the AVR compatible code is also included for comparison.
//
// The example includes some serial output that you can follow along with as it
// does the animation.
//
#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>
const uint16_t PixelCount = 4; // make sure to set this to the number of pixels in your strip
const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
// For Esp8266, the Pin is ignored and it uses GPIO3.
// There are other Esp8266 alternative methods that provide more pin options, but also have
// other side effects.
//NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart800KbpsMethod> strip(PixelCount, PixelPin);
// NeoEsp8266Uart800KbpsMethod also ignores the pin parameter and uses GPI02
//NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> strip(PixelCount, PixelPin);
// NeoEsp8266Uart800KbpsMethod will work with all but pin 16, but is not stable with WiFi
// being active
// NeoPixel animation time management object
NeoPixelAnimator animations(PixelCount, NEO_CENTISECONDS);
// create with enough animations to have one per pixel, depending on the animation
// effect, you may need more or less.
//
// since the normal animation time range is only about 65 seconds, by passing timescale value
// to the NeoPixelAnimator constructor we can increase the time range, but we also increase
// the time between the animation updates.
// NEO_CENTISECONDS will update the animations every 100th of a second rather than the default
// of a 1000th of a second, but the time range will now extend from about 65 seconds to about
// 10.9 minutes. But you must remember that the values passed to StartAnimations are now
// in centiseconds.
//
// Possible values from 1 to 32768, and there some helpful constants defined as...
// NEO_MILLISECONDS 1 // ~65 seconds max duration, ms updates
// NEO_CENTISECONDS 10 // ~10.9 minutes max duration, centisecond updates
// NEO_DECISECONDS 100 // ~1.8 hours max duration, decisecond updates
// NEO_SECONDS 1000 // ~18.2 hours max duration, second updates
// NEO_DECASECONDS 10000 // ~7.5 days, 10 second updates
//
#ifdef ARDUINO_ARCH_AVR
// for AVR, you need to manage the state due to lack of STL/compiler support
// for Esp8266 you can define the function using a lambda and state is created for you
// see below for an example
struct MyAnimationState
{
RgbColor StartingColor; // the color the animation starts at
RgbColor EndingColor; // the color the animation will end at
AnimEaseFunction Easeing; // the acceleration curve it will use
};
MyAnimationState animationState[PixelCount];
// one entry per pixel to match the animation timing manager
void AnimUpdate(const AnimationParam& param)
{
// first apply an easing (curve) to the animation
// this simulates acceleration to the effect
float progress = animationState[param.index].Easeing(param.progress);
// this gets called for each animation on every time step
// progress will start at 0.0 and end at 1.0
// we use the blend function on the RgbColor to mix
// color based on the progress given to us in the animation
RgbColor updatedColor = RgbColor::LinearBlend(
animationState[param.index].StartingColor,
animationState[param.index].EndingColor,
progress);
// apply the color to the strip
strip.SetPixelColor(param.index, updatedColor);
}
#endif
void SetRandomSeed()
{
uint32_t seed;
// random works best with a seed that can use 31 bits
// analogRead on a unconnected pin tends toward less than four bits
seed = analogRead(0);
delay(1);
for (int shifts = 3; shifts < 31; shifts += 3)
{
seed ^= analogRead(0) << shifts;
delay(1);
}
// Serial.println(seed);
randomSeed(seed);
}
void setup()
{
Serial.begin(115200);
while (!Serial); // wait for serial attach
strip.Begin();
strip.Show();
SetRandomSeed();
// just pick some colors
for (uint16_t pixel = 0; pixel < PixelCount; pixel++)
{
RgbColor color = RgbColor(random(255), random(255), random(255));
strip.SetPixelColor(pixel, color);
}
Serial.println();
Serial.println("Running...");
}
void SetupAnimationSet()
{
// setup some animations
for (uint16_t pixel = 0; pixel < PixelCount; pixel++)
{
const uint8_t peak = 128;
// pick a random duration of the animation for this pixel
// since values are centiseconds, the range is 1 - 4 seconds
uint16_t time = random(100, 400);
// each animation starts with the color that was present
RgbColor originalColor = strip.GetPixelColor(pixel);
// and ends with a random color
RgbColor targetColor = RgbColor(random(peak), random(peak), random(peak));
// with the random ease function
AnimEaseFunction easing;
switch (random(3))
{
case 0:
easing = NeoEase::CubicIn;
break;
case 1:
easing = NeoEase::CubicOut;
break;
case 2:
easing = NeoEase::QuadraticInOut;
break;
}
#ifdef ARDUINO_ARCH_AVR
// each animation starts with the color that was present
animationState[pixel].StartingColor = originalColor;
// and ends with a random color
animationState[pixel].EndingColor = targetColor;
// using the specific curve
animationState[pixel].Easeing = easing;
// now use the animation state we just calculated and start the animation
// which will continue to run and call the update function until it completes
animations.StartAnimation(pixel, time, AnimUpdate);
#else
// we must supply a function that will define the animation, in this example
// we are using "lambda expression" to define the function inline, which gives
// us an easy way to "capture" the originalColor and targetColor for the call back.
//
// this function will get called back when ever the animation needs to change
// the state of the pixel, it will provide a animation progress value
// from 0.0 (start of animation) to 1.0 (end of animation)
//
// we use this progress value to define how we want to animate in this case
// we call RgbColor::LinearBlend which will return a color blended between
// the values given, by the amount passed, hich is also a float value from 0.0-1.0.
// then we set the color.
//
// There is no need for the MyAnimationState struct as the compiler takes care
// of those details for us
AnimUpdateCallback animUpdate = [=](const AnimationParam& param)
{
// progress will start at 0.0 and end at 1.0
// we convert to the curve we want
float progress = easing(param.progress);
// use the curve value to apply to the animation
RgbColor updatedColor = RgbColor::LinearBlend(originalColor, targetColor, progress);
strip.SetPixelColor(pixel, updatedColor);
};
// now use the animation properties we just calculated and start the animation
// which will continue to run and call the update function until it completes
animations.StartAnimation(pixel, time, animUpdate);
#endif
}
}
void loop()
{
if (animations.IsAnimating())
{
// the normal loop just needs these two to run the active animations
animations.UpdateAnimations();
strip.Show();
}
else
{
Serial.println();
Serial.println("Setup Next Set...");
// example function that sets up some animations
SetupAnimationSet();
}
}

View File

@@ -1,139 +0,0 @@
#include <NeoPixelBus.h>
#define pixelCount 4
NeoPixelBus strip = NeoPixelBus(pixelCount, 8);
uint16_t effectState = 0;
void setup()
{
strip.Begin();
strip.Show();
SetRandomSeed();
}
void loop()
{
// There are three fun functions that implement different effects
// uncomment one at a time and upload to see the effect
// LoopAround(192, 200); // very interesting on rings of NeoPixels
PickRandom(128);
// FadeInFadeOutRinseRepeat(192);
// start animating
strip.StartAnimating();
// wait until no more animations are running
while (strip.IsAnimating())
{
strip.UpdateAnimations();
strip.Show();
delay(31); // ~30hz change cycle
}
}
void FadeInFadeOutRinseRepeat(uint8_t peak)
{
if (effectState == 0)
{
for (uint8_t pixel = 0; pixel < pixelCount; pixel++)
{
uint16_t time = random(800,1000);
strip.LinearFadePixelColor(time, pixel, RgbColor(random(peak), random(peak), random(peak)));
}
}
else if (effectState == 1)
{
for (uint8_t pixel = 0; pixel < pixelCount; pixel++)
{
uint16_t time = random(600,700);
strip.LinearFadePixelColor(time, pixel, RgbColor(0, 0, 0));
}
}
effectState = (effectState + 1) % 2; // next effectState and keep within the number of effectStates
}
void PickRandom(uint8_t peak)
{
// pick random set of pixels to animate
uint8_t count = random(pixelCount);
while (count > 0)
{
uint8_t pixel = random(pixelCount);
// configure the animations
RgbColor color; // = strip.getPixelColor(pixel);
color = RgbColor(random(peak), random(peak), random(peak));
uint16_t time = random(100,400);
strip.LinearFadePixelColor( time, pixel, color);
count--;
}
}
void LoopAround(uint8_t peak, uint16_t speed)
{
// Looping around the ring sample
uint16_t prevPixel;
RgbColor prevColor;
// fade previous one dark
prevPixel = (effectState + (pixelCount - 5)) % pixelCount;
strip.LinearFadePixelColor(speed, prevPixel, RgbColor(0, 0, 0));
// fade previous one dark
prevPixel = (effectState + (pixelCount - 4)) % pixelCount;
prevColor = strip.GetPixelColor( prevPixel );
prevColor.Darken(prevColor.CalculateBrightness() / 2);
strip.LinearFadePixelColor(speed, prevPixel, prevColor);
// fade previous one dark
prevPixel = (effectState + (pixelCount - 3)) % pixelCount;
prevColor = strip.GetPixelColor( prevPixel );
prevColor.Darken(prevColor.CalculateBrightness() / 2);
strip.LinearFadePixelColor(speed, prevPixel, prevColor);
// fade previous one dark
prevPixel = (effectState + (pixelCount - 2)) % pixelCount;
prevColor = strip.GetPixelColor( prevPixel );
prevColor.Darken(prevColor.CalculateBrightness() / 2);
strip.LinearFadePixelColor(speed, prevPixel, prevColor);
// fade previous one dark
prevPixel = (effectState + (pixelCount - 1)) % pixelCount;
prevColor = strip.GetPixelColor( prevPixel );
prevColor.Darken(prevColor.CalculateBrightness() / 2);
strip.LinearFadePixelColor(speed, prevPixel, prevColor);
// fade current one light
strip.LinearFadePixelColor(speed, effectState, RgbColor(random(peak), random(peak), random(peak)));
effectState = (effectState + 1) % pixelCount;
}
void SetRandomSeed()
{
uint32_t seed;
// random works best with a seed that can use 31 bits
// analogRead on a unconnected pin tends toward less than four bits
seed = analogRead(0);
delay(1);
for (int shifts = 3; shifts < 31; shifts += 3)
{
seed ^= analogRead(0) << shifts;
delay(1);
}
// Serial.println(seed);
randomSeed(seed);
}

View File

@@ -0,0 +1,134 @@
// NeoPixelFunFadeInOut
// This example will randomly pick a color and fade all pixels to that color, then
// it will fade them to black and restart over
//
// This example demonstrates the use of a single animation channel to animate all
// the pixels at once.
//
#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>
const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip
const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266
const uint8_t AnimationChannels = 1; // we only need one as all the pixels are animated at once
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
// For Esp8266, the Pin is ignored and it uses GPIO3.
// There are other Esp8266 alternative methods that provide more pin options, but also have
// other side effects.
//NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart800KbpsMethod> strip(PixelCount, PixelPin);
// NeoEsp8266Uart800KbpsMethod also ignores the pin parameter and uses GPI02
//NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> strip(PixelCount, PixelPin);
// NeoEsp8266Uart800KbpsMethod will work with all but pin 16, but is not stable with WiFi
// being active
NeoPixelAnimator animations(AnimationChannels); // NeoPixel animation management object
uint16_t effectState = 0; // general purpose variable used to store effect state
// what is stored for state is specific to the need, in this case, the colors.
// basically what ever you need inside the animation update function
struct MyAnimationState
{
RgbColor StartingColor;
RgbColor EndingColor;
};
// one entry per pixel to match the animation timing manager
MyAnimationState animationState[AnimationChannels];
void SetRandomSeed()
{
uint32_t seed;
// random works best with a seed that can use 31 bits
// analogRead on a unconnected pin tends toward less than four bits
seed = analogRead(0);
delay(1);
for (int shifts = 3; shifts < 31; shifts += 3)
{
seed ^= analogRead(0) << shifts;
delay(1);
}
randomSeed(seed);
}
// simple blend function
void BlendAnimUpdate(const AnimationParam& param)
{
// this gets called for each animation on every time step
// progress will start at 0.0 and end at 1.0
// we use the blend function on the RgbColor to mix
// color based on the progress given to us in the animation
RgbColor updatedColor = RgbColor::LinearBlend(
animationState[param.index].StartingColor,
animationState[param.index].EndingColor,
param.progress);
// apply the color to the strip
for (uint16_t pixel = 0; pixel < PixelCount; pixel++)
{
strip.SetPixelColor(pixel, updatedColor);
}
}
void FadeInFadeOutRinseRepeat(float luminance)
{
if (effectState == 0)
{
// Fade upto a random color
// we use HslColor object as it allows us to easily pick a hue
// with the same saturation and luminance so the colors picked
// will have similiar overall brightness
RgbColor target = HslColor(random(360) / 360.0f, 1.0f, luminance);
uint16_t time = random(800, 2000);
animationState[0].StartingColor = strip.GetPixelColor(0);
animationState[0].EndingColor = target;
animations.StartAnimation(0, time, BlendAnimUpdate);
}
else if (effectState == 1)
{
// fade to black
uint16_t time = random(600, 700);
animationState[0].StartingColor = strip.GetPixelColor(0);
animationState[0].EndingColor = RgbColor(0);
animations.StartAnimation(0, time, BlendAnimUpdate);
}
// toggle to the next effect state
effectState = (effectState + 1) % 2;
}
void setup()
{
strip.Begin();
strip.Show();
SetRandomSeed();
}
void loop()
{
if (animations.IsAnimating())
{
// the normal loop just needs these two to run the active animations
animations.UpdateAnimations();
strip.Show();
}
else
{
// no animation runnning, start some
//
FadeInFadeOutRinseRepeat(0.2f); // 0.0 = black, 0.25 is normal, 0.5 is bright
}
}

View File

@@ -0,0 +1,140 @@
// NeoPixelFunLoop
// This example will move a trail of light around a series of pixels.
// A ring formation of pixels looks best.
// The trail will have a slowly fading tail.
//
// This will demonstrate the use of the NeoPixelAnimator.
// It shows the advanced use an animation to control the modification and
// starting of other animations.
// It also shows the normal use of animating colors.
// It also demonstrates the ability to share an animation channel rather than
// hard code them to pixels.
//
#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>
const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip
const uint16_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266
const uint16_t AnimCount = 8; // we only need about 8 animations, one to track movement, and the rest to actually animate
const uint16_t PixelFadeDuration = 400; // half a second
// one second divide by the number of pixels = loop once a second
const uint16_t NextPixelMoveDuration = 1000 / PixelCount; // how fast we move through the pixels
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
// For Esp8266, the Pin is ignored and it uses GPIO3.
// There are other Esp8266 alternative methods that provide more pin options, but also have
// other side effects.
//NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart800KbpsMethod> strip(PixelCount, PixelPin);
// NeoEsp8266Uart800KbpsMethod also ignores the pin parameter and uses GPI02
//NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> strip(PixelCount, PixelPin);
// NeoEsp8266Uart800KbpsMethod will work with all but pin 16, but is not stable with WiFi
// being active
// what is stored for state is specific to the need, in this case, the colors and
// the pixel to animate;
// basically what ever you need inside the animation update function
struct MyAnimationState
{
RgbColor StartingColor;
RgbColor EndingColor;
uint16_t IndexPixel; // which pixel this animation is effecting
};
NeoPixelAnimator animations(AnimCount); // NeoPixel animation management object
MyAnimationState animationState[AnimCount];
uint16_t frontPixel = 0; // the front of the loop
RgbColor frontColor; // the color at the front of the loop
void SetRandomSeed()
{
uint32_t seed;
// random works best with a seed that can use 31 bits
// analogRead on a unconnected pin tends toward less than four bits
seed = analogRead(0);
delay(1);
for (int shifts = 3; shifts < 31; shifts += 3)
{
seed ^= analogRead(0) << shifts;
delay(1);
}
// Serial.println(seed);
randomSeed(seed);
}
void FadeOutAnimUpdate(const AnimationParam& param)
{
// this gets called for each animation on every time step
// progress will start at 0.0 and end at 1.0
// we use the blend function on the RgbColor to mix
// color based on the progress given to us in the animation
RgbColor updatedColor = RgbColor::LinearBlend(
animationState[param.index].StartingColor,
animationState[param.index].EndingColor,
param.progress);
// apply the color to the strip
strip.SetPixelColor(animationState[param.index].IndexPixel, updatedColor);
}
void LoopAnimUpdate(const AnimationParam& param)
{
// wait for this animation to complete,
// we are using it as a timer of sorts
if (param.state == AnimationState_Completed)
{
// done, time to restart this position tracking animation/timer
animations.RestartAnimation(param.index);
// pick the next pixel inline to start animating
//
frontPixel = (frontPixel + 1) % PixelCount; // increment and wrap
if (frontPixel == 1)
{
// we looped, lets pick a new front color
frontColor = HslColor(random(360) / 360.0f, 1.0f, 0.25f);
}
uint16_t indexAnim;
// do we have an animation available to use to animate the next front pixel?
// if you see skipping, then either you are going to fast or need to increase
// the number of animation channels
if (animations.NextAvailableAnimation(&indexAnim, 0))
{
animationState[indexAnim].StartingColor = frontColor;
animationState[indexAnim].EndingColor = RgbColor(0, 0, 0);
animationState[indexAnim].IndexPixel = frontPixel;
animations.StartAnimation(indexAnim, PixelFadeDuration, FadeOutAnimUpdate);
}
}
}
void setup()
{
strip.Begin();
strip.Show();
SetRandomSeed();
// we use the index 0 animation to time how often we move to the next
// pixel in the strip
animations.StartAnimation(0, NextPixelMoveDuration, LoopAnimUpdate);
}
void loop()
{
// this is all that is needed to keep it running
// and avoiding using delay() is always a good thing for
// any timing related routines
animations.UpdateAnimations();
strip.Show();
}

View File

@@ -0,0 +1,118 @@
// NeoPixelFunRandomChange
// This example will randomly select a number pixels and then
// start an animation to blend them from their current color to
// randomly selected a color
//
#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>
const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip
const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
// For Esp8266, the Pin is ignored and it uses GPIO3.
// There are other Esp8266 alternative methods that provide more pin options, but also have
// other side effects.
//NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart800KbpsMethod> strip(PixelCount, PixelPin);
// NeoEsp8266Uart800KbpsMethod also ignores the pin parameter and uses GPI02
//NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> strip(PixelCount, PixelPin);
// NeoEsp8266Uart800KbpsMethod will work with all but pin 16, but is not stable with WiFi
// being active
NeoPixelAnimator animations(PixelCount); // NeoPixel animation management object
// what is stored for state is specific to the need, in this case, the colors.
// Basically what ever you need inside the animation update function
struct MyAnimationState
{
RgbColor StartingColor;
RgbColor EndingColor;
};
// one entry per pixel to match the animation timing manager
MyAnimationState animationState[PixelCount];
void SetRandomSeed()
{
uint32_t seed;
// random works best with a seed that can use 31 bits
// analogRead on a unconnected pin tends toward less than four bits
seed = analogRead(0);
delay(1);
for (int shifts = 3; shifts < 31; shifts += 3)
{
seed ^= analogRead(0) << shifts;
delay(1);
}
// Serial.println(seed);
randomSeed(seed);
}
// simple blend function
void BlendAnimUpdate(const AnimationParam& param)
{
// this gets called for each animation on every time step
// progress will start at 0.0 and end at 1.0
// we use the blend function on the RgbColor to mix
// color based on the progress given to us in the animation
RgbColor updatedColor = RgbColor::LinearBlend(
animationState[param.index].StartingColor,
animationState[param.index].EndingColor,
param.progress);
// apply the color to the strip
strip.SetPixelColor(param.index, updatedColor);
}
void PickRandom(float luminance)
{
// pick random count of pixels to animate
uint16_t count = random(PixelCount);
while (count > 0)
{
// pick a random pixel
uint16_t pixel = random(PixelCount);
// pick random time and random color
// we use HslColor object as it allows us to easily pick a color
// with the same saturation and luminance
uint16_t time = random(100, 400);
animationState[pixel].StartingColor = strip.GetPixelColor(pixel);
animationState[pixel].EndingColor = HslColor(random(360) / 360.0f, 1.0f, luminance);
animations.StartAnimation(pixel, time, BlendAnimUpdate);
count--;
}
}
void setup()
{
strip.Begin();
strip.Show();
SetRandomSeed();
}
void loop()
{
if (animations.IsAnimating())
{
// the normal loop just needs these two to run the active animations
animations.UpdateAnimations();
strip.Show();
}
else
{
// no animations runnning, start some
//
PickRandom(0.2f); // 0.0 = black, 0.25 is normal, 0.5 is bright
}
}

View File

@@ -0,0 +1,133 @@
// NeoPixelTest
// This example will cycle between showing four pixels as Red, Green, Blue, White
// and then showing those pixels as Black.
//
// Included but commented out are examples of configuring a NeoPixelBus for
// different color order including an extra white channel, different data speeds, and
// for Esp8266 different methods to send the data.
// NOTE: You will need to make sure to pick the one for your platform
//
//
// There is serial output of the current state so you can confirm and follow along
//
#include <NeoPixelBus.h>
const uint16_t PixelCount = 4; // this example assumes 4 pixels, making it smaller will cause a failure
const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266
#define colorSaturation 128
// three element pixels, in different order and speeds
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
//NeoPixelBus<NeoRgbFeature, Neo400KbpsMethod> strip(PixelCount, PixelPin);
// You can also use one of these for Esp8266,
// each having their own restrictions
//
// These two are the same as above as the DMA method is the default
// NOTE: These will ignore the PIN and use GPI03 pin
//NeoPixelBus<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod> strip(PixelCount, PixelPin);
//NeoPixelBus<NeoRgbFeature, NeoEsp8266Dma400KbpsMethod> strip(PixelCount, PixelPin);
// Uart method is good for the Esp-01 or other pin restricted modules
// NOTE: These will ignore the PIN and use GPI02 pin
//NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart800KbpsMethod> strip(PixelCount, PixelPin);
//NeoPixelBus<NeoRgbFeature, NeoEsp8266Uart400KbpsMethod> strip(PixelCount, PixelPin);
// The bitbang method is really only good if you are not using WiFi features of the ESP
// It works with all but pin 16
//NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> strip(PixelCount, PixelPin);
//NeoPixelBus<NeoRgbFeature, NeoEsp8266BitBang400KbpsMethod> strip(PixelCount, PixelPin);
// four element pixels, RGBW
//NeoPixelBus<NeoRgbwFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
RgbColor red(colorSaturation, 0, 0);
RgbColor green(0, colorSaturation, 0);
RgbColor blue(0, 0, colorSaturation);
RgbColor white(colorSaturation);
RgbColor black(0);
HslColor hslRed(red);
HslColor hslGreen(green);
HslColor hslBlue(blue);
HslColor hslWhite(white);
HslColor hslBlack(black);
void setup()
{
Serial.begin(115200);
while (!Serial); // wait for serial attach
Serial.println();
Serial.println("Initializing...");
Serial.flush();
// this resets all the neopixels to an off state
strip.Begin();
strip.Show();
Serial.println();
Serial.println("Running...");
}
void loop()
{
delay(5000);
Serial.println("Colors R, G, B, W...");
// set the colors,
// if they don't match in order, you need to use NeoGrbFeature feature
strip.SetPixelColor(0, red);
strip.SetPixelColor(1, green);
strip.SetPixelColor(2, blue);
strip.SetPixelColor(3, white);
// the following line demonstrates rgbw color support
// if the NeoPixels are rgbw types the following line will compile
// if the NeoPixels are anything else, the following line will give an error
//strip.SetPixelColor(3, RgbwColor(colorSaturation));
strip.Show();
delay(5000);
Serial.println("Off ...");
// turn off the pixels
strip.SetPixelColor(0, black);
strip.SetPixelColor(1, black);
strip.SetPixelColor(2, black);
strip.SetPixelColor(3, black);
strip.Show();
delay(5000);
Serial.println("HSL Colors R, G, B, W...");
// set the colors,
// if they don't match in order, you may need to use NeoGrbFeature feature
strip.SetPixelColor(0, hslRed);
strip.SetPixelColor(1, hslGreen);
strip.SetPixelColor(2, hslBlue);
strip.SetPixelColor(3, hslWhite);
strip.Show();
delay(5000);
Serial.println("Off again...");
// turn off the pixels
strip.SetPixelColor(0, hslBlack);
strip.SetPixelColor(1, hslBlack);
strip.SetPixelColor(2, hslBlack);
strip.SetPixelColor(3, hslBlack);
strip.Show();
}

View File

@@ -1,44 +0,0 @@
#include <NeoPixelBus.h>
#define pixelCount 4
#define colorSaturation 128
NeoPixelBus strip = NeoPixelBus(pixelCount, 8);
RgbColor red = RgbColor(colorSaturation, 0, 0);
RgbColor green = RgbColor(0, colorSaturation, 0);
RgbColor blue = RgbColor(0, 0, colorSaturation);
RgbColor white = RgbColor(colorSaturation);
RgbColor black = RgbColor(0);
void setup()
{
// this resets all the neopixels to an off state
strip.Begin();
strip.Show();
}
void loop()
{
delay(1000);
// set the colors,
// if they don't match in order, you may need to use NEO_GRB flag
strip.SetPixelColor(0, red);
strip.SetPixelColor(1, green);
strip.SetPixelColor(2, blue);
strip.SetPixelColor(3, white);
strip.Show();
delay(3000);
// turn off the pixels
strip.SetPixelColor(0, black);
strip.SetPixelColor(1, black);
strip.SetPixelColor(2, black);
strip.SetPixelColor(3, black);
strip.Show();
}

99
keywords.txt Normal file
View File

@@ -0,0 +1,99 @@
#######################################
# Syntax Coloring Map NeoPixelBus
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
NeoPixelBus KEYWORD1
RgbwColor KEYWORD1
RgbColor KEYWORD1
HslColor KEYWORD1
HsbColor KEYWORD1
NeoGrbFeature KEYWORD1
NeoRgbwFeature KEYWORD1
NeoRgbFeature KEYWORD1
NeoBrgFeature KEYWORD1
Neo800KbpsMethod KEYWORD1
Neo400KbpsMethod KEYWORD1
NeoAvr800KbpsMethod KEYWORD1
NeoAvr400KbpsMethod KEYWORD1
NeoEsp8266Dma800Method KEYWORD1
NeoEsp8266Dma400Method KEYWORD1
NeoEsp8266Uart800Method KEYWORD1
NeoEsp8266Uart400Method KEYWORD1
NeoEsp8266BitBang800Method KEYWORD1
NeoEsp8266BitBang400Method KEYWORD1
NeoPixelAnimator KEYWORD1
AnimUpdateCallback KEYWORD1
AnimationParam KEYWORD1
NeoEase KEYWORD1
AnimEaseFunction KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
Begin KEYWORD2
Show KEYWORD2
CanShow KEYWORD2
ClearTo KEYWORD2
IsDirty KEYWORD2
Dirty KEYWORD2
ResetDirty KEYWORD2
Pixels KEYWORD2
PixelsSize KEYWORD2
PixelCount KEYWORD2
SetPixelColor KEYWORD2
GetPixelColor KEYWORD2
CalculateBrightness KEYWORD2
Darken KEYWORD2
Lighten KEYWORD2
LinearBlend KEYWORD2
IsAnimating KEYWORD2
NextAvailableAnimation KEYWORD2
StartAnimation KEYWORD2
StopAnimation KEYWORD2
RestartAnimation KEYWORD2
IsAnimationActive KEYWORD2
AnimationDuration KEYWORD2
UpdateAnimations KEYWORD2
IsPaused KEYWORD2
Pause KEYWORD2
Resume KEYWORD2
TimeScale KEYWORD2
QuadraticIn KEYWORD2
QuadraticOut KEYWORD2
QuadraticInOut KEYWORD2
CubicIn KEYWORD2
CubicOut KEYWORD2
CubicInOut KEYWORD2
QuarticIn KEYWORD2
QuarticOut KEYWORD2
QuarticInOut KEYWORD2
QuinticIn KEYWORD2
QuinticOut KEYWORD2
QuinticInOut KEYWORD2
SinusoidalIn KEYWORD2
SinusoidalOut KEYWORD2
SinusoidalInOut KEYWORD2
ExponentialIn KEYWORD2
ExponentialOut KEYWORD2
ExponentialInOut KEYWORD2
CircularIn KEYWORD2
CircularOut KEYWORD2
CircularInOut KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
NEO_MILLISECONDS LITERAL1
NEO_CENTISECONDS LITERAL1
NEO_DECISECONDS LITERAL1
NEO_SECONDS LITERAL1
NEO_DECASECONDS LITERAL1
AnimationState_Started LITERAL1
AnimationState_Progress LITERAL1
AnimationState_Completed LITERAL1

View File

@@ -1,11 +1,13 @@
{
"name": "NeoPixelBus",
"description": "Adafruit enhanced NeoPixel support library (WS2811, WS2812)",
"keywords": "led, strip",
"frameworks": "arduino",
"platforms": "*",
"repository": {
"name": "NeoPixelBus by Makuna",
"keywords": "NeoPixel, WS2811, WS2812, SK6812, RGB, RGBW",
"description": "A library that makes controlling NeoPixels (WS2811, WS2812 & SK6812) easy. Supports most Arduino platforms. Support for RGBW pixels. Includes seperate RgbColor, RgbwColor, HslColor, and HsbColor objects. Includes an animator class that helps create asyncronous animations. For Esp8266 it has three methods of sending data, DMA, UART, and Bit Bang.",
"repository":
{
"type": "git",
"url": "https://github.com/Makuna/NeoPixelBus.git"
}
"url": "https://github.com/Makuna/NeoPixelBus"
},
"frameworks": "arduino",
"platforms": "*"
}

9
library.properties Normal file
View File

@@ -0,0 +1,9 @@
name=NeoPixelBus by Makuna
version=2.0.0
author=Michael C. Miller (makuna@live.com)
maintainer=Michael C. Miller (makuna@live.com)
sentence=A library that makes controlling NeoPixels (WS2811, WS2812 & SK6812) easy.
paragraph=Supports most Arduino platforms, and especially Esp8266. Support for RGBW pixels. Includes seperate RgbColor, RgbwColor, HslColor, and HsbColor objects. Includes an animator class that helps create asyncronous animations. For Esp8266 it has three methods of sending data, DMA, UART, and Bit Bang.
category=Display
url=https://github.com/Makuna/NeoPixelBus
architectures=*

75
src/HsbColor.cpp Normal file
View File

@@ -0,0 +1,75 @@
/*-------------------------------------------------------------------------
HsbColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "RgbColor.h"
#include "HsbColor.h"
HsbColor::HsbColor(RgbColor color)
{
// convert colors to float between (0.0 - 1.0)
float r = color.R / 255.0f;
float g = color.G / 255.0f;
float b = color.B / 255.0f;
float max = (r > g && r > b) ? r : (g > b) ? g : b;
float min = (r < g && r < b) ? r : (g < b) ? g : b;
float d = max - min;
float h = 0.0;
float v = max;
float s = (v == 0.0f) ? 0 : (d / v);
if (d != 0.0f)
{
if (r == max)
{
h = (g - b) / d + (g < b ? 6.0f : 0.0f);
}
else if (g == max)
{
h = (b - r) / d + 2.0f;
}
else
{
h = (r - g) / d + 4.0f;
}
h /= 6.0f;
}
H = h;
S = s;
B = v;
}
HsbColor HsbColor::LinearBlend(HsbColor left, HsbColor right, float progress)
{
return HsbColor(left.H + ((right.H - left.H) * progress),
left.S + ((right.S - left.S) * progress),
left.B + ((right.B - left.B) * progress));
}

75
src/HsbColor.h Normal file
View File

@@ -0,0 +1,75 @@
/*-------------------------------------------------------------------------
HsbColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
// ------------------------------------------------------------------------
// HsbColor represents a color object that is represented by Hue, Saturation, Brightness
// component values. It contains helpful color routines to manipulate the
// color.
// ------------------------------------------------------------------------
struct HsbColor
{
// ------------------------------------------------------------------------
// Construct a HsbColor using H, S, B values (0.0 - 1.0)
// ------------------------------------------------------------------------
HsbColor(float h, float s, float b) :
H(h), S(s), B(b)
{
};
// ------------------------------------------------------------------------
// Construct a HsbColor using RgbColor
// ------------------------------------------------------------------------
HsbColor(RgbColor color);
// ------------------------------------------------------------------------
// Construct a HsbColor that will have its values set in latter operations
// CAUTION: The H,S,B members are not initialized and may not be consistent
// ------------------------------------------------------------------------
HsbColor()
{
};
// ------------------------------------------------------------------------
// LinearBlend between two colors by the amount defined by progress variable
// left - the color to start the blend at
// right - the color to end the blend at
// progress - (0.0 - 1.0) value where 0.0 will return left and 1.0 will return right
// and a value between will blend the color weighted linearly between them
// ------------------------------------------------------------------------
static HsbColor LinearBlend(HsbColor left, HsbColor right, float progress);
// ------------------------------------------------------------------------
// Hue, Saturation, Brightness color members
// ------------------------------------------------------------------------
float H;
float S;
float B;
};

78
src/HslColor.cpp Normal file
View File

@@ -0,0 +1,78 @@
/*-------------------------------------------------------------------------
HslColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "RgbColor.h"
#include "HslColor.h"
HslColor::HslColor(RgbColor color)
{
// convert colors to float between (0.0 - 1.0)
float r = color.R / 255.0f;
float g = color.G / 255.0f;
float b = color.B / 255.0f;
float max = (r > g && r > b) ? r : (g > b) ? g : b;
float min = (r < g && r < b) ? r : (g < b) ? g : b;
float h, s, l;
l = (max + min) / 2.0f;
if (max == min)
{
h = s = 0.0f;
}
else
{
float d = max - min;
s = (l > 0.5f) ? d / (2.0f - (max + min)) : d / (max + min);
if (r > g && r > b)
{
h = (g - b) / d + (g < b ? 6.0f : 0.0f);
}
else if (g > b)
{
h = (b - r) / d + 2.0f;
}
else
{
h = (r - g) / d + 4.0f;
}
h /= 6.0f;
}
H = h;
S = s;
L = l;
}
HslColor HslColor::LinearBlend(HslColor left, HslColor right, float progress)
{
return HslColor(left.H + ((right.H - left.H) * progress),
left.S + ((right.S - left.S) * progress),
left.L + ((right.L - left.L) * progress));
}

76
src/HslColor.h Normal file
View File

@@ -0,0 +1,76 @@
/*-------------------------------------------------------------------------
HslColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
// ------------------------------------------------------------------------
// HslColor represents a color object that is represented by Hue, Saturation, Lightness
// component values. It contains helpful color routines to manipulate the
// color.
// ------------------------------------------------------------------------
struct HslColor
{
// ------------------------------------------------------------------------
// Construct a HslColor using H, S, L values (0.0 - 1.0)
// L should be limited to between (0.0 - 0.5)
// ------------------------------------------------------------------------
HslColor(float h, float s, float l) :
H(h), S(s), L(l)
{
};
// ------------------------------------------------------------------------
// Construct a HslColor using RgbColor
// ------------------------------------------------------------------------
HslColor(RgbColor color);
// ------------------------------------------------------------------------
// Construct a HslColor that will have its values set in latter operations
// CAUTION: The H,S,L members are not initialized and may not be consistent
// ------------------------------------------------------------------------
HslColor()
{
};
// ------------------------------------------------------------------------
// LinearBlend between two colors by the amount defined by progress variable
// left - the color to start the blend at
// right - the color to end the blend at
// progress - (0.0 - 1.0) value where 0.0 will return left and 1.0 will return right
// and a value between will blend the color weighted linearly between them
// ------------------------------------------------------------------------
static HslColor LinearBlend(HslColor left, HslColor right, float progress);
// ------------------------------------------------------------------------
// Hue, Saturation, Lightness color members
// ------------------------------------------------------------------------
float H;
float S;
float L;
};

624
src/NeoArmMethod.h Normal file
View File

@@ -0,0 +1,624 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for ARM MCUs.
Teensy 3.0, 3.1, LC, Arduino Due
Written by Michael C. Miller.
Some work taken from the Adafruit NeoPixel library.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
The contents of this file were taken from the Adafruit NeoPixel library
and modified only to fit within individual calling functions.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#if defined(__arm__)
template<typename T_SPEED> class NeoArmMethodBase
{
public:
NeoArmMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize) :
_pin(pin)
{
pinMode(pin, OUTPUT);
_sizePixels = pixelCount * elementSize;
_pixels = (uint8_t*)malloc(_sizePixels);
memset(_pixels, 0, _sizePixels);
}
~NeoArmMethodBase()
{
pinMode(_pin, INPUT);
free(_pixels);
}
bool IsReadyToUpdate() const
{
uint32_t delta = micros() - _endTime;
return (delta >= 50L);
}
void Initialize()
{
digitalWrite(_pin, LOW);
_endTime = micros();
}
void Update()
{
// Data latch = 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.
while (!IsReadyToUpdate())
{
yield(); // allows for system yield if needed
}
noInterrupts(); // Need 100% focus on instruction timing
T_SPEED::send_pixels(_pixels, _sizePixels, _pin);
interrupts();
// save EOD time for latch on next call
_endTime = micros();
}
uint8_t* getPixels() const
{
return _pixels;
};
size_t getPixelsSize() const
{
return _sizePixels;
};
private:
uint32_t _endTime; // Latch timing reference
size_t _sizePixels; // Size of '_pixels' buffer below
uint8_t* _pixels; // Holds LED color values
uint8_t _pin; // output pin number
};
#if defined(__MK20DX128__) || defined(__MK20DX256__) // Teensy 3.0 & 3.1
class NeoArmMk20dxSpeedProps800Kbps
{
public:
static const uint32_t CyclesT0h = (F_CPU / 4000000);
static const uint32_t CyclesT1h = (F_CPU / 1250000);
static const uint32_t Cycles = (F_CPU / 800000);
};
class NeoArmMk20dxSpeedProps400Kbps
{
public:
static const uint32_t CyclesT0h = (F_CPU / 2000000);
static const uint32_t CyclesT1h = (F_CPU / 833333);
static const uint32_t Cycles = (F_CPU / 400000);
};
template<typename T_SPEEDPROPS> class NeoArmMk20dxSpeedBase
{
public:
static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin)
{
uint8_t* p = pixels;
uint8_t* end = p + sizePixels;
uint8_t pix;
uint8_t mask;
volatile uint8_t* set = portSetRegister(pin);
volatile uint8_t* clr = portClearRegister(pin);
uint32_t cyc;
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
cyc = ARM_DWT_CYCCNT + T_SPEEDPROPS::Cycles;
while (p < end)
{
pix = *p++;
for (mask = 0x80; mask; mask >>= 1)
{
while (ARM_DWT_CYCCNT - cyc < T_SPEEDPROPS::Cycles);
cyc = ARM_DWT_CYCCNT;
*set = 1;
if (pix & mask)
{
while (ARM_DWT_CYCCNT - cyc < T_SPEEDPROPS::CyclesT1h);
}
else
{
while (ARM_DWT_CYCCNT - cyc < T_SPEEDPROPS::CyclesT0h);
}
*clr = 1;
}
}
}
};
typedef NeoArmMethodBase<NeoArmMk20dxSpeedBase<NeoArmMk20dxSpeedProps800Kbps>> NeoArm800KbpsMethod;
typedef NeoArmMethodBase<NeoArmMk20dxSpeedBase<NeoArmMk20dxSpeedProps400Kbps>> NeoArm400KbpsMethod;
#elif defined(__MKL26Z64__) // Teensy-LC
#if F_CPU == 48000000
class NeoArmMk26z64Speed800Kbps
{
public:
static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin)
{
uint8_t* p = pixels;
uint8_t pix;
uint8_t count;
uint8_t dly;
uint8_t bitmask = digitalPinToBitMask(pin);
volatile uint8_t* reg = portSetRegister(pin);
uint32_t num = sizePixels;
asm volatile(
"L%=_begin:" "\n\t"
"ldrb %[pix], [%[p], #0]" "\n\t"
"lsl %[pix], #24" "\n\t"
"movs %[count], #7" "\n\t"
"L%=_loop:" "\n\t"
"lsl %[pix], #1" "\n\t"
"bcs L%=_loop_one" "\n\t"
"L%=_loop_zero:"
"strb %[bitmask], [%[reg], #0]" "\n\t"
"movs %[dly], #4" "\n\t"
"L%=_loop_delay_T0H:" "\n\t"
"sub %[dly], #1" "\n\t"
"bne L%=_loop_delay_T0H" "\n\t"
"strb %[bitmask], [%[reg], #4]" "\n\t"
"movs %[dly], #13" "\n\t"
"L%=_loop_delay_T0L:" "\n\t"
"sub %[dly], #1" "\n\t"
"bne L%=_loop_delay_T0L" "\n\t"
"b L%=_next" "\n\t"
"L%=_loop_one:"
"strb %[bitmask], [%[reg], #0]" "\n\t"
"movs %[dly], #13" "\n\t"
"L%=_loop_delay_T1H:" "\n\t"
"sub %[dly], #1" "\n\t"
"bne L%=_loop_delay_T1H" "\n\t"
"strb %[bitmask], [%[reg], #4]" "\n\t"
"movs %[dly], #4" "\n\t"
"L%=_loop_delay_T1L:" "\n\t"
"sub %[dly], #1" "\n\t"
"bne L%=_loop_delay_T1L" "\n\t"
"nop" "\n\t"
"L%=_next:" "\n\t"
"sub %[count], #1" "\n\t"
"bne L%=_loop" "\n\t"
"lsl %[pix], #1" "\n\t"
"bcs L%=_last_one" "\n\t"
"L%=_last_zero:"
"strb %[bitmask], [%[reg], #0]" "\n\t"
"movs %[dly], #4" "\n\t"
"L%=_last_delay_T0H:" "\n\t"
"sub %[dly], #1" "\n\t"
"bne L%=_last_delay_T0H" "\n\t"
"strb %[bitmask], [%[reg], #4]" "\n\t"
"movs %[dly], #10" "\n\t"
"L%=_last_delay_T0L:" "\n\t"
"sub %[dly], #1" "\n\t"
"bne L%=_last_delay_T0L" "\n\t"
"b L%=_repeat" "\n\t"
"L%=_last_one:"
"strb %[bitmask], [%[reg], #0]" "\n\t"
"movs %[dly], #13" "\n\t"
"L%=_last_delay_T1H:" "\n\t"
"sub %[dly], #1" "\n\t"
"bne L%=_last_delay_T1H" "\n\t"
"strb %[bitmask], [%[reg], #4]" "\n\t"
"movs %[dly], #1" "\n\t"
"L%=_last_delay_T1L:" "\n\t"
"sub %[dly], #1" "\n\t"
"bne L%=_last_delay_T1L" "\n\t"
"nop" "\n\t"
"L%=_repeat:" "\n\t"
"add %[p], #1" "\n\t"
"sub %[num], #1" "\n\t"
"bne L%=_begin" "\n\t"
"L%=_done:" "\n\t"
: [p] "+r" (p),
[pix] "=&r" (pix),
[count] "=&r" (count),
[dly] "=&r" (dly),
[num] "+r" (num)
: [bitmask] "r" (bitmask),
[reg] "r" (reg)
);
}
};
typedef NeoArmMethodBase<NeoArmMk26z64Speed800Kbps> NeoArm800KbpsMethod;
#else
#error "Teensy-LC: Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz"
#endif // F_CPU == 48000000
#elif defined(__SAMD21G18A__) // Arduino Zero
class NeoArmSamd21g18aSpeedProps800Kbps
{
public:
static void BitPreWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop;");
}
static void BitT1hWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;");
}
static void BitT0lWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;");
}
static void BitPostWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;");
}
};
class NeoArmSamd21g18aSpeedProps400Kbps
{
public:
static void BitPreWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");
}
static void BitT1hWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop;");
}
static void BitT0lWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop;");
}
static void BitPostWait()
{
asm("nop; nop; nop; nop; nop; nop; nop;");
}
};
template<typename T_SPEEDPROPS> class NeoArmSamd21g18aSpeedBase
{
public:
static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin)
{
// Tried this with a timer/counter, couldn't quite get adequate
// resolution. So yay, you get a load of goofball NOPs...
uint8_t* ptr = pixels;
uint8_t* end = ptr + sizePixels;;
uint8_t p = *ptr++;
uint8_t bitMask = 0x80;
uint8_t portNum = g_APinDescription[pin].ulPort;
uint32_t pinMask = 1ul << g_APinDescription[pin].ulPin;
volatile uint32_t* set = &(PORT->Group[portNum].OUTSET.reg);
volatile uint32_t* clr = &(PORT->Group[portNum].OUTCLR.reg);
for (;;)
{
*set = pinMask;
T_SPEEDPROPS::BitPreWait();
if (p & bitMask)
{
T_SPEEDPROPS::BitT1hWait();
*clr = pinMask;
}
else
{
*clr = pinMask;
T_SPEEDPROPS::BitT0lWait();
}
if (bitMask >>= 1)
{
T_SPEEDPROPS::BitPostWait();
}
else
{
if (ptr >= end)
{
break;
}
p = *ptr++;
bitMask = 0x80;
}
}
}
};
typedef NeoArmMethodBase<NeoArmSamd21g18aSpeedBase<NeoArmSamd21g18aSpeedProps800Kbps>> NeoArm800KbpsMethod;
typedef NeoArmMethodBase<NeoArmSamd21g18aSpeedBase<NeoArmSamd21g18aSpeedProps400Kbps>> NeoArm400KbpsMethod;
#elif defined (ARDUINO_STM32_FEATHER) // FEATHER WICED (120MHz)
class NeoArmStm32SpeedProps800Kbps
{
static void BitT1hWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop;");
}
static void BitT1lWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop;");
}
static void BitT0hWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop;");
}
static void BitT0lWait()
{
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;");
}
};
/* TODO - not found in Adafruit library
class NeoArmStm32SpeedProps400Kbps
{
static void BitT1hWait()
{
}
static void BitT1lWait()
{
}
static void BitT0hWait()
{
}
static void BitT0lWait()
{
}
};
*/
template<typename T_SPEEDPROPS> class NeoArmStm32SpeedBase
{
static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin)
{
// Tried this with a timer/counter, couldn't quite get adequate
// resolution. So yay, you get a load of goofball NOPs...
uint8_t* ptr = pixels;
uint8_t* end = ptr + sizePixels;
uint8_t p = *ptr++;
uint8_t bitMask = 0x80;
uint32_t pinMask = BIT(PIN_MAP[pin].gpio_bit);
volatile uint16_t* set = &(PIN_MAP[pin].gpio_device->regs->BSRRL);
volatile uint16_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH);
for (;;)
{
if (p & bitMask)
{
// ONE
// High 800ns
*set = pinMask;
T_SPEEDPROPS::BitT1hWait();
// Low 450ns
*clr = pinMask;
T_SPEEDPROPS::BitT1lWait();
}
else
{
// ZERO
// High 400ns
*set = pinMask;
T_SPEEDPROPS::BitT0hWait();
// Low 850ns
*clr = pinMask;
T_SPEEDPROPS::BitT0lWait();
}
if (bitMask >>= 1)
{
// Move on to the next pixel
asm("nop;");
}
else
{
if (ptr >= end)
{
break;
}
p = *ptr++;
bitMask = 0x80;
}
}
}
};
typedef NeoArmMethodBase<NeoArmStm32SpeedBase<NeoArmStm32SpeedProps800Kbps>> NeoArm800KbpsMethod;
#else // Other ARM architecture -- Presumed Arduino Due
#define ARM_OTHER_SCALE VARIANT_MCK / 2UL / 1000000UL
#define ARM_OTHER_INST (2UL * F_CPU / VARIANT_MCK)
class NeoArmOtherSpeedProps800Kbps
{
public:
static const uint32_t CyclesT0h = ((uint32_t)(0.40 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
static const uint32_t CyclesT1h = ((uint32_t)(0.80 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
static const uint32_t Cycles = ((uint32_t)(1.25 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
};
class NeoArmOtherSpeedProps400Kbps
{
public:
static const uint32_t CyclesT0h = ((uint32_t)(0.50 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
static const uint32_t CyclesT1h = ((uint32_t)(1.20 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
static const uint32_t Cycles = ((uint32_t)(2.50 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
};
template<typename T_SPEEDPROPS> class NeoArmOtherSpeedBase
{
public:
static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin)
{
uint32_t pinMask;
uint32_t t;
Pio* port;
volatile WoReg* portSet;
volatile WoReg* portClear;
volatile WoReg* timeValue;
volatile WoReg* timeReset;
uint8_t* p;
uint8_t* end;
uint8_t pix;
uint8_t mask;
pmc_set_writeprotect(false);
pmc_enable_periph_clk((uint32_t)TC3_IRQn);
TC_Configure(TC1, 0,
TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1);
TC_Start(TC1, 0);
pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into
port = g_APinDescription[pin].pPort; // declarations above. Want to
portSet = &(port->PIO_SODR); // burn a few cycles after
portClear = &(port->PIO_CODR); // starting timer to minimize
timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'.
timeReset = &(TC1->TC_CHANNEL[0].TC_CCR);
p = pixels;
end = p + sizePixels;
pix = *p++;
mask = 0x80;
for (;;)
{
if (pix & mask)
{
t = T_SPEEDPROPS::CyclesT1h;
}
else
{
t = T_SPEEDPROPS::CyclesT0h;
}
// wait for the end of the previous cycle
while (*timeValue < T_SPEEDPROPS::Cycles);
*portSet = pinMask;
*timeReset = TC_CCR_CLKEN | TC_CCR_SWTRG;
while (*timeValue < t);
*portClear = pinMask;
if (!(mask >>= 1))
{
// This 'inside-out' loop logic utilizes
if (p >= end)
{
break; // idle time to minimize inter-byte delays.
}
pix = *p++;
mask = 0x80;
}
}
// not really needed as the wait for latch does this and
// while (*timeValue < T_SPEEDPROPS::Cycles); // Wait for last bit
TC_Stop(TC1, 0);
}
};
typedef NeoArmMethodBase<NeoArmOtherSpeedBase<NeoArmOtherSpeedProps800Kbps>> NeoArm800KbpsMethod;
typedef NeoArmMethodBase<NeoArmOtherSpeedBase<NeoArmOtherSpeedProps400Kbps>> NeoArm400KbpsMethod;
#endif
// Arm doesn't have alternatives methods yet, so only one to make the default
typedef NeoArm800KbpsMethod Neo800KbpsMethod;
#ifdef NeoArm400KbpsMethod // this is needed due to missing 400Kbps for some platforms
typedef NeoArm400KbpsMethod Neo400KbpsMethod;
#endif
#endif // defined(__arm__)

184
src/NeoAvrMethod.h Normal file
View File

@@ -0,0 +1,184 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for Atmel AVR.
Written by Michael C. Miller.
Some work taken from the Adafruit NeoPixel library.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#ifdef ARDUINO_ARCH_AVR
extern "C"
{
void send_pixels_8mhz_800_PortD(uint8_t* pixels, size_t sizePixels, uint8_t pinMask);
void send_pixels_8mhz_800_PortB(uint8_t* pixels, size_t sizePixels, uint8_t pinMask);
void send_pixels_8mhz_400(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask);
void send_pixels_12mhz_800_PortD(uint8_t* pixels, size_t sizePixels, uint8_t pinMask);
void send_pixels_12mhz_800_PortB(uint8_t* pixels, size_t sizePixels, uint8_t pinMask);
void send_pixels_12mhz_400(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask);
void send_pixels_16mhz_800(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask);
void send_pixels_16mhz_400(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask);
}
class NeoAvrSpeed800Kbps
{
public:
static void send_pixels(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask)
{
#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
#ifdef PORTD // PORTD isn't present on ATtiny85, etc.
if (port == &PORTD)
send_pixels_8mhz_800_PortD(pixels, sizePixels, pinMask);
else if (port == &PORTB)
#endif // PORTD
send_pixels_8mhz_800_PortB(pixels, sizePixels, pinMask);
#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
#ifdef PORTD // PORTD
if (port == &PORTD)
send_pixels_12mhz_800_PortD(pixels, sizePixels, pinMask);
else if (port == &PORTB)
#endif // PORTD
send_pixels_12mhz_800_PortB(pixels, sizePixels, pinMask);
#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L) // 16Mhz CPU
send_pixels_16mhz_800(pixels, sizePixels, port, pinMask);
#else
#error "CPU SPEED NOT SUPPORTED"
#endif
}
};
class NeoAvrSpeed400Kbps
{
public:
static void send_pixels(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask)
{
#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
send_pixels_8mhz_400(pixels, sizePixels, port, pinMask);
#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
send_pixels_12mhz_400(pixels, sizePixels, port, pinMask);
#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L) // 16Mhz CPU
send_pixels_16mhz_400(pixels, sizePixels, port, pinMask);
#else
#error "CPU SPEED NOT SUPPORTED"
#endif
}
};
template<typename T_SPEED> class NeoAvrMethodBase
{
public:
NeoAvrMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize) :
_pin(pin),
_port(NULL),
_pinMask(0)
{
pinMode(pin, OUTPUT);
_sizePixels = pixelCount * elementSize;
_pixels = (uint8_t*)malloc(_sizePixels);
memset(_pixels, 0, _sizePixels);
_port = portOutputRegister(digitalPinToPort(pin));
_pinMask = digitalPinToBitMask(pin);
}
~NeoAvrMethodBase()
{
pinMode(_pin, INPUT);
free(_pixels);
}
bool IsReadyToUpdate() const
{
uint32_t delta = micros() - _endTime;
return (delta >= 50L);
}
void Initialize()
{
digitalWrite(_pin, LOW);
_endTime = micros();
}
void Update()
{
// Data latch = 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.
while (!IsReadyToUpdate())
{
#if !defined(ARDUINO_TEEONARDU_LEO) && !defined(ARDUINO_TEEONARDU_FLORA)
yield(); // allows for system yield if needed
#endif
}
noInterrupts(); // Need 100% focus on instruction timing
T_SPEED::send_pixels(_pixels, _sizePixels, _port, _pinMask);
interrupts();
// save EOD time for latch on next call
_endTime = micros();
}
uint8_t* getPixels() const
{
return _pixels;
};
size_t getPixelsSize() const
{
return _sizePixels;
};
private:
uint32_t _endTime; // Latch timing reference
size_t _sizePixels; // Size of '_pixels' buffer below
uint8_t* _pixels; // Holds LED color values
uint8_t _pin; // output pin number
volatile uint8_t* _port; // Output PORT register
uint8_t _pinMask; // Output PORT bitmask
};
typedef NeoAvrMethodBase<NeoAvrSpeed800Kbps> NeoAvr800KbpsMethod;
typedef NeoAvrMethodBase<NeoAvrSpeed400Kbps> NeoAvr400KbpsMethod;
// AVR doesn't have alternatives yet, so there is just the default
typedef NeoAvr800KbpsMethod Neo800KbpsMethod;
typedef NeoAvr400KbpsMethod Neo400KbpsMethod;
#endif

158
src/NeoColorFeatures.h Normal file
View File

@@ -0,0 +1,158 @@
/*-------------------------------------------------------------------------
NeoPixelFeatures provides feature classes to describe color order and
color depth for NeoPixelBus template class
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
class Neo3Elements
{
public:
static const size_t PixelSize = 3;
static const RgbColor Empty();
static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel)
{
return &pPixels[indexPixel * PixelSize];
}
typedef RgbColor ColorObject;
};
class Neo4Elements
{
public:
static const size_t PixelSize = 4;
static const RgbColor Empty();
static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel)
{
return &pPixels[indexPixel * PixelSize];
}
typedef RgbwColor ColorObject;
};
class NeoGrbFeature : public Neo3Elements
{
public:
static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, Neo3Elements::ColorObject color)
{
uint8_t* p = getPixelAddress(pPixels, indexPixel);
*p++ = color.G;
*p++ = color.R;
*p = color.B;
}
static Neo3Elements::ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel)
{
Neo3Elements::ColorObject color;
uint8_t* p = getPixelAddress(pPixels, indexPixel);
color.G = *p++;
color.R = *p++;
color.B = *p;
return color;
}
};
class NeoRgbwFeature : public Neo4Elements
{
public:
static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, Neo4Elements::ColorObject color)
{
uint8_t* p = getPixelAddress(pPixels, indexPixel);
*p++ = color.R;
*p++ = color.G;
*p++ = color.B;
*p = color.W;
}
static Neo4Elements::ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel)
{
Neo4Elements::ColorObject color;
uint8_t* p = getPixelAddress(pPixels, indexPixel);
color.R = *p++;
color.G = *p++;
color.B = *p++;
color.W = *p;
return color;
}
};
class NeoRgbFeature : public Neo3Elements
{
public:
static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, Neo3Elements::ColorObject color)
{
uint8_t* p = getPixelAddress(pPixels, indexPixel);
*p++ = color.R;
*p++ = color.G;
*p = color.B;
}
static Neo3Elements::ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel)
{
Neo3Elements::ColorObject color;
uint8_t* p = getPixelAddress(pPixels, indexPixel);
color.R = *p++;
color.G = *p++;
color.B = *p;
return color;
}
};
class NeoBrgFeature : public Neo3Elements
{
public:
static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, Neo3Elements::ColorObject color)
{
uint8_t* p = getPixelAddress(pPixels, indexPixel);
*p++ = color.B;
*p++ = color.R;
*p = color.G;
}
static Neo3Elements::ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel)
{
Neo3Elements::ColorObject color;
uint8_t* p = getPixelAddress(pPixels, indexPixel);
color.B = *p++;
color.R = *p++;
color.G = *p;
return color;
}
};

214
src/NeoEase.h Normal file
View File

@@ -0,0 +1,214 @@
/*-------------------------------------------------------------------------
NeoEase provides animation curve equations for animation support.
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#ifdef ARDUINO_ARCH_AVR
typedef float(*AnimEaseFunction)(float linear);
#else
#undef max
#undef min
#include <functional>
typedef std::function<float(float linear)> AnimEaseFunction;
#endif
class NeoEase
{
public:
static float QuadraticIn(float linear)
{
return linear * linear;
}
static float QuadraticOut(float linear)
{
return (-linear * (linear - 2.0f));
}
static float QuadraticInOut(float linear)
{
linear *= 2.0f;
if (linear < 1.0f)
{
return (0.5f * linear * linear);
}
else
{
linear -= 1.0f;
return (-0.5f * (linear * (linear - 2.0f) - 1.0f));
}
}
static float CubicIn(float linear)
{
return (linear * linear * linear);
}
static float CubicOut(float linear)
{
linear -= 1.0f;
return (linear * linear * linear + 1);
}
static float CubicInOut(float linear)
{
linear *= 2.0f;
if (linear < 1.0f)
{
return (0.5f * linear * linear * linear);
}
else
{
linear -= 2.0f;
return (0.5f * (linear * linear * linear + 2.0f));
}
}
static float QuarticIn(float linear)
{
return (linear * linear * linear * linear);
}
static float QuarticOut(float linear)
{
linear -= 1.0f;
return -(linear * linear * linear * linear - 1);
}
static float QuarticInOut(float linear)
{
linear *= 2.0f;
if (linear < 1.0f)
{
return (0.5f * linear * linear * linear * linear);
}
else
{
linear -= 2.0f;
return (-0.5f * (linear * linear * linear * linear - 2.0f));
}
}
static float QuinticIn(float linear)
{
return (linear * linear * linear * linear * linear);
}
static float QuinticOut(float linear)
{
linear -= 1.0f;
return (linear * linear * linear * linear * linear + 1.0f);
}
static float QuinticInOut(float linear)
{
linear *= 2.0f;
if (linear < 1.0f)
{
return (0.5f * linear * linear * linear * linear * linear);
}
else
{
linear -= 2.0f;
return (0.5f * (linear * linear * linear * linear * linear + 2.0f));
}
}
static float SinusoidalIn(float linear)
{
return (-cos(linear * HALF_PI) + 1.0f);
}
static float SinusoidalOut(float linear)
{
return (sin(linear * HALF_PI));
}
static float SinusoidalInOut(float linear)
{
return -0.5 * (cos(PI * linear) - 1.0f);
}
static float ExponentialIn(float linear)
{
return (pow(2, 10.0f * (linear - 1.0f)));
}
static float ExponentialOut(float linear)
{
return (-pow(2, -10.0f * linear) + 1.0f);
}
static float ExponentialInOut(float linear)
{
linear *= 2.0f;
if (linear < 1.0f)
{
return (0.5f * pow(2, 10.0f * (linear - 1.0f)));
}
else
{
linear -= 1.0f;
return (0.5f * (-pow(2, -10.0f * linear) + 2.0f));
}
}
static float CircularIn(float linear)
{
if (linear == 1.0f)
{
return 1.0f;
}
else
{
return (-(sqrt(1.0f - linear * linear) - 1.0f));
}
}
static float CircularOut(float linear)
{
linear -= 1.0f;
return (sqrt(1.0f - linear * linear));
}
static float CircularInOut(float linear)
{
linear *= 2.0f;
if (linear < 1.0f)
{
return (-0.5f * (sqrt(1.0f - linear * linear) - 1));
}
else
{
linear -= 2.0f;
return (0.5f * (sqrt(1.0f - linear * linear) + 1.0f));
}
}
};

View File

@@ -0,0 +1,131 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for Esp8266.
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#ifdef ARDUINO_ARCH_ESP8266
// due to linker overriding the ICACHE_RAM_ATTR for cpp files, these methods are
// moved into a C file so the attribute will be applied correctly
extern "C" void ICACHE_RAM_ATTR bitbang_send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin);
extern "C" void ICACHE_RAM_ATTR bitbang_send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin);
class NeoEsp8266BitBangSpeed800Kbps
{
public:
static void send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin)
{
bitbang_send_pixels_800(pixels, end, pin);
}
};
class NeoEsp8266BitBangSpeed400Kbps
{
public:
static void send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin)
{
bitbang_send_pixels_400(pixels, end, pin);
}
};
template<typename T_SPEED> class NeoEsp8266BitBangMethodBase
{
public:
NeoEsp8266BitBangMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize) :
_pin(pin)
{
pinMode(pin, OUTPUT);
_sizePixels = pixelCount * elementSize;
_pixels = (uint8_t*)malloc(_sizePixels);
memset(_pixels, 0, _sizePixels);
}
~NeoEsp8266BitBangMethodBase()
{
pinMode(_pin, INPUT);
free(_pixels);
}
bool IsReadyToUpdate() const
{
uint32_t delta = micros() - _endTime;
return (delta >= 50L);
}
void Initialize()
{
digitalWrite(_pin, LOW);
_endTime = micros();
}
void Update()
{
// Data latch = 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.
while (!IsReadyToUpdate())
{
yield(); // allows for system yield if needed
}
noInterrupts(); // Need 100% focus on instruction timing
T_SPEED::send_pixels(_pixels, _pixels + _sizePixels, _pin);
interrupts();
// save EOD time for latch on next call
_endTime = micros();
}
uint8_t* getPixels() const
{
return _pixels;
};
size_t getPixelsSize() const
{
return _sizePixels;
};
private:
uint32_t _endTime; // Latch timing reference
size_t _sizePixels; // Size of '_pixels' buffer below
uint8_t* _pixels; // Holds LED color values
uint8_t _pin; // output pin number
};
typedef NeoEsp8266BitBangMethodBase<NeoEsp8266BitBangSpeed800Kbps> NeoEsp8266BitBang800KbpsMethod;
typedef NeoEsp8266BitBangMethodBase<NeoEsp8266BitBangSpeed400Kbps> NeoEsp8266BitBang400KbpsMethod;
#endif

337
src/NeoEsp8266DmaMethod.h Normal file
View File

@@ -0,0 +1,337 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for Esp8266.
Written by Michael C. Miller.
Thanks to g3gg0.de for porting the initial DMA support.
Thanks to github/cnlohr for the original work on DMA support, which is
located at https://github.com/cnlohr/esp8266ws2812i2s.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#ifdef ARDUINO_ARCH_ESP8266
extern "C"
{
#include "Arduino.h"
#include "osapi.h"
#include "ets_sys.h"
#include "i2s_reg.h"
#include "i2s.h"
#include "eagle_soc.h"
#include "esp8266_peri.h"
#include "slc_register.h"
#include "osapi.h"
#include "ets_sys.h"
#include "user_interface.h"
void rom_i2c_writeReg_Mask(uint32_t block, uint32_t host_id, uint32_t reg_add, uint32_t Msb, uint32_t Lsb, uint32_t indata);
}
struct slc_queue_item
{
uint32 blocksize : 12;
uint32 datalen : 12;
uint32 unused : 5;
uint32 sub_sof : 1;
uint32 eof : 1;
uint32 owner : 1;
uint32 buf_ptr;
uint32 next_link_ptr;
};
class NeoEsp8266DmaSpeed800Kbps
{
public:
const static uint32_t I2sClockDivisor = 3;
const static uint32_t I2sBaseClockDivisor = 16;
};
class NeoEsp8266DmaSpeed400Kbps
{
public:
const static uint32_t I2sClockDivisor = 6;
const static uint32_t I2sBaseClockDivisor = 16;
};
template<typename T_SPEED> class NeoEsp8266DmaMethodBase
{
public:
NeoEsp8266DmaMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize)
{
_sizePixels = pixelCount * elementSize;
_bitBufferSize = CalculateI2sBufferSize(pixelCount, elementSize);
_pixels = (uint8_t*)malloc(_sizePixels);
memset(_pixels, 0x00, _sizePixels);
_i2sBlock = (uint8_t*)malloc(_bitBufferSize);
memset(_i2sBlock, 0x00, _bitBufferSize);
memset(_i2sZeroes, 0x00, sizeof(_i2sZeroes));
}
~NeoEsp8266DmaMethodBase()
{
free(_pixels);
free(_i2sBlock);
}
bool IsReadyToUpdate() const
{
return IsStoppedDmaState();
}
void Initialize()
{
InitializeDma();
}
void InitializeDma()
{
// reset DMA registers
SET_PERI_REG_MASK(SLC_CONF0, SLC_RXLINK_RST);
CLEAR_PERI_REG_MASK(SLC_CONF0, SLC_RXLINK_RST);
// clear all interrupt flags
SET_PERI_REG_MASK(SLC_INT_CLR, 0xffffffff);
// set up DMA
CLEAR_PERI_REG_MASK(SLC_CONF0, (SLC_MODE << SLC_MODE_S));
SET_PERI_REG_MASK(SLC_CONF0, (1 << SLC_MODE_S));
SET_PERI_REG_MASK(SLC_RX_DSCR_CONF, SLC_INFOR_NO_REPLACE | SLC_TOKEN_NO_REPLACE);
CLEAR_PERI_REG_MASK(SLC_RX_DSCR_CONF, SLC_RX_FILL_EN | SLC_RX_EOF_MODE | SLC_RX_FILL_MODE);
// prepare linked DMA descriptors, having EOF set for all
_i2sBufDescOut.owner = 1;
_i2sBufDescOut.eof = 1;
_i2sBufDescOut.sub_sof = 0;
_i2sBufDescOut.datalen = _bitBufferSize;
_i2sBufDescOut.blocksize = _bitBufferSize;
_i2sBufDescOut.buf_ptr = (uint32_t)_i2sBlock;
_i2sBufDescOut.unused = 0;
_i2sBufDescOut.next_link_ptr = (uint32_t)&_i2sBufDescLatch;
// this zero-buffer block implements the latch/reset signal
_i2sBufDescLatch.owner = 1;
_i2sBufDescLatch.eof = 1;
_i2sBufDescLatch.sub_sof = 0;
_i2sBufDescLatch.datalen = sizeof(_i2sZeroes);
_i2sBufDescLatch.blocksize = sizeof(_i2sZeroes);
_i2sBufDescLatch.buf_ptr = (uint32_t)_i2sZeroes;
_i2sBufDescLatch.unused = 0;
_i2sBufDescLatch.next_link_ptr = (uint32_t)&_i2sBufDescStopped;
// this empty block will stop the output and provide a flag that latch/reset has been sent
// it basically loops
_i2sBufDescStopped.owner = 1;
_i2sBufDescStopped.eof = 1;
_i2sBufDescStopped.sub_sof = 0;
_i2sBufDescStopped.datalen = sizeof(_i2sZeroes);
_i2sBufDescStopped.blocksize = sizeof(_i2sZeroes);
_i2sBufDescStopped.buf_ptr = (uint32_t)_i2sZeroes;;
_i2sBufDescStopped.unused = 0;
_i2sBufDescStopped.next_link_ptr = (uint32_t)&_i2sBufDescStopped;
// configure the first descriptor
// TX_LINK isnt used, but has to be configured
CLEAR_PERI_REG_MASK(SLC_RX_LINK, SLC_RXLINK_DESCADDR_MASK);
CLEAR_PERI_REG_MASK(SLC_TX_LINK, SLC_TXLINK_DESCADDR_MASK);
SET_PERI_REG_MASK(SLC_RX_LINK, ((uint32)&_i2sBufDescOut) & SLC_RXLINK_DESCADDR_MASK);
SET_PERI_REG_MASK(SLC_TX_LINK, ((uint32)&_i2sBufDescOut) & SLC_TXLINK_DESCADDR_MASK);
// we dont need interrupts
ETS_SLC_INTR_DISABLE();
WRITE_PERI_REG(SLC_INT_CLR, 0xffffffff);
// configure RDX0/GPIO3 for output. it is the only supported pin unfortunately.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_I2SO_DATA);
// configure I2S subsystem
I2S_CLK_ENABLE();
CLEAR_PERI_REG_MASK(I2SCONF, I2S_I2S_RESET_MASK);
SET_PERI_REG_MASK(I2SCONF, I2S_I2S_RESET_MASK);
CLEAR_PERI_REG_MASK(I2SCONF, I2S_I2S_RESET_MASK);
// Select 16bits per channel (FIFO_MOD=0), no DMA access (FIFO only)
CLEAR_PERI_REG_MASK(I2S_FIFO_CONF, I2S_I2S_DSCR_EN |
(I2S_I2S_RX_FIFO_MOD << I2S_I2S_RX_FIFO_MOD_S) |
(I2S_I2S_TX_FIFO_MOD << I2S_I2S_TX_FIFO_MOD_S));
// Enable DMA in i2s subsystem
SET_PERI_REG_MASK(I2S_FIFO_CONF, I2S_I2S_DSCR_EN);
// configure the rates
CLEAR_PERI_REG_MASK(I2SCONF, I2S_TRANS_SLAVE_MOD |
(I2S_BITS_MOD << I2S_BITS_MOD_S) |
(I2S_BCK_DIV_NUM << I2S_BCK_DIV_NUM_S) |
(I2S_CLKM_DIV_NUM << I2S_CLKM_DIV_NUM_S));
SET_PERI_REG_MASK(I2SCONF, I2S_RIGHT_FIRST | I2S_MSB_RIGHT | I2S_RECE_SLAVE_MOD |
I2S_RECE_MSB_SHIFT | I2S_TRANS_MSB_SHIFT |
((T_SPEED::I2sBaseClockDivisor & I2S_BCK_DIV_NUM) << I2S_BCK_DIV_NUM_S) |
((T_SPEED::I2sClockDivisor & I2S_CLKM_DIV_NUM) << I2S_CLKM_DIV_NUM_S));
// disable all interrupts
SET_PERI_REG_MASK(I2SINT_CLR, I2S_I2S_RX_WFULL_INT_CLR |
I2S_I2S_PUT_DATA_INT_CLR |
I2S_I2S_TAKE_DATA_INT_CLR);
// dma is now ready to start
}
void Update()
{
FillBuffers();
// wait for latch to complete if it hasn't already
NullWait();
StopDma();
StartDma();
}
uint8_t* getPixels() const
{
return _pixels;
};
size_t getPixelsSize() const
{
return _sizePixels;
}
private:
static uint32_t CalculateI2sBufferSize(uint16_t pixelCount, size_t elementSize)
{
// 4 I2S bytes per pixels byte
return ((uint32_t)pixelCount * elementSize * 4);
}
void FillBuffers()
{
const uint16_t bitpatterns[16] =
{
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110,
0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110,
0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110,
0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110,
};
// wait for the data to be done transmission before updating,
// it may still be sending the latch/reset though, buts OK
SyncWait();
// now it is transferring blank area, so it is safe to update dma buffers
uint16_t* pDma = (uint16_t*)_i2sBlock;
uint8_t* pPixelsEnd = _pixels + _sizePixels;
for (uint8_t* pPixel = _pixels; pPixel < pPixelsEnd; pPixel++)
{
*(pDma++) = bitpatterns[((*pPixel) & 0x0f)];
*(pDma++) = bitpatterns[((*pPixel) >> 4) & 0x0f];
}
}
void StopDma()
{
SET_PERI_REG_MASK(SLC_RX_LINK, SLC_RXLINK_STOP);
}
void StartDma()
{
// clear all interrupt flags
SET_PERI_REG_MASK(SLC_INT_CLR, 0xffffffff);
// configure the first descriptor
// TX_LINK isnt used, but has to be configured
CLEAR_PERI_REG_MASK(SLC_RX_LINK, SLC_RXLINK_DESCADDR_MASK);
CLEAR_PERI_REG_MASK(SLC_TX_LINK, SLC_TXLINK_DESCADDR_MASK);
SET_PERI_REG_MASK(SLC_RX_LINK, ((uint32)&_i2sBufDescOut) & SLC_RXLINK_DESCADDR_MASK);
SET_PERI_REG_MASK(SLC_TX_LINK, ((uint32)&_i2sBufDescOut) & SLC_TXLINK_DESCADDR_MASK);
WRITE_PERI_REG(SLC_INT_CLR, 0xffffffff);
// start RX link
SET_PERI_REG_MASK(SLC_RX_LINK, SLC_RXLINK_START);
// fire the machine again
SET_PERI_REG_MASK(I2SCONF, I2S_I2S_TX_START);
}
void NullWait() const
{
while (!IsStoppedDmaState())
{
// due to how long the data send could be (300 pixels is 9ms)
// we will yield while we wait
yield();
}
// okay right now we are not sending anything
}
void SyncWait() const
{
// poll for SLC_RX_EOF_DES_ADDR getting set to anything other than
// the buffer with pixel data
while (READ_PERI_REG(SLC_RX_EOF_DES_ADDR) == (uint32_t)&_i2sBufDescLatch)
{
WRITE_PERI_REG(SLC_RX_EOF_DES_ADDR, 0xffffffff);
// due to how long the data send could be (300 pixels is 9ms)
// we will yield while we wait
yield();
}
// okay right now we are somewhere in the blank "latch/reset" section or
// stopped
}
bool IsStoppedDmaState() const
{
uint32_t state = READ_PERI_REG(SLC_RX_EOF_DES_ADDR);
return (state == 0 || state == (uint32_t)&_i2sBufDescStopped);
}
size_t _sizePixels; // Size of '_pixels' buffer below
uint8_t* _pixels; // Holds LED color values
struct slc_queue_item _i2sBufDescOut;
struct slc_queue_item _i2sBufDescLatch;
struct slc_queue_item _i2sBufDescStopped;
uint32_t _bitBufferSize;
uint8_t* _i2sBlock;
uint8_t _i2sZeroes[24]; // 24 bytes creates the minimum 50us latch per spec
};
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaSpeed800Kbps> NeoEsp8266Dma800KbpsMethod;
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaSpeed400Kbps> NeoEsp8266Dma400KbpsMethod;
// Dma method is the default method for Esp8266
typedef NeoEsp8266Dma800KbpsMethod Neo800KbpsMethod;
typedef NeoEsp8266Dma400KbpsMethod Neo400KbpsMethod;
#endif

137
src/NeoEsp8266UartMethod.h Normal file
View File

@@ -0,0 +1,137 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for Esp8266.
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#ifdef ARDUINO_ARCH_ESP8266
extern "C"
{
#include "eagle_soc.h"
#include "uart_register.h"
}
// due to linker overriding ICACHE_RAM_ATTR for cpp files, this function was
// moved into a NeoPixelEsp8266.c file.
extern "C" void ICACHE_RAM_ATTR esp8266_uart1_send_pixels(uint8_t* pixels, uint8_t* end);
class NeoEsp8266UartSpeed800Kbps
{
public:
static const uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800mhz speed
static const uint32_t UartBaud = 3200000; // 800mhz, 4 serial bytes per NeoByte
};
class NeoEsp8266UartSpeed400Kbps
{
public:
static const uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400mhz speed
static const uint32_t UartBaud = 1600000; // 400mhz, 4 serial bytes per NeoByte
};
#define UART1 1
#define UART1_INV_MASK (0x3f << 19)
template<typename T_SPEED> class NeoEsp8266UartMethodBase
{
public:
NeoEsp8266UartMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize)
{
_sizePixels = pixelCount * elementSize;
_pixels = (uint8_t*)malloc(_sizePixels);
memset(_pixels, 0x00, _sizePixels);
}
~NeoEsp8266UartMethodBase()
{
free(_pixels);
}
bool IsReadyToUpdate() const
{
uint32_t delta = micros() - _endTime;
return (delta >= 50L && delta <= (4294967296L - getPixelTime()));
}
void Initialize()
{
Serial1.begin(T_SPEED::UartBaud, SERIAL_6N1, SERIAL_TX_ONLY);
CLEAR_PERI_REG_MASK(UART_CONF0(UART1), UART1_INV_MASK);
SET_PERI_REG_MASK(UART_CONF0(UART1), (BIT(22)));
_endTime = micros();
}
void Update()
{
// Data latch = 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.
while (!IsReadyToUpdate())
{
yield();
}
// since uart is async buffer send, we have to calc the endtime that it will take
// to correctly manage the data latch in the above code
// add the calculated time to the current time
_endTime = micros() + getPixelTime();
// esp hardware uart sending of data
esp8266_uart1_send_pixels(_pixels, _pixels + _sizePixels);
}
uint8_t* getPixels() const
{
return _pixels;
};
size_t getPixelsSize() const
{
return _sizePixels;
};
private:
uint32_t getPixelTime() const
{
return (T_SPEED::ByteSendTimeUs * _sizePixels);
};
size_t _sizePixels; // Size of '_pixels' buffer below
uint8_t* _pixels; // Holds LED color values
uint32_t _endTime; // Latch timing reference
};
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps> NeoEsp8266Uart800KbpsMethod;
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps> NeoEsp8266Uart400KbpsMethod;
#endif

164
src/NeoPixelAnimator.cpp Normal file
View File

@@ -0,0 +1,164 @@
/*-------------------------------------------------------------------------
NeoPixelAnimator provides animation timing support.
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "NeoPixelAnimator.h"
NeoPixelAnimator::NeoPixelAnimator(uint16_t countAnimations, uint16_t timeScale) :
_countAnimations(countAnimations),
_animationLastTick(0),
_activeAnimations(0),
_isRunning(true)
{
// due to strange esp include header issues, min and max aren't usable
_timeScale = (timeScale < 1) ? (1) : (timeScale > 32768) ? 32768 : timeScale;
//_timeScale = max(1, min(32768, timeScale));
_animations = new AnimationContext[_countAnimations];
}
NeoPixelAnimator::~NeoPixelAnimator()
{
delete[] _animations;
}
bool NeoPixelAnimator::NextAvailableAnimation(uint16_t* indexAvailable, uint16_t indexStart)
{
if (indexStart >= _countAnimations)
{
// last one
indexStart = _countAnimations - 1;
}
uint16_t next = indexStart;
do
{
if (!IsAnimationActive(next))
{
if (indexAvailable)
{
*indexAvailable = next;
}
return true;
}
next = (next + 1) % _countAnimations;
} while (next != indexStart);
return false;
}
void NeoPixelAnimator::StartAnimation(uint16_t indexAnimation,
uint16_t duration,
AnimUpdateCallback animUpdate)
{
if (indexAnimation >= _countAnimations || animUpdate == NULL)
{
return;
}
if (_activeAnimations == 0)
{
_animationLastTick = millis();
}
StopAnimation(indexAnimation);
// all animations must have at least non zero duration, otherwise
// they are considered stopped
if (duration == 0)
{
duration = 1;
}
_animations[indexAnimation].StartAnimation(duration, animUpdate);
_activeAnimations++;
}
void NeoPixelAnimator::StopAnimation(uint16_t indexAnimation)
{
if (indexAnimation >= _countAnimations)
{
return;
}
if (IsAnimationActive(indexAnimation))
{
_activeAnimations--;
_animations[indexAnimation].StopAnimation();
}
}
void NeoPixelAnimator::UpdateAnimations()
{
if (_isRunning)
{
uint32_t currentTick = millis();
uint32_t delta = currentTick - _animationLastTick;
if (delta >= _timeScale)
{
uint16_t countAnimations = _activeAnimations;
AnimationContext* pAnim;
delta /= _timeScale; // scale delta into animation time
for (uint16_t iAnim = 0; iAnim < _countAnimations && countAnimations > 0; iAnim++)
{
pAnim = &_animations[iAnim];
AnimUpdateCallback fnUpdate = pAnim->_fnCallback;
AnimationParam param;
param.index = iAnim;
if (pAnim->_remaining > delta)
{
param.state = (pAnim->_remaining == pAnim->_duration) ? AnimationState_Started : AnimationState_Progress;
param.progress = (float)(pAnim->_duration - pAnim->_remaining) / (float)pAnim->_duration;
fnUpdate(param);
pAnim->_remaining -= delta;
countAnimations--;
}
else if (pAnim->_remaining > 0)
{
param.state = AnimationState_Completed;
param.progress = 1.0f;
_activeAnimations--;
pAnim->StopAnimation();
fnUpdate(param);
countAnimations--;
}
}
_animationLastTick = currentTick;
}
}
}

167
src/NeoPixelAnimator.h Normal file
View File

@@ -0,0 +1,167 @@
/*-------------------------------------------------------------------------
NeoPixelAnimator provides animation timing support.
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
#include "NeoEase.h"
enum AnimationState
{
AnimationState_Started,
AnimationState_Progress,
AnimationState_Completed
};
struct AnimationParam
{
float progress;
uint16_t index;
AnimationState state;
};
#ifdef ARDUINO_ARCH_AVR
typedef void(*AnimUpdateCallback)(const AnimationParam& param);
#else
#undef max
#undef min
#include <functional>
typedef std::function<void(const AnimationParam& param)> AnimUpdateCallback;
#endif
#define NEO_MILLISECONDS 1 // ~65 seconds max duration, ms updates
#define NEO_CENTISECONDS 10 // ~10.9 minutes max duration, centisecond updates
#define NEO_DECISECONDS 100 // ~1.8 hours max duration, decisecond updates
#define NEO_SECONDS 1000 // ~18.2 hours max duration, second updates
#define NEO_DECASECONDS 10000 // ~7.5 days, 10 second updates
class NeoPixelAnimator
{
public:
NeoPixelAnimator(uint16_t countAnimations, uint16_t timeScale = NEO_MILLISECONDS);
~NeoPixelAnimator();
bool IsAnimating() const
{
return _activeAnimations > 0;
}
bool NextAvailableAnimation(uint16_t* indexAvailable, uint16_t indexStart = 0);
void StartAnimation(uint16_t indexAnimation, uint16_t duration, AnimUpdateCallback animUpdate);
void StopAnimation(uint16_t indexAnimation);
void RestartAnimation(uint16_t indexAnimation)
{
if (indexAnimation >= _countAnimations || _animations[indexAnimation]._duration == 0)
{
return;
}
StartAnimation(indexAnimation, _animations[indexAnimation]._duration, (_animations[indexAnimation]._fnCallback));
}
bool IsAnimationActive(uint16_t indexAnimation) const
{
if (indexAnimation >= _countAnimations)
{
return false;
}
return (IsAnimating() && _animations[indexAnimation]._remaining != 0);
}
uint16_t AnimationDuration(uint16_t indexAnimation)
{
if (indexAnimation >= _countAnimations)
{
return 0;
}
return _animations[indexAnimation]._duration;
}
void UpdateAnimations();
bool IsPaused()
{
return (!_isRunning);
}
void Pause()
{
_isRunning = false;
}
void Resume()
{
_isRunning = true;
_animationLastTick = millis();
}
uint16_t TimeScale()
{
return _timeScale;
}
private:
struct AnimationContext
{
AnimationContext() :
_duration(0),
_remaining(0),
_fnCallback(NULL)
{}
void StartAnimation(uint16_t duration, AnimUpdateCallback animUpdate)
{
_duration = duration;
_remaining = duration;
_fnCallback = animUpdate;
}
void StopAnimation()
{
_remaining = 0;
}
uint16_t _duration;
uint16_t _remaining;
AnimUpdateCallback _fnCallback;
};
uint16_t _countAnimations;
AnimationContext* _animations;
uint32_t _animationLastTick;
uint16_t _activeAnimations;
uint16_t _timeScale;
bool _isRunning;
};

648
src/NeoPixelAvr.c Normal file
View File

@@ -0,0 +1,648 @@
/*-------------------------------------------------------------------------
Arduino library to control a wide variety of WS2811- and WS2812-based RGB
LED devices such as Adafruit FLORA RGB Smart Pixels and NeoPixel strips.
Currently handles 400 and 800 KHz bitstreams on 8, 12 and 16 MHz ATmega
MCUs, with LEDs wired for various color orders. 8 MHz MCUs provide
output on PORTB and PORTD, while 16 MHz chips can handle most output pins
(possible exception with upper PORT registers on the Arduino Mega).
Written by Phil Burgess / Paint Your Dragon for Adafruit Industries,
contributions by PJRC, Michael Miller 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!
-------------------------------------------------------------------------
The contents of this file were taken from the Adafruit NeoPixel library
and modified only to fit within individual calling functions.
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/>.
-------------------------------------------------------------------------*/
// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__)
#include <Arduino.h>
// Hand-tuned assembly code issues data to the LED drivers at a specific
// rate. There's separate code for different CPU speeds (8, 12, 16 MHz)
// for both the WS2811 (400 KHz) and WS2812 (800 KHz) drivers. The
// datastream timing for the LED drivers allows a little wiggle room each
// way (listed in the datasheets), so the conditions for compiling each
// case are set up for a range of frequencies rather than just the exact
// 8, 12 or 16 MHz values, permitting use with some close-but-not-spot-on
// devices (e.g. 16.5 MHz DigiSpark). The ranges were arrived at based
// on the datasheet figures and have not been extensively tested outside
// the canonical 8/12/16 MHz speeds; there's no guarantee these will work
// close to the extremes (or possibly they could be pushed further).
// Keep in mind only one CPU speed case actually gets compiled; the
// resulting program isn't as massive as it might look from source here.
#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
#ifdef PORTD // PORTD isn't present on ATtiny85, etc.
void send_pixels_8mhz_800_PortD(uint8_t* pixels, size_t sizePixels, uint8_t pinMask)
{
volatile size_t i = sizePixels; // Loop counter
volatile uint8_t* ptr = pixels; // Pointer to next byte
volatile uint8_t b = *ptr++; // Current byte value
volatile uint8_t hi; // PORT w/output bit set high
volatile uint8_t lo; // PORT w/output bit set low
volatile uint8_t n1;
volatile n2 = 0; // First, next bits out
// Squeezing an 800 KHz stream out of an 8 MHz chip requires code
// specific to each PORT register. At present this is only written
// to work with pins on PORTD or PORTB, the most likely use case --
// this covers all the pins on the Adafruit Flora and the bulk of
// digital pins on the Arduino Pro 8 MHz (keep in mind, this code
// doesn't even get compiled for 16 MHz boards like the Uno, Mega,
// Leonardo, etc., so don't bother extending this out of hand).
// Additional PORTs could be added if you really need them, just
// duplicate the else and loop and change the PORT. Each add'l
// PORT will require about 150(ish) bytes of program space.
// 10 instruction clocks per bit: HHxxxxxLLL
// OUT instructions: ^ ^ ^ (T=0,2,7)
hi = PORTD | pinMask;
lo = PORTD & ~pinMask;
n1 = lo;
if (b & 0x80)
{
n1 = hi;
}
// Dirty trick: RJMPs proceeding to the next instruction are used
// to delay two clock cycles in one instruction word (rather than
// using two NOPs). This was necessary in order to squeeze the
// loop down to exactly 64 words -- the maximum possible for a
// relative branch.
asm volatile(
"headD:" "\n\t" // Clk Pseudocode
// Bit 7:
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1
"rjmp .+0" "\n\t" // 2 nop nop
"sbrc %[byte] , 6" "\n\t" // 1-2 if(b & 0x40)
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
"rjmp .+0" "\n\t" // 2 nop nop
// Bit 6:
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2
"rjmp .+0" "\n\t" // 2 nop nop
"sbrc %[byte] , 5" "\n\t" // 1-2 if(b & 0x20)
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
"rjmp .+0" "\n\t" // 2 nop nop
// Bit 5:
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1
"rjmp .+0" "\n\t" // 2 nop nop
"sbrc %[byte] , 4" "\n\t" // 1-2 if(b & 0x10)
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
"rjmp .+0" "\n\t" // 2 nop nop
// Bit 4:
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2
"rjmp .+0" "\n\t" // 2 nop nop
"sbrc %[byte] , 3" "\n\t" // 1-2 if(b & 0x08)
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
"rjmp .+0" "\n\t" // 2 nop nop
// Bit 3:
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1
"rjmp .+0" "\n\t" // 2 nop nop
"sbrc %[byte] , 2" "\n\t" // 1-2 if(b & 0x04)
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
"rjmp .+0" "\n\t" // 2 nop nop
// Bit 2:
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2
"rjmp .+0" "\n\t" // 2 nop nop
"sbrc %[byte] , 1" "\n\t" // 1-2 if(b & 0x02)
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
"rjmp .+0" "\n\t" // 2 nop nop
// Bit 1:
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1
"rjmp .+0" "\n\t" // 2 nop nop
"sbrc %[byte] , 0" "\n\t" // 1-2 if(b & 0x01)
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
"sbiw %[count], 1" "\n\t" // 2 i-- (don't act on Z flag yet)
// Bit 0:
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80)
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
"brne headD" "\n" // 2 while(i) (Z flag set above)
: [byte] "+r" (b),
[n1] "+r" (n1),
[n2] "+r" (n2),
[count] "+w" (i)
: [port] "I" (_SFR_IO_ADDR(PORTD)),
[ptr] "e" (ptr),
[hi] "r" (hi),
[lo] "r" (lo) );
}
#endif
void send_pixels_8mhz_800_PortB(uint8_t* pixels, size_t sizePixels, uint8_t pinMask)
{
volatile size_t i = sizePixels; // Loop counter
volatile uint8_t* ptr = pixels; // Pointer to next byte
volatile uint8_t b = *ptr++; // Current byte value
volatile uint8_t hi; // PORT w/output bit set high
volatile uint8_t lo; // PORT w/output bit set low
volatile uint8_t n1;
volatile n2 = 0; // First, next bits out
// Same as above, just switched to PORTB and stripped of comments.
hi = PORTB | pinMask;
lo = PORTB & ~pinMask;
n1 = lo;
if (b & 0x80)
{
n1 = hi;
}
asm volatile(
"headB:" "\n\t"
"out %[port] , %[hi]" "\n\t"
"mov %[n2] , %[lo]" "\n\t"
"out %[port] , %[n1]" "\n\t"
"rjmp .+0" "\n\t"
"sbrc %[byte] , 6" "\n\t"
"mov %[n2] , %[hi]" "\n\t"
"out %[port] , %[lo]" "\n\t"
"rjmp .+0" "\n\t"
"out %[port] , %[hi]" "\n\t"
"mov %[n1] , %[lo]" "\n\t"
"out %[port] , %[n2]" "\n\t"
"rjmp .+0" "\n\t"
"sbrc %[byte] , 5" "\n\t"
"mov %[n1] , %[hi]" "\n\t"
"out %[port] , %[lo]" "\n\t"
"rjmp .+0" "\n\t"
"out %[port] , %[hi]" "\n\t"
"mov %[n2] , %[lo]" "\n\t"
"out %[port] , %[n1]" "\n\t"
"rjmp .+0" "\n\t"
"sbrc %[byte] , 4" "\n\t"
"mov %[n2] , %[hi]" "\n\t"
"out %[port] , %[lo]" "\n\t"
"rjmp .+0" "\n\t"
"out %[port] , %[hi]" "\n\t"
"mov %[n1] , %[lo]" "\n\t"
"out %[port] , %[n2]" "\n\t"
"rjmp .+0" "\n\t"
"sbrc %[byte] , 3" "\n\t"
"mov %[n1] , %[hi]" "\n\t"
"out %[port] , %[lo]" "\n\t"
"rjmp .+0" "\n\t"
"out %[port] , %[hi]" "\n\t"
"mov %[n2] , %[lo]" "\n\t"
"out %[port] , %[n1]" "\n\t"
"rjmp .+0" "\n\t"
"sbrc %[byte] , 2" "\n\t"
"mov %[n2] , %[hi]" "\n\t"
"out %[port] , %[lo]" "\n\t"
"rjmp .+0" "\n\t"
"out %[port] , %[hi]" "\n\t"
"mov %[n1] , %[lo]" "\n\t"
"out %[port] , %[n2]" "\n\t"
"rjmp .+0" "\n\t"
"sbrc %[byte] , 1" "\n\t"
"mov %[n1] , %[hi]" "\n\t"
"out %[port] , %[lo]" "\n\t"
"rjmp .+0" "\n\t"
"out %[port] , %[hi]" "\n\t"
"mov %[n2] , %[lo]" "\n\t"
"out %[port] , %[n1]" "\n\t"
"rjmp .+0" "\n\t"
"sbrc %[byte] , 0" "\n\t"
"mov %[n2] , %[hi]" "\n\t"
"out %[port] , %[lo]" "\n\t"
"sbiw %[count], 1" "\n\t"
"out %[port] , %[hi]" "\n\t"
"mov %[n1] , %[lo]" "\n\t"
"out %[port] , %[n2]" "\n\t"
"ld %[byte] , %a[ptr]+" "\n\t"
"sbrc %[byte] , 7" "\n\t"
"mov %[n1] , %[hi]" "\n\t"
"out %[port] , %[lo]" "\n\t"
"brne headB" "\n"
: [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i)
: [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi),
[lo] "r" (lo));
}
void send_pixels_8mhz_400(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask)
{
volatile size_t i = sizePixels; // Loop counter
volatile uint8_t* ptr = pixels; // Pointer to next byte
volatile uint8_t b = *ptr++; // Current byte value
volatile uint8_t hi; // PORT w/output bit set high
volatile uint8_t lo; // PORT w/output bit set low
// Timing is more relaxed; unrolling the inner loop for each bit is
// not necessary. Still using the peculiar RJMPs as 2X NOPs, not out
// of need but just to trim the code size down a little.
// This 400-KHz-datastream-on-8-MHz-CPU code is not quite identical
// to the 800-on-16 code later -- the hi/lo timing between WS2811 and
// WS2812 is not simply a 2:1 scale!
// 20 inst. clocks per bit: HHHHxxxxxxLLLLLLLLLL
// ST instructions: ^ ^ ^ (T=0,4,10)
volatile uint8_t next, bit;
hi = *port | pinMask;
lo = *port & ~pinMask;
next = lo;
bit = 8;
asm volatile(
"head20:" "\n\t" // Clk Pseudocode (T = 0)
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 6)
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7)
"dec %[bit]" "\n\t" // 1 bit-- (T = 8)
"breq nextbyte20" "\n\t" // 1-2 if(bit == 0)
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12)
"rjmp .+0" "\n\t" // 2 nop nop (T = 14)
"rjmp .+0" "\n\t" // 2 nop nop (T = 16)
"rjmp .+0" "\n\t" // 2 nop nop (T = 18)
"rjmp head20" "\n\t" // 2 -> head20 (next bit out)
"nextbyte20:" "\n\t" // (T = 10)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12)
"nop" "\n\t" // 1 nop (T = 13)
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 14)
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 16)
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 18)
"brne head20" "\n" // 2 if(i != 0) -> (next byte)
: [port] "+e" (port),
[byte] "+r" (b),
[bit] "+r" (bit),
[next] "+r" (next),
[count] "+w" (i)
: [hi] "r" (hi),
[lo] "r" (lo),
[ptr] "e" (ptr));
}
#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
#ifdef PORTD // PORTD isn't present on ATtiny85, etc.
void send_pixels_12mhz_800_PortD(uint8_t* pixels, size_t sizePixels, uint8_t pinMask)
{
volatile size_t i = sizePixels; // Loop counter
volatile uint8_t* ptr = pixels; // Pointer to next byte
volatile uint8_t b = *ptr++; // Current byte value
volatile uint8_t hi; // PORT w/output bit set high
volatile uint8_t lo; // PORT w/output bit set low
// In the 12 MHz case, an optimized 800 KHz datastream (no dead time
// between bytes) requires a PORT-specific loop similar to the 8 MHz
// code (but a little more relaxed in this case).
// 15 instruction clocks per bit: HHHHxxxxxxLLLLL
// OUT instructions: ^ ^ ^ (T=0,4,10)
volatile uint8_t next;
hi = PORTD | pinMask;
lo = PORTD & ~pinMask;
next = lo;
if (b & 0x80) next = hi;
// Don't "optimize" the OUT calls into the bitTime subroutine;
// we're exploiting the RCALL and RET as 3- and 4-cycle NOPs!
asm volatile(
"headD:" "\n\t" // (T = 0)
"out %[port], %[hi]" "\n\t" // (T = 1)
"rcall bitTimeD" "\n\t" // Bit 7 (T = 15)
"out %[port], %[hi]" "\n\t"
"rcall bitTimeD" "\n\t" // Bit 6
"out %[port], %[hi]" "\n\t"
"rcall bitTimeD" "\n\t" // Bit 5
"out %[port], %[hi]" "\n\t"
"rcall bitTimeD" "\n\t" // Bit 4
"out %[port], %[hi]" "\n\t"
"rcall bitTimeD" "\n\t" // Bit 3
"out %[port], %[hi]" "\n\t"
"rcall bitTimeD" "\n\t" // Bit 2
"out %[port], %[hi]" "\n\t"
"rcall bitTimeD" "\n\t" // Bit 1
// Bit 0:
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi (T = 1)
"rjmp .+0" "\n\t" // 2 nop nop (T = 3)
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 5)
"out %[port] , %[next]" "\n\t" // 1 PORT = next (T = 6)
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7)
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) (T = 8)
"mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 9)
"nop" "\n\t" // 1 (T = 10)
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo (T = 11)
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 13)
"brne headD" "\n\t" // 2 if(i != 0) -> (next byte)
"rjmp doneD" "\n\t"
"bitTimeD:" "\n\t" // nop nop nop (T = 4)
"out %[port], %[next]" "\n\t" // 1 PORT = next (T = 5)
"mov %[next], %[lo]" "\n\t" // 1 next = lo (T = 6)
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 7)
"sbrc %[byte], 7" "\n\t" // 1-2 if(b & 0x80) (T = 8)
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 9)
"nop" "\n\t" // 1 (T = 10)
"out %[port], %[lo]" "\n\t" // 1 PORT = lo (T = 11)
"ret" "\n\t" // 4 nop nop nop nop (T = 15)
"doneD:" "\n"
: [byte] "+r" (b),
[next] "+r" (next),
[count] "+w" (i)
: [port] "I" (_SFR_IO_ADDR(PORTD)),
[ptr] "e" (ptr),
[hi] "r" (hi),
[lo] "r" (lo));
}
#endif
void send_pixels_12mhz_800_PortB(uint8_t* pixels, size_t sizePixels, uint8_t pinMask)
{
volatile uint16_t i = (uint16_t)sizePixels; // Loop counter
volatile uint8_t* ptr = pixels; // Pointer to next byte
volatile uint8_t b = *ptr++; // Current byte value
volatile uint8_t hi; // PORT w/output bit set high
volatile uint8_t lo; // PORT w/output bit set low
volatile uint8_t next;
hi = PORTB | pinMask;
lo = PORTB & ~pinMask;
next = lo;
if (b & 0x80)
{
next = hi;
}
// Same as above, just set for PORTB & stripped of comments
asm volatile(
"headB:" "\n\t"
"out %[port], %[hi]" "\n\t"
"rcall bitTimeB" "\n\t"
"out %[port], %[hi]" "\n\t"
"rcall bitTimeB" "\n\t"
"out %[port], %[hi]" "\n\t"
"rcall bitTimeB" "\n\t"
"out %[port], %[hi]" "\n\t"
"rcall bitTimeB" "\n\t"
"out %[port], %[hi]" "\n\t"
"rcall bitTimeB" "\n\t"
"out %[port], %[hi]" "\n\t"
"rcall bitTimeB" "\n\t"
"out %[port], %[hi]" "\n\t"
"rcall bitTimeB" "\n\t"
"out %[port] , %[hi]" "\n\t"
"rjmp .+0" "\n\t"
"ld %[byte] , %a[ptr]+" "\n\t"
"out %[port] , %[next]" "\n\t"
"mov %[next] , %[lo]" "\n\t"
"sbrc %[byte] , 7" "\n\t"
"mov %[next] , %[hi]" "\n\t"
"nop" "\n\t"
"out %[port] , %[lo]" "\n\t"
"sbiw %[count], 1" "\n\t"
"brne headB" "\n\t"
"rjmp doneB" "\n\t"
"bitTimeB:" "\n\t"
"out %[port], %[next]" "\n\t"
"mov %[next], %[lo]" "\n\t"
"rol %[byte]" "\n\t"
"sbrc %[byte], 7" "\n\t"
"mov %[next], %[hi]" "\n\t"
"nop" "\n\t"
"out %[port], %[lo]" "\n\t"
"ret" "\n\t"
"doneB:" "\n"
: [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i)
: [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi),
[lo] "r" (lo));
}
void send_pixels_12mhz_400(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask)
{
volatile uint16_t i = (uint16_t)sizePixels; // Loop counter
volatile uint8_t* ptr = pixels; // Pointer to next byte
volatile uint8_t b = *ptr++; // Current byte value
volatile uint8_t hi; // PORT w/output bit set high
volatile uint8_t lo; // PORT w/output bit set low
// 30 instruction clocks per bit: HHHHHHxxxxxxxxxLLLLLLLLLLLLLLL
// ST instructions: ^ ^ ^ (T=0,6,15)
volatile uint8_t next, bit;
hi = *port | pinMask;
lo = *port & ~pinMask;
next = lo;
bit = 8;
asm volatile(
"head30:" "\n\t" // Clk Pseudocode (T = 0)
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
"rjmp .+0" "\n\t" // 2 nop nop (T = 6)
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 8)
"rjmp .+0" "\n\t" // 2 nop nop (T = 10)
"rjmp .+0" "\n\t" // 2 nop nop (T = 12)
"rjmp .+0" "\n\t" // 2 nop nop (T = 14)
"nop" "\n\t" // 1 nop (T = 15)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 17)
"rjmp .+0" "\n\t" // 2 nop nop (T = 19)
"dec %[bit]" "\n\t" // 1 bit-- (T = 20)
"breq nextbyte30" "\n\t" // 1-2 if(bit == 0)
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 22)
"rjmp .+0" "\n\t" // 2 nop nop (T = 24)
"rjmp .+0" "\n\t" // 2 nop nop (T = 26)
"rjmp .+0" "\n\t" // 2 nop nop (T = 28)
"rjmp head30" "\n\t" // 2 -> head30 (next bit out)
"nextbyte30:" "\n\t" // (T = 22)
"nop" "\n\t" // 1 nop (T = 23)
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 24)
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 26)
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 28)
"brne head30" "\n" // 1-2 if(i != 0) -> (next byte)
: [port] "+e" (port),
[byte] "+r" (b),
[bit] "+r" (bit),
[next] "+r" (next),
[count] "+w" (i)
: [hi] "r" (hi),
[lo] "r" (lo),
[ptr] "e" (ptr));
}
#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L) // 16Mhz CPU
void send_pixels_16mhz_800(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask)
{
volatile uint16_t i = (uint16_t)sizePixels; // Loop counter
volatile uint8_t* ptr = pixels; // Pointer to next byte
volatile uint8_t b = *ptr++; // Current byte value
volatile uint8_t hi; // PORT w/output bit set high
volatile uint8_t lo; // PORT w/output bit set low
// WS2811 and WS2812 have different hi/lo duty cycles; this is
// similar but NOT an exact copy of the prior 400-on-8 code.
// 20 inst. clocks per bit: HHHHHxxxxxxxxLLLLLLL
// ST instructions: ^ ^ ^ (T=0,5,13)
volatile uint8_t next;
volatile uint8_t bit;
hi = *port | pinMask;
lo = *port & ~pinMask;
next = lo;
bit = 8;
asm volatile(
"head20:" "\n\t" // Clk Pseudocode (T = 0)
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
"sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128)
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
"dec %[bit]" "\n\t" // 1 bit-- (T = 5)
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 7)
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 8)
"breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above)
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10)
"rjmp .+0" "\n\t" // 2 nop nop (T = 12)
"nop" "\n\t" // 1 nop (T = 13)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15)
"nop" "\n\t" // 1 nop (T = 16)
"rjmp .+0" "\n\t" // 2 nop nop (T = 18)
"rjmp head20" "\n\t" // 2 -> head20 (next bit out)
"nextbyte20:" "\n\t" // (T = 10)
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 11)
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 13)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15)
"nop" "\n\t" // 1 nop (T = 16)
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 18)
"brne head20" "\n" // 2 if(i != 0) -> (next byte)
: [port] "+e" (port),
[byte] "+r" (b),
[bit] "+r" (bit),
[next] "+r" (next),
[count] "+w" (i)
: [ptr] "e" (ptr),
[hi] "r" (hi),
[lo] "r" (lo));
}
void send_pixels_16mhz_400(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask)
{
volatile size_t i = sizePixels; // Loop counter
volatile uint8_t* ptr = pixels; // Pointer to next byte
volatile uint8_t b = *ptr++; // Current byte value
volatile uint8_t hi; // PORT w/output bit set high
volatile uint8_t lo; // PORT w/output bit set low
// The 400 KHz clock on 16 MHz MCU is the most 'relaxed' version.
// 40 inst. clocks per bit: HHHHHHHHxxxxxxxxxxxxLLLLLLLLLLLLLLLLLLLL
// ST instructions: ^ ^ ^ (T=0,8,20)
volatile uint8_t next, bit;
hi = *port | pinMask;
lo = *port & ~pinMask;
next = lo;
bit = 8;
asm volatile(
"head40:" "\n\t" // Clk Pseudocode (T = 0)
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
"mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 4)
"rjmp .+0" "\n\t" // 2 nop nop (T = 6)
"rjmp .+0" "\n\t" // 2 nop nop (T = 8)
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 10)
"rjmp .+0" "\n\t" // 2 nop nop (T = 12)
"rjmp .+0" "\n\t" // 2 nop nop (T = 14)
"rjmp .+0" "\n\t" // 2 nop nop (T = 16)
"rjmp .+0" "\n\t" // 2 nop nop (T = 18)
"rjmp .+0" "\n\t" // 2 nop nop (T = 20)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 22)
"nop" "\n\t" // 1 nop (T = 23)
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 24)
"dec %[bit]" "\n\t" // 1 bit-- (T = 25)
"breq nextbyte40" "\n\t" // 1-2 if(bit == 0)
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 27)
"nop" "\n\t" // 1 nop (T = 28)
"rjmp .+0" "\n\t" // 2 nop nop (T = 30)
"rjmp .+0" "\n\t" // 2 nop nop (T = 32)
"rjmp .+0" "\n\t" // 2 nop nop (T = 34)
"rjmp .+0" "\n\t" // 2 nop nop (T = 36)
"rjmp .+0" "\n\t" // 2 nop nop (T = 38)
"rjmp head40" "\n\t" // 2 -> head40 (next bit out)
"nextbyte40:" "\n\t" // (T = 27)
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 28)
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 30)
"rjmp .+0" "\n\t" // 2 nop nop (T = 32)
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 34)
"rjmp .+0" "\n\t" // 2 nop nop (T = 36)
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 38)
"brne head40" "\n" // 1-2 if(i != 0) -> (next byte)
: [port] "+e" (port),
[byte] "+r" (b),
[bit] "+r" (bit),
[next] "+r" (next),
[count] "+w" (i)
: [ptr] "e" (ptr),
[hi] "r" (hi),
[lo] "r" (lo));
}
#else
#error "CPU SPEED NOT SUPPORTED"
#endif
#endif

165
src/NeoPixelBus.h Normal file
View File

@@ -0,0 +1,165 @@
/*-------------------------------------------------------------------------
NeoPixel library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
#include "RgbColor.h"
#include "HslColor.h"
#include "HsbColor.h"
#include "RgbwColor.h"
#include "NeoColorFeatures.h"
#if defined(ARDUINO_ARCH_ESP8266)
#include "NeoEsp8266DmaMethod.h"
#include "NeoEsp8266UartMethod.h"
#include "NeoEsp8266BitBangMethod.h"
#elif defined(__arm__) // must be before ARDUINO_ARCH_AVR due to Teensy incorrectly having it set
#include "NeoArmMethod.h"
#elif defined(ARDUINO_ARCH_AVR)
#include "NeoAvrMethod.h"
#else
#error "Platform Currently Not Supported, please add an Issue at Github/Makuna/NeoPixelBus"
#endif
// '_state' flags for internal state
#define NEO_DIRTY 0x80 // a change was made to pixel data that requires a show
template<typename T_COLOR_FEATURE, typename T_METHOD> class NeoPixelBus
{
public:
// Constructor: number of LEDs, pin number
// NOTE: Pin Number maybe ignored due to hardware limitations of the method.
NeoPixelBus(uint16_t countPixels, uint8_t pin) :
_countPixels(countPixels),
_method(pin, countPixels, T_COLOR_FEATURE::PixelSize)
{
}
~NeoPixelBus()
{
}
void Begin()
{
_method.Initialize();
Dirty();
}
void Show()
{
if (!IsDirty())
{
return;
}
_method.Update();
ResetDirty();
}
inline bool CanShow() const
{
return _method.IsReadyToUpdate();
};
bool IsDirty() const
{
return (_state & NEO_DIRTY);
};
void Dirty()
{
_state |= NEO_DIRTY;
};
void ResetDirty()
{
_state &= ~NEO_DIRTY;
};
uint8_t* Pixels() const
{
return _method.getPixels();
};
size_t PixelsSize() const
{
return _method.getPixelsSize();
};
size_t PixelSize() const
{
return T_COLOR_FEATURE::PixelSize;
};
uint16_t PixelCount() const
{
return _countPixels;
};
void SetPixelColor(uint16_t indexPixel, typename T_COLOR_FEATURE::ColorObject color)
{
if (indexPixel < _countPixels)
{
T_COLOR_FEATURE::applyPixelColor(_method.getPixels(), indexPixel, color);
Dirty();
}
};
typename T_COLOR_FEATURE::ColorObject GetPixelColor(uint16_t indexPixel) const
{
if (indexPixel < _countPixels)
{
return T_COLOR_FEATURE::retrievePixelColor(_method.getPixels(), indexPixel);
}
else
{
// Pixel # is out of bounds, this will get converted to a
// color object type initialized to 0 (black)
return 0;
}
};
void ClearTo(typename T_COLOR_FEATURE::ColorObject color)
{
uint8_t* pixels = _method.getPixels();
for (uint16_t n = 0; n < _countPixels; n++)
{
T_COLOR_FEATURE::applyPixelColor(pixels, n, color);
}
Dirty();
};
private:
const uint16_t _countPixels; // Number of RGB LEDs in strip
uint8_t _state; // internal state
T_METHOD _method;
};

View File

@@ -1,27 +1,59 @@
/*
NeoPixelEsp8266.h - NeoPixel library helper functions for Esp8266 using cycle count
Copyright (c) 2015 Michael C. Miller. All right reserved.
/*-------------------------------------------------------------------------
NeoPixel library helper functions for Esp8266.
This library 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 2.1 of the License, or (at your option) any later version.
Written by Michael C. Miller.
This library is distributed in the hope that it will be useful,
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
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 this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#if defined(ESP8266)
#ifdef ARDUINO_ARCH_ESP8266
#include <Arduino.h>
#include <eagle_soc.h>
void ICACHE_RAM_ATTR esp8266_uart1_send_pixels(uint8_t* pixels, uint8_t* end)
{
const uint8_t _uartData[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 };
const uint8_t _uartFifoTrigger = 124; // tx fifo should be 128 bytes. minus the four we need to send
do
{
uint8_t subpix = *pixels++;
uint8_t buf[4] = { _uartData[(subpix >> 6) & 3],
_uartData[(subpix >> 4) & 3],
_uartData[(subpix >> 2) & 3],
_uartData[subpix & 3] };
// now wait till this the FIFO buffer has room to send more
while (((U1S >> USTXC) & 0xff) > _uartFifoTrigger);
for (uint8_t i = 0; i < 4; i++)
{
// directly write the byte to transfer into the UART1 FIFO register
U1F = buf[i];
}
} while (pixels < end);
}
inline uint32_t _getCycleCount()
{
uint32_t ccount;
@@ -36,7 +68,7 @@ inline uint32_t _getCycleCount()
#define CYCLES_400_T1H (F_CPU / 833333)
#define CYCLES_400 (F_CPU / 400000)
void ICACHE_RAM_ATTR send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin)
void ICACHE_RAM_ATTR bitbang_send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin)
{
const uint32_t pinRegister = _BV(pin);
uint8_t mask;
@@ -77,13 +109,9 @@ void ICACHE_RAM_ATTR send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin)
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister);
}
} while (pixels < end);
// while accurate, this isn't needed due to the delays at the
// top of Show() to enforce between update timing
// while ((_getCycleCount() - cyclesStart) < CYCLES_800);
}
void ICACHE_RAM_ATTR send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin)
void ICACHE_RAM_ATTR bitbang_send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin)
{
const uint32_t pinRegister = _BV(pin);
uint8_t mask;
@@ -92,7 +120,7 @@ void ICACHE_RAM_ATTR send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin)
// trigger emediately
cyclesStart = _getCycleCount() - CYCLES_400;
while (pixels < end)
do
{
subpix = *pixels++;
for (mask = 0x80; mask; mask >>= 1)
@@ -121,11 +149,7 @@ void ICACHE_RAM_ATTR send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin)
// set low
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister);
}
}
// while accurate, this isn't needed due to the delays at the
// top of Show() to enforce between update timing
// while ((_getCycleCount() - cyclesStart) < CYCLES_400);
} while (pixels < end);
}
#endif

215
src/RgbColor.cpp Normal file
View File

@@ -0,0 +1,215 @@
/*-------------------------------------------------------------------------
RgbColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "RgbColor.h"
#include "HslColor.h"
#include "HsbColor.h"
static float _CalcColor(float p, float q, float t)
{
if (t < 0.0f)
t += 1.0f;
if (t > 1.0f)
t -= 1.0f;
if (t < 1.0f / 6.0f)
return p + (q - p) * 6.0f * t;
if (t < 0.5f)
return q;
if (t < 2.0f / 3.0f)
return p + ((q - p) * (2.0f / 3.0f - t) * 6.0f);
return p;
}
RgbColor::RgbColor(HslColor color)
{
float r;
float g;
float b;
float h = color.H;
float s = color.S;
float l = color.L;
if (color.S == 0.0f || color.L == 0.0f)
{
r = g = b = l; // achromatic or black
}
else
{
float q = l < 0.5f ? l * (1.0f + s) : l + s - (l * s);
float p = 2.0f * l - q;
r = _CalcColor(p, q, h + 1.0f / 3.0f);
g = _CalcColor(p, q, h);
b = _CalcColor(p, q, h - 1.0f / 3.0f);
}
R = (uint8_t)(r * 255.0f);
G = (uint8_t)(g * 255.0f);
B = (uint8_t)(b * 255.0f);
}
RgbColor::RgbColor(HsbColor color)
{
float r;
float g;
float b;
float h = color.H;
float s = color.S;
float v = color.B;
if (color.S == 0.0f)
{
r = g = b = v; // achromatic or black
}
else
{
if (h < 0.0f)
h += 1.0f;
if (h > 1.0f)
h -= 1.0f;
h *= 6.0f;
int i = (int)h;
float f = h - i;
float q = v * (1.0f - s * f);
float p = v * (1.0f - s);
float t = v * (1.0f - s * (1.0f - f));
switch (i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default:
r = v;
g = p;
b = q;
break;
}
}
R = (uint8_t)(r * 255.0f);
G = (uint8_t)(g * 255.0f);
B = (uint8_t)(b * 255.0f);
}
uint8_t RgbColor::CalculateBrightness() const
{
return (uint8_t)(((uint16_t)R + (uint16_t)G + (uint16_t)B) / 3);
}
void RgbColor::Darken(uint8_t delta)
{
if (R > delta)
{
R -= delta;
}
else
{
R = 0;
}
if (G > delta)
{
G -= delta;
}
else
{
G = 0;
}
if (B > delta)
{
B -= delta;
}
else
{
B = 0;
}
}
void RgbColor::Lighten(uint8_t delta)
{
if (R < 255 - delta)
{
R += delta;
}
else
{
R = 255;
}
if (G < 255 - delta)
{
G += delta;
}
else
{
G = 255;
}
if (B < 255 - delta)
{
B += delta;
}
else
{
B = 255;
}
}
RgbColor RgbColor::LinearBlend(RgbColor left, RgbColor right, float progress)
{
return RgbColor( left.R + ((right.R - left.R) * progress),
left.G + ((right.G - left.G) * progress),
left.B + ((right.B - left.B) * progress));
}

View File

@@ -1,10 +1,20 @@
/*--------------------------------------------------------------------
NeoPixel is free software: you can redistribute it and/or modify
/*-------------------------------------------------------------------------
RgbColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixel is distributed in the hope that it will be useful,
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
@@ -12,11 +22,14 @@ 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/>.
--------------------------------------------------------------------*/
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
struct HslColor;
struct HsbColor;
// ------------------------------------------------------------------------
// RgbColor represents a color object that is represented by Red, Green, Blue
// component values. It contains helpful color routines to manipulate the
@@ -27,64 +40,74 @@ struct RgbColor
// ------------------------------------------------------------------------
// Construct a RgbColor using R, G, B values (0-255)
// ------------------------------------------------------------------------
RgbColor(uint8_t r, uint8_t g, uint8_t b) :
R(r), G(g), B(b)
{
};
RgbColor(uint8_t r, uint8_t g, uint8_t b) :
R(r), G(g), B(b)
{
};
// ------------------------------------------------------------------------
// Construct a RgbColor using a single brightness value (0-255)
// This works well for creating gray tone colors
// (0) = blakc, (255) = white, (128) = gray
// (0) = black, (255) = white, (128) = gray
// ------------------------------------------------------------------------
RgbColor(uint8_t brightness) :
R(brightness), G(brightness), B(brightness)
{
};
RgbColor(uint8_t brightness) :
R(brightness), G(brightness), B(brightness)
{
};
// ------------------------------------------------------------------------
// Construct a RgbColor using HslColor
// ------------------------------------------------------------------------
RgbColor(HslColor color);
// ------------------------------------------------------------------------
// Construct a RgbColor using HsbColor
// ------------------------------------------------------------------------
RgbColor(HsbColor color);
// ------------------------------------------------------------------------
// Construct a RgbColor that will have its values set in latter operations
// CAUTION: The R,G,B members are not initialized and may not be consistent
// ------------------------------------------------------------------------
RgbColor()
{
};
RgbColor()
{
};
// ------------------------------------------------------------------------
// CalculateBrightness will calculate the overall brightness
// NOTE: This is a simple linear brightness
// ------------------------------------------------------------------------
uint8_t CalculateBrightness();
uint8_t CalculateBrightness() const;
// ------------------------------------------------------------------------
// Darken will adjust the color by the given delta toward black
// NOTE: This is a simple linear change
// delta - (0-255) the amount to dim the color
// ------------------------------------------------------------------------
void Darken(uint8_t delta);
void Darken(uint8_t delta);
// ------------------------------------------------------------------------
// Lighten will adjust the color by the given delta toward white
// NOTE: This is a simple linear change
// delta - (0-255) the amount to lighten the color
// ------------------------------------------------------------------------
void Lighten(uint8_t delta);
void Lighten(uint8_t delta);
// ------------------------------------------------------------------------
// LinearBlend between two colors by the amount defined by progress variable
// left - the color to start the blend at
// right - the color to end the blend at
// progress - (0-255) value where 0 will return left and 255 will return right
// progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right
// and a value between will blend the color weighted linearly between them
// ------------------------------------------------------------------------
static RgbColor LinearBlend(RgbColor left, RgbColor right, uint8_t progress);
static RgbColor LinearBlend(RgbColor left, RgbColor right, float progress);
// ------------------------------------------------------------------------
// Red, Green, Blue color members (0-255) where
// (0,0,0) is black and (255,255,255) is white
// ------------------------------------------------------------------------
uint8_t R;
uint8_t G;
uint8_t B;
uint8_t R;
uint8_t G;
uint8_t B;
};

146
src/RgbwColor.cpp Normal file
View File

@@ -0,0 +1,146 @@
/*-------------------------------------------------------------------------
RgbwColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "RgbColor.h"
#include "HslColor.h"
#include "HsbColor.h"
#include "RgbwColor.h"
RgbwColor::RgbwColor(HslColor color)
{
RgbColor rgbColor(color);
*this = rgbColor;
}
RgbwColor::RgbwColor(HsbColor color)
{
RgbColor rgbColor(color);
*this = rgbColor;
}
uint8_t RgbwColor::CalculateBrightness() const
{
uint8_t colorB = (uint8_t)(((uint16_t)R + (uint16_t)G + (uint16_t)B) / 3);
if (W > colorB)
{
return W;
}
else
{
return colorB;
}
}
void RgbwColor::Darken(uint8_t delta)
{
if (R > delta)
{
R -= delta;
}
else
{
R = 0;
}
if (G > delta)
{
G -= delta;
}
else
{
G = 0;
}
if (B > delta)
{
B -= delta;
}
else
{
B = 0;
}
if (W > delta)
{
W -= delta;
}
else
{
W = 0;
}
}
void RgbwColor::Lighten(uint8_t delta)
{
if (IsColorLess())
{
if (W < 255 - delta)
{
W += delta;
}
else
{
W = 255;
}
}
else
{
if (R < 255 - delta)
{
R += delta;
}
else
{
R = 255;
}
if (G < 255 - delta)
{
G += delta;
}
else
{
G = 255;
}
if (B < 255 - delta)
{
B += delta;
}
else
{
B = 255;
}
}
}
RgbwColor RgbwColor::LinearBlend(RgbwColor left, RgbwColor right, float progress)
{
return RgbwColor( left.R + ((right.R - left.R) * progress),
left.G + ((right.G - left.G) * progress),
left.B + ((right.B - left.B) * progress),
left.W + ((right.W - left.W) * progress) );
}

145
src/RgbwColor.h Normal file
View File

@@ -0,0 +1,145 @@
/*-------------------------------------------------------------------------
RgbwColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
struct RgbColor;
struct HslColor;
struct HsbColor;
// ------------------------------------------------------------------------
// RgbwColor represents a color object that is represented by Red, Green, Blue
// component values and an extra White component. It contains helpful color
// routines to manipulate the color.
// ------------------------------------------------------------------------
struct RgbwColor
{
// ------------------------------------------------------------------------
// Construct a RgbwColor using R, G, B, W values (0-255)
// ------------------------------------------------------------------------
RgbwColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) :
R(r), G(g), B(b), W(w)
{
};
// ------------------------------------------------------------------------
// Construct a RgbColor using a single brightness value (0-255)
// This works well for creating gray tone colors
// (0) = black, (255) = white, (128) = gray
// ------------------------------------------------------------------------
RgbwColor(uint8_t brightness) :
R(0), G(0), B(0), W(brightness)
{
};
// ------------------------------------------------------------------------
// Construct a RgbwColor using RgbColor
// ------------------------------------------------------------------------
RgbwColor(RgbColor color) :
R(color.R),
G(color.G),
B(color.B),
W(0)
{
}
// ------------------------------------------------------------------------
// Construct a RgbwColor using HslColor
// ------------------------------------------------------------------------
RgbwColor(HslColor color);
// ------------------------------------------------------------------------
// Construct a RgbwColor using HsbColor
// ------------------------------------------------------------------------
RgbwColor(HsbColor color);
// ------------------------------------------------------------------------
// Construct a RgbwColor that will have its values set in latter operations
// CAUTION: The R,G,B, W members are not initialized and may not be consistent
// ------------------------------------------------------------------------
RgbwColor()
{
};
// ------------------------------------------------------------------------
// Returns if the color is grey, all values are equal other than white
// ------------------------------------------------------------------------
bool IsMonotone() const
{
return (R == B && R == G);
};
// ------------------------------------------------------------------------
// Returns if the color components are all zero, the white component maybe
// anything
// ------------------------------------------------------------------------
bool IsColorLess() const
{
return (R == 0 && B == 0 && G == 0);
};
// ------------------------------------------------------------------------
// CalculateBrightness will calculate the overall brightness
// NOTE: This is a simple linear brightness
// ------------------------------------------------------------------------
uint8_t CalculateBrightness() const;
// ------------------------------------------------------------------------
// Darken will adjust the color by the given delta toward black
// NOTE: This is a simple linear change
// delta - (0-255) the amount to dim the color
// ------------------------------------------------------------------------
void Darken(uint8_t delta);
// ------------------------------------------------------------------------
// Lighten will adjust the color by the given delta toward white
// NOTE: This is a simple linear change
// delta - (0-255) the amount to lighten the color
// ------------------------------------------------------------------------
void Lighten(uint8_t delta);
// ------------------------------------------------------------------------
// LinearBlend between two colors by the amount defined by progress variable
// left - the color to start the blend at
// right - the color to end the blend at
// progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right
// and a value between will blend the color weighted linearly between them
// ------------------------------------------------------------------------
static RgbwColor LinearBlend(RgbwColor left, RgbwColor right, float progress);
// ------------------------------------------------------------------------
// Red, Green, Blue, White color members (0-255) where
// (0,0,0,0) is black and (255,255,255, 0) and (0,0,0,255) is white
// Note (255,255,255,255) is extreme bright white
// ------------------------------------------------------------------------
uint8_t R;
uint8_t G;
uint8_t B;
uint8_t W;
};