forked from Makuna/NeoPixelBus
V2
This commit is contained in:
1 parent
34deb7f8ad
commit
62f699c120
35 files changed
+4765
-1871
No files matched your search
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
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/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#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;
|
||||
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
|
||||
return ccount;
|
||||
}
|
||||
|
||||
#define CYCLES_800_T0H (F_CPU / 2500000) // 0.4us
|
||||
#define CYCLES_800_T1H (F_CPU / 1250000) // 0.8us
|
||||
#define CYCLES_800 (F_CPU / 800000) // 1.25us per bit
|
||||
#define CYCLES_400_T0H (F_CPU / 2000000)
|
||||
#define CYCLES_400_T1H (F_CPU / 833333)
|
||||
#define CYCLES_400 (F_CPU / 400000)
|
||||
|
||||
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;
|
||||
uint8_t subpix;
|
||||
uint32_t cyclesStart;
|
||||
|
||||
// trigger emediately
|
||||
cyclesStart = _getCycleCount() - CYCLES_800;
|
||||
do
|
||||
{
|
||||
subpix = *pixels++;
|
||||
for (mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
// do the checks here while we are waiting on time to pass
|
||||
uint32_t cyclesBit = ((subpix & mask)) ? CYCLES_800_T1H : CYCLES_800_T0H;
|
||||
uint32_t cyclesNext = cyclesStart;
|
||||
uint32_t delta;
|
||||
|
||||
// after we have done as much work as needed for this next bit
|
||||
// now wait for the HIGH
|
||||
do
|
||||
{
|
||||
// cache and use this count so we don't incur another
|
||||
// instruction before we turn the bit high
|
||||
cyclesStart = _getCycleCount();
|
||||
} while ((cyclesStart - cyclesNext) < CYCLES_800);
|
||||
|
||||
// set high
|
||||
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister);
|
||||
|
||||
// wait for the LOW
|
||||
do
|
||||
{
|
||||
cyclesNext = _getCycleCount();
|
||||
} while ((cyclesNext - cyclesStart) < cyclesBit);
|
||||
|
||||
// set low
|
||||
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister);
|
||||
}
|
||||
} while (pixels < end);
|
||||
}
|
||||
|
||||
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;
|
||||
uint8_t subpix;
|
||||
uint32_t cyclesStart;
|
||||
|
||||
// trigger emediately
|
||||
cyclesStart = _getCycleCount() - CYCLES_400;
|
||||
do
|
||||
{
|
||||
subpix = *pixels++;
|
||||
for (mask = 0x80; mask; mask >>= 1)
|
||||
{
|
||||
uint32_t cyclesBit = ((subpix & mask)) ? CYCLES_400_T1H : CYCLES_400_T0H;
|
||||
uint32_t cyclesNext = cyclesStart;
|
||||
|
||||
// after we have done as much work as needed for this next bit
|
||||
// now wait for the HIGH
|
||||
do
|
||||
{
|
||||
// cache and use this count so we don't incur another
|
||||
// instruction before we turn the bit high
|
||||
cyclesStart = _getCycleCount();
|
||||
} while ((cyclesStart - cyclesNext) < CYCLES_400);
|
||||
|
||||
// set high
|
||||
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister);
|
||||
|
||||
// wait for the LOW
|
||||
do
|
||||
{
|
||||
cyclesNext = _getCycleCount();
|
||||
} while ((cyclesNext - cyclesStart) < cyclesBit);
|
||||
|
||||
// set low
|
||||
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister);
|
||||
}
|
||||
} while (pixels < end);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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));
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
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/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
#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
|
||||
// color.
|
||||
// ------------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Construct a RgbColor using a single brightness value (0-255)
|
||||
// This works well for creating gray tone colors
|
||||
// (0) = black, (255) = white, (128) = gray
|
||||
// ------------------------------------------------------------------------
|
||||
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()
|
||||
{
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 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 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;
|
||||
};
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user