forked from Makuna/NeoPixelBus
esp32 full
This commit is contained in:
@ -7,7 +7,7 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Makuna/NeoPixelBus"
|
"url": "https://github.com/Makuna/NeoPixelBus"
|
||||||
},
|
},
|
||||||
"version": "2.9.0",
|
"version": "2.9.1",
|
||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
||||||
"platforms": "*",
|
"platforms": "*",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name=NeoPixelBus by Makuna
|
name=NeoPixelBus by Makuna
|
||||||
version=2.9.0
|
version=2.9.1
|
||||||
author=Michael C. Miller (makuna@live.com)
|
author=Michael C. Miller (makuna@live.com)
|
||||||
maintainer=Michael C. Miller (makuna@live.com)
|
maintainer=Michael C. Miller (makuna@live.com)
|
||||||
sentence=A library that makes controlling NeoPixels (WS2812x and many others) and DotStars (SK6812 and many others) easy.
|
sentence=A library that makes controlling NeoPixels (WS2812x and many others) and DotStars (SK6812 and many others) easy.
|
||||||
|
@ -60,6 +60,25 @@ License along with NeoPixel. If not, see
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ESP32C6_DEV) && !defined(ARDUINO_ESP32H2_DEV)
|
||||||
|
|
||||||
|
inline
|
||||||
|
static uint32_t getEspCycleCount(void)
|
||||||
|
{
|
||||||
|
uint32_t ccount;
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
__asm__ __volatile__("csrr %0,0x7e2":"=r" (ccount));
|
||||||
|
//ccount = esp_cpu_get_ccount();
|
||||||
|
#else
|
||||||
|
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
|
||||||
|
#endif
|
||||||
|
return ccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// some platforms do not define this standard progmem type for some reason
|
// some platforms do not define this standard progmem type for some reason
|
||||||
//
|
//
|
||||||
#ifndef PGM_VOID_P
|
#ifndef PGM_VOID_P
|
||||||
|
@ -171,7 +171,7 @@ public:
|
|||||||
typedef NeoNoSettings SettingsObject;
|
typedef NeoNoSettings SettingsObject;
|
||||||
|
|
||||||
NeoAvrMethodBase(uint8_t pin,
|
NeoAvrMethodBase(uint8_t pin,
|
||||||
[[maybe_unused]] uint16_t pixelCount,
|
uint16_t pixelCount,
|
||||||
[[maybe_unused]] size_t elementSize,
|
[[maybe_unused]] size_t elementSize,
|
||||||
[[maybe_unused]] size_t settingsSize) :
|
[[maybe_unused]] size_t settingsSize) :
|
||||||
_pin(pin),
|
_pin(pin),
|
||||||
|
@ -58,7 +58,7 @@ public:
|
|||||||
|
|
||||||
const static size_t MuxBusDataSize = 1;
|
const static size_t MuxBusDataSize = 1;
|
||||||
|
|
||||||
static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId)
|
static void EncodeIntoDma(uint8_t** dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId)
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
// 1234 - order
|
// 1234 - order
|
||||||
@ -84,7 +84,7 @@ public:
|
|||||||
const uint32_t EncodedOneBit = 0x01010001;
|
const uint32_t EncodedOneBit = 0x01010001;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t* pDma = reinterpret_cast<uint32_t*>(dmaBuffer);
|
uint32_t* pDma = reinterpret_cast<uint32_t*>(*dmaBuffer);
|
||||||
const uint8_t* pEnd = data + sizeData;
|
const uint8_t* pEnd = data + sizeData;
|
||||||
|
|
||||||
for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++)
|
for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++)
|
||||||
@ -100,6 +100,8 @@ public:
|
|||||||
value <<= 1;
|
value <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// return the buffer pointer advanced by encoding progress
|
||||||
|
*dmaBuffer = reinterpret_cast<uint8_t*>(pDma);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -113,7 +115,7 @@ public:
|
|||||||
|
|
||||||
const static size_t MuxBusDataSize = 2;
|
const static size_t MuxBusDataSize = 2;
|
||||||
|
|
||||||
static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId)
|
static void EncodeIntoDma(uint8_t** dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId)
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
// 1234 5678 - order
|
// 1234 5678 - order
|
||||||
@ -177,14 +179,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void Fillx16(uint8_t* dmaBuffer,
|
static void Fillx16(uint8_t** dmaBuffer,
|
||||||
const uint8_t* data,
|
const uint8_t* data,
|
||||||
size_t sizeData,
|
size_t sizeData,
|
||||||
uint8_t muxShift,
|
uint8_t muxShift,
|
||||||
const uint64_t EncodedZeroBit64,
|
const uint64_t EncodedZeroBit64,
|
||||||
const uint64_t EncodedOneBit64)
|
const uint64_t EncodedOneBit64)
|
||||||
{
|
{
|
||||||
uint64_t* pDma64 = reinterpret_cast<uint64_t*>(dmaBuffer);
|
uint64_t* pDma64 = reinterpret_cast<uint64_t*>(*dmaBuffer);
|
||||||
const uint8_t* pEnd = data + sizeData;
|
const uint8_t* pEnd = data + sizeData;
|
||||||
|
|
||||||
for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++)
|
for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++)
|
||||||
@ -200,6 +202,8 @@ protected:
|
|||||||
value <<= 1;
|
value <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// return the buffer pointer advanced by encoding progress
|
||||||
|
*dmaBuffer = reinterpret_cast<uint8_t*>(pDma64);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -403,203 +407,45 @@ public:
|
|||||||
MuxMap.Reset();
|
MuxMap.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartWrite(uint8_t i2sBusNumber)
|
void ResetBuffer()
|
||||||
{
|
{
|
||||||
if (MuxMap.IsAllMuxBusesUpdated())
|
// to keep the inner loops for EncodeIntoDma smaller
|
||||||
|
// they will just OR in their values
|
||||||
|
// so the buffer must be cleared first
|
||||||
|
if (MuxMap.IsNoMuxBusesUpdate())
|
||||||
{
|
{
|
||||||
MuxMap.ResetMuxBusesUpdated();
|
// clear all the data in preperation for each mux channel to add
|
||||||
i2sWrite(i2sBusNumber);
|
memset(I2sBuffer, 0x00, I2sBufferSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FillBuffers(const uint8_t* data,
|
void FillBuffer(uint8_t** dmaBuffer,
|
||||||
|
const uint8_t* data,
|
||||||
size_t sizeData,
|
size_t sizeData,
|
||||||
uint8_t muxId,
|
uint8_t muxId)
|
||||||
uint8_t i2sBusNumber)
|
|
||||||
{
|
{
|
||||||
// wait for not actively sending data
|
MuxMap.EncodeIntoDma(dmaBuffer,
|
||||||
while (!i2sWriteDone(i2sBusNumber))
|
|
||||||
{
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
// to keep the inner loops for EncodeIntoDma smaller
|
|
||||||
// they will just OR in their values
|
|
||||||
// so the buffer must be cleared first
|
|
||||||
if (MuxMap.IsNoMuxBusesUpdate())
|
|
||||||
{
|
|
||||||
// clear all the data in preperation for each mux channel to add
|
|
||||||
memset(I2sBuffer, 0x00, I2sBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
MuxMap.EncodeIntoDma(I2sBuffer,
|
|
||||||
data,
|
data,
|
||||||
sizeData,
|
sizeData,
|
||||||
muxId);
|
muxId);
|
||||||
|
|
||||||
MuxMap.MarkMuxBusUpdated(muxId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Implementation of a Double Buffered version of a I2sContext
|
|
||||||
// Manages the underlying I2S details including the buffer(s)
|
|
||||||
// This creates a front buffer that can be filled while actively sending
|
|
||||||
// the back buffer, thus improving async operation of the i2s DMA.
|
|
||||||
// Note that the back buffer must be DMA memory, a limited resource, so
|
|
||||||
// the front buffer uses normal memory and copies rather than swap pointers
|
|
||||||
//
|
|
||||||
// T_MUXMAP - NeoEspI2sMuxMap - tracking class for mux state
|
|
||||||
//
|
|
||||||
template<typename T_MUXMAP>
|
|
||||||
class NeoEspI2sDblBuffContext
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
const static size_t DmaBitsPerPixelBit = 4;
|
|
||||||
|
|
||||||
size_t I2sBufferSize; // total size of I2sBuffer
|
|
||||||
uint8_t* I2sBuffer; // holds the DMA buffer that is referenced by I2sBufDesc
|
|
||||||
uint8_t* I2sEditBuffer; // hold a editable buffer that is copied to I2sBuffer
|
|
||||||
T_MUXMAP MuxMap;
|
|
||||||
|
|
||||||
// as a static instance, all members get initialized to zero
|
|
||||||
// and the constructor is called at inconsistent time to other globals
|
|
||||||
// so its not useful to have or rely on,
|
|
||||||
// but without it presence they get zeroed far too late
|
|
||||||
NeoEspI2sDblBuffContext()
|
|
||||||
//:
|
|
||||||
//I2sBufferSize(0),
|
|
||||||
//I2sBuffer(nullptr),
|
|
||||||
//I2sEditBuffer(nullptr),
|
|
||||||
//MuxMap()
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Construct(const uint8_t busNumber, uint32_t i2sSampleRate)
|
|
||||||
{
|
|
||||||
// construct only once on first time called
|
|
||||||
if (I2sBuffer == nullptr)
|
|
||||||
{
|
|
||||||
// MuxMap.MaxBusDataSize = max size in bytes of a single channel
|
|
||||||
// DmaBitsPerPixelBit = how many dma bits/byte are needed for each source (pixel) bit/byte
|
|
||||||
// T_MUXMAP::MuxBusDataSize = the true size of data for selected mux mode (not exposed size as i2s0 only supports 16bit mode)
|
|
||||||
I2sBufferSize = MuxMap.MaxBusDataSize * 8 * DmaBitsPerPixelBit * T_MUXMAP::MuxBusDataSize;
|
|
||||||
|
|
||||||
// must have a 4 byte aligned buffer for i2s
|
|
||||||
uint32_t alignment = I2sBufferSize % 4;
|
|
||||||
if (alignment)
|
|
||||||
{
|
|
||||||
I2sBufferSize += 4 - alignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t dmaBlockCount = (I2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN;
|
|
||||||
|
|
||||||
I2sBuffer = static_cast<uint8_t*>(heap_caps_malloc(I2sBufferSize, MALLOC_CAP_DMA));
|
|
||||||
if (I2sBuffer == nullptr)
|
|
||||||
{
|
|
||||||
log_e("send buffer memory allocation failure (size %u)",
|
|
||||||
I2sBufferSize);
|
|
||||||
}
|
|
||||||
memset(I2sBuffer, 0x00, I2sBufferSize);
|
|
||||||
|
|
||||||
I2sEditBuffer = static_cast<uint8_t*>(malloc(I2sBufferSize));
|
|
||||||
if (I2sEditBuffer == nullptr)
|
|
||||||
{
|
|
||||||
log_e("edit buffer memory allocation failure (size %u)",
|
|
||||||
I2sBufferSize);
|
|
||||||
}
|
|
||||||
memset(I2sEditBuffer, 0x00, I2sBufferSize);
|
|
||||||
|
|
||||||
i2sInit(busNumber,
|
|
||||||
true,
|
|
||||||
T_MUXMAP::MuxBusDataSize,
|
|
||||||
i2sSampleRate,
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
|
||||||
// using these modes on ESP32S2 actually allows it to function
|
|
||||||
// in both x8 and x16
|
|
||||||
I2S_CHAN_STEREO,
|
|
||||||
I2S_FIFO_16BIT_DUAL,
|
|
||||||
#else
|
|
||||||
// but they won't work on ESP32 in parallel mode, but these will
|
|
||||||
I2S_CHAN_RIGHT_TO_LEFT,
|
|
||||||
I2S_FIFO_16BIT_SINGLE,
|
|
||||||
#endif
|
|
||||||
dmaBlockCount,
|
|
||||||
I2sBuffer,
|
|
||||||
I2sBufferSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Destruct(const uint8_t busNumber)
|
|
||||||
{
|
|
||||||
if (I2sBuffer == nullptr)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2sSetPins(busNumber, -1, -1, -1, false);
|
|
||||||
i2sDeinit(busNumber);
|
|
||||||
|
|
||||||
free(I2sEditBuffer);
|
|
||||||
heap_caps_free(I2sBuffer);
|
|
||||||
|
|
||||||
I2sBufferSize = 0;
|
|
||||||
I2sBuffer = nullptr;
|
|
||||||
I2sEditBuffer = nullptr;
|
|
||||||
|
|
||||||
MuxMap.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartWrite(uint8_t i2sBusNumber)
|
void StartWrite(uint8_t i2sBusNumber)
|
||||||
{
|
{
|
||||||
if (MuxMap.IsAllMuxBusesUpdated())
|
if (MuxMap.IsAllMuxBusesUpdated())
|
||||||
{
|
{
|
||||||
MuxMap.ResetMuxBusesUpdated();
|
MuxMap.ResetMuxBusesUpdated();
|
||||||
|
|
||||||
// wait for not actively sending data
|
|
||||||
while (!i2sWriteDone(i2sBusNumber))
|
|
||||||
{
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy edit buffer to sending buffer
|
|
||||||
memcpy(I2sBuffer, I2sEditBuffer, I2sBufferSize);
|
|
||||||
|
|
||||||
i2sWrite(i2sBusNumber);
|
i2sWrite(i2sBusNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FillBuffers(const uint8_t* data,
|
|
||||||
size_t sizeData,
|
|
||||||
uint8_t muxId,
|
|
||||||
uint8_t i2sBusNumber)
|
|
||||||
{
|
|
||||||
// to keep the inner loops for EncodeIntoDma smaller
|
|
||||||
// they will just OR in their values
|
|
||||||
// so the buffer must be cleared first
|
|
||||||
if (MuxMap.IsNoMuxBusesUpdate())
|
|
||||||
{
|
|
||||||
// clear all the data in preperation for each mux channel to add
|
|
||||||
memset(I2sEditBuffer, 0x00, I2sBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
MuxMap.EncodeIntoDma(I2sEditBuffer,
|
|
||||||
data,
|
|
||||||
sizeData,
|
|
||||||
muxId);
|
|
||||||
|
|
||||||
MuxMap.MarkMuxBusUpdated(muxId);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Implementation of the low level interface into i2s mux bus
|
// Implementation of the low level interface into i2s mux bus
|
||||||
//
|
//
|
||||||
// T_BUSCONTEXT - the context to use, currently only NeoEspI2sDblBuffContext but there is
|
// T_BUSCONTEXT - the context to use, currently only NeoEspI2sMonoBuffContext
|
||||||
// a plan to provide one that doesn't implement the front buffer but would be less
|
|
||||||
// async as it would have to wait until the last frame was completely sent before
|
|
||||||
// updating and new data
|
|
||||||
// T_BUS - the bus id, NeoEsp32I2sBusZero, NeoEsp32I2sBusOne
|
// T_BUS - the bus id, NeoEsp32I2sBusZero, NeoEsp32I2sBusOne
|
||||||
//
|
//
|
||||||
template<typename T_BUSCONTEXT, typename T_BUS>
|
template<typename T_BUSCONTEXT, typename T_BUS>
|
||||||
@ -636,24 +482,28 @@ public:
|
|||||||
_muxId = s_context.MuxMap.InvalidMuxId;
|
_muxId = s_context.MuxMap.InvalidMuxId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartWrite()
|
|
||||||
{
|
|
||||||
s_context.StartWrite(T_BUS::I2sBusNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsWriteDone() const
|
bool IsWriteDone() const
|
||||||
{
|
{
|
||||||
return i2sWriteDone(T_BUS::I2sBusNumber);
|
return i2sWriteDone(T_BUS::I2sBusNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FillBuffers(const uint8_t* data, size_t sizeData)
|
uint8_t* BeginUpdate()
|
||||||
{
|
{
|
||||||
s_context.FillBuffers(data, sizeData, _muxId, T_BUS::I2sBusNumber);
|
s_context.ResetBuffer();
|
||||||
|
return s_context.I2sBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkUpdated()
|
void FillBuffer(uint8_t** dmaBuffer,
|
||||||
|
const uint8_t* data,
|
||||||
|
size_t sizeData)
|
||||||
|
{
|
||||||
|
s_context.FillBuffer(dmaBuffer, data, sizeData, _muxId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndUpdate()
|
||||||
{
|
{
|
||||||
s_context.MuxMap.MarkMuxBusUpdated(_muxId);
|
s_context.MuxMap.MarkMuxBusUpdated(_muxId);
|
||||||
|
s_context.StartWrite(T_BUS::I2sBusNumber); // only when all buses are update is actual write started
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -677,11 +527,11 @@ public:
|
|||||||
typedef NeoNoSettings SettingsObject;
|
typedef NeoNoSettings SettingsObject;
|
||||||
|
|
||||||
NeoEsp32I2sXMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
NeoEsp32I2sXMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||||
_sizeData(pixelCount * elementSize + settingsSize),
|
|
||||||
_pin(pin),
|
_pin(pin),
|
||||||
|
_pixelCount(pixelCount),
|
||||||
_bus()
|
_bus()
|
||||||
{
|
{
|
||||||
_bus.RegisterNewMuxBus(_sizeData + T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs);
|
_bus.RegisterNewMuxBus((pixelCount * elementSize + settingsSize) + T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
~NeoEsp32I2sXMethodBase()
|
~NeoEsp32I2sXMethodBase()
|
||||||
@ -692,8 +542,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
_bus.DeregisterMuxBus(_pin);
|
_bus.DeregisterMuxBus(_pin);
|
||||||
|
|
||||||
free(_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsReadyToUpdate() const
|
bool IsReadyToUpdate() const
|
||||||
@ -704,37 +552,65 @@ public:
|
|||||||
void Initialize()
|
void Initialize()
|
||||||
{
|
{
|
||||||
_bus.Initialize(_pin, T_SPEED::I2sSampleRate, T_INVERT::Inverted);
|
_bus.Initialize(_pin, T_SPEED::I2sSampleRate, T_INVERT::Inverted);
|
||||||
|
}
|
||||||
|
|
||||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
template <typename T_COLOR_OBJECT,
|
||||||
if (_data == nullptr)
|
typename T_COLOR_FEATURE,
|
||||||
|
typename T_SHADER>
|
||||||
|
void Update(
|
||||||
|
T_COLOR_OBJECT* pixels,
|
||||||
|
size_t countPixels,
|
||||||
|
const typename T_COLOR_FEATURE::SettingsObject& featureSettings,
|
||||||
|
const T_SHADER& shader)
|
||||||
|
{
|
||||||
|
// wait for not actively sending data
|
||||||
|
while (!_bus.IsWriteDone())
|
||||||
{
|
{
|
||||||
log_e("front buffer memory allocation failure");
|
yield();
|
||||||
}
|
}
|
||||||
// data cleared later in Begin()
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update(bool)
|
const size_t sendDataSize = T_COLOR_FEATURE::SettingsSize >= T_COLOR_FEATURE::PixelSize ? T_COLOR_FEATURE::SettingsSize : T_COLOR_FEATURE::PixelSize;
|
||||||
{
|
uint8_t sendData[sendDataSize];
|
||||||
_bus.FillBuffers(_data, _sizeData);
|
uint8_t* data = _bus.BeginUpdate();
|
||||||
_bus.StartWrite(); // only triggers actual write after all mux busses have updated
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AlwaysUpdate()
|
// if there are settings at the front
|
||||||
{
|
//
|
||||||
// this method requires update to be called even if no changes to method buffer
|
if (T_COLOR_FEATURE::applyFrontSettings(sendData, sendDataSize, featureSettings))
|
||||||
// as edit buffer is always cleared and then copied to send buffer and all
|
{
|
||||||
// mux bus needs to included
|
_bus.FillBuffer(&data, sendData, T_COLOR_FEATURE::SettingsSize);
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* getData() const
|
// apply primary color data
|
||||||
{
|
//
|
||||||
return _data;
|
T_COLOR_OBJECT* pixel = pixels;
|
||||||
};
|
const T_COLOR_OBJECT* pixelEnd = pixel + countPixels;
|
||||||
|
uint16_t stripCount = _pixelCount;
|
||||||
|
|
||||||
size_t getDataSize() const
|
while (stripCount--)
|
||||||
{
|
{
|
||||||
return _sizeData;
|
typename T_COLOR_FEATURE::ColorObject color = shader.Apply(*pixel);
|
||||||
|
T_COLOR_FEATURE::applyPixelColor(sendData, sendDataSize, color);
|
||||||
|
|
||||||
|
_bus.FillBuffer(&data, sendData, T_COLOR_FEATURE::PixelSize);
|
||||||
|
|
||||||
|
pixel++;
|
||||||
|
|
||||||
|
if (pixel >= pixelEnd)
|
||||||
|
{
|
||||||
|
// restart at first
|
||||||
|
pixel = pixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// if there are settings at the back
|
||||||
|
//
|
||||||
|
if (T_COLOR_FEATURE::applyBackSettings(sendData, sendDataSize, featureSettings))
|
||||||
|
{
|
||||||
|
_bus.FillBuffer(&data, sendData, T_COLOR_FEATURE::SettingsSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
_bus.EndUpdate(); // triggers actual write after all mux busses have updated
|
||||||
}
|
}
|
||||||
|
|
||||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||||
@ -742,11 +618,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const size_t _sizeData; // Size of '_data' buffer
|
const uint8_t _pin; // output pin number
|
||||||
const uint8_t _pin; // output pin number
|
const uint16_t _pixelCount; // count of pixels in the strip
|
||||||
|
|
||||||
T_BUS _bus; // holds instance for mux bus support
|
T_BUS _bus; // holds instance for mux bus support
|
||||||
uint8_t* _data; // Holds LED color values
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
@ -754,10 +629,6 @@ private:
|
|||||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
||||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
|
||||||
|
|
||||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0DblMux8Bus;
|
|
||||||
|
|
||||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0DblMux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8DblWs2812xMethod;
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
||||||
@ -767,8 +638,6 @@ typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint16_t, Neo
|
|||||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux8Bus;
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux8Bus;
|
||||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux16Bus;
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux16Bus;
|
||||||
|
|
||||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1DblMux8Bus;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NORMAL
|
// NORMAL
|
||||||
@ -815,8 +684,6 @@ typedef NeoEsp32I2s0X16Sk6812Method NeoEsp32I2s0X16Lc8812Method;
|
|||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
|
||||||
// I2s1x8
|
// I2s1x8
|
||||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1DblMux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8DblWs2812xMethod;
|
|
||||||
|
|
||||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2812xMethod;
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2812xMethod;
|
||||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2805, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2805Method;
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2805, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2805Method;
|
||||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Sk6812Method;
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Sk6812Method;
|
||||||
|
@ -28,47 +28,15 @@ License along with NeoPixel. If not, see
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "..\NeoUtil.h"
|
||||||
|
|
||||||
#if ESP_IDF_VERSION_MAJOR>=5
|
#if ESP_IDF_VERSION_MAJOR>=5
|
||||||
#include <soc/gpio_struct.h>
|
#include <soc/gpio_struct.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline uint32_t getCycleCount(void)
|
|
||||||
{
|
|
||||||
uint32_t ccount;
|
|
||||||
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
|
||||||
__asm__ __volatile__("csrr %0,0x7e2":"=r" (ccount));
|
|
||||||
//ccount = esp_cpu_get_ccount();
|
|
||||||
#else
|
|
||||||
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
|
|
||||||
#endif
|
|
||||||
return ccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interrupt lock class, used for RAII interrupt disabling
|
// Interrupt lock class, used for RAII interrupt disabling
|
||||||
class InterruptLock {
|
class InterruptLock
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
{
|
||||||
portMUX_TYPE updateMux;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inline void lock()
|
|
||||||
{
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
|
||||||
portENTER_CRITICAL(&updateMux);
|
|
||||||
#else
|
|
||||||
noInterrupts();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void unlock()
|
|
||||||
{
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
|
||||||
portEXIT_CRITICAL(&updateMux);
|
|
||||||
#else
|
|
||||||
interrupts();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
inline void poll()
|
inline void poll()
|
||||||
@ -79,7 +47,7 @@ public:
|
|||||||
|
|
||||||
inline InterruptLock()
|
inline InterruptLock()
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
: updateMux(portMUX_INITIALIZER_UNLOCKED)
|
: _updateMux(portMUX_INITIALIZER_UNLOCKED)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
lock();
|
lock();
|
||||||
@ -89,23 +57,46 @@ public:
|
|||||||
{
|
{
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
portMUX_TYPE _updateMux;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline void lock()
|
||||||
|
{
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
portENTER_CRITICAL(&_updateMux);
|
||||||
|
#else
|
||||||
|
noInterrupts();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void unlock()
|
||||||
|
{
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
portEXIT_CRITICAL(&_updateMux);
|
||||||
|
#else
|
||||||
|
interrupts();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
__attribute__((flatten))
|
||||||
const uint8_t* end,
|
uint32_t IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* data,
|
||||||
|
size_t dataSize,
|
||||||
uint8_t pin,
|
uint8_t pin,
|
||||||
uint32_t t0h,
|
uint32_t t0h,
|
||||||
uint32_t t1h,
|
uint32_t t1h,
|
||||||
uint32_t period,
|
uint32_t period,
|
||||||
size_t sizePixel,
|
|
||||||
uint32_t tLatch,
|
|
||||||
bool invert)
|
bool invert)
|
||||||
{
|
{
|
||||||
|
const uint8_t* dataEnd = data + dataSize;
|
||||||
uint32_t setValue = _BV(pin);
|
uint32_t setValue = _BV(pin);
|
||||||
uint32_t clearValue = _BV(pin);
|
uint32_t clearValue = _BV(pin);
|
||||||
uint8_t mask = 0x80;
|
uint8_t mask = 0x80;
|
||||||
uint8_t subpix = *pixels++;
|
uint8_t subpix = *data++;
|
||||||
uint8_t element = 0;
|
|
||||||
uint32_t cyclesStart = 0; // trigger emediately
|
uint32_t cyclesStart = 0; // trigger emediately
|
||||||
uint32_t cyclesNext = 0;
|
uint32_t cyclesNext = 0;
|
||||||
|
|
||||||
@ -153,7 +144,7 @@ bool IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
|||||||
|
|
||||||
// after we have done as much work as needed for this next bit
|
// after we have done as much work as needed for this next bit
|
||||||
// now wait for the HIGH
|
// now wait for the HIGH
|
||||||
while (((cyclesStart = getCycleCount()) - cyclesNext) < period);
|
while (((cyclesStart = getEspCycleCount()) - cyclesNext) < period);
|
||||||
|
|
||||||
// set pin state
|
// set pin state
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
@ -163,7 +154,7 @@ bool IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// wait for the LOW
|
// wait for the LOW
|
||||||
while ((getCycleCount() - cyclesStart) < cyclesBit);
|
while ((getEspCycleCount() - cyclesStart) < cyclesBit);
|
||||||
|
|
||||||
// reset pin start
|
// reset pin start
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
@ -180,37 +171,18 @@ bool IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
|||||||
{
|
{
|
||||||
// no more bits to send in this byte
|
// no more bits to send in this byte
|
||||||
// check for another byte
|
// check for another byte
|
||||||
if (pixels >= end)
|
if (data >= dataEnd)
|
||||||
{
|
{
|
||||||
// no more bytes to send so stop
|
// no more bytes to send so stop
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// reset mask to first bit and get the next byte
|
// reset mask to first bit and get the next byte
|
||||||
mask = 0x80;
|
mask = 0x80;
|
||||||
subpix = *pixels++;
|
subpix = *data++;
|
||||||
|
|
||||||
// Hack: permit interrupts to fire
|
|
||||||
// If we get held up more than the latch period, stop.
|
|
||||||
// We do this at the end of an element to ensure that each
|
|
||||||
// element always gets valid data, even if we don't update
|
|
||||||
// every element.
|
|
||||||
if (tLatch)
|
|
||||||
{
|
|
||||||
++element;
|
|
||||||
if (element == sizePixel)
|
|
||||||
{
|
|
||||||
isrGuard.poll();
|
|
||||||
if ((getCycleCount() - cyclesNext) > tLatch)
|
|
||||||
{
|
|
||||||
return false; // failed
|
|
||||||
}
|
|
||||||
element = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true; // update complete
|
return getEspCycleCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,14 +39,12 @@ License along with NeoPixel. If not, see
|
|||||||
#define CYCLES_LOOPTEST (4) // adjustment due to loop exit test instruction cycles
|
#define CYCLES_LOOPTEST (4) // adjustment due to loop exit test instruction cycles
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern bool neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
extern bool neoEspBitBangWriteSpacingPixels(const uint8_t* data,
|
||||||
const uint8_t* end,
|
size_t dataSize,
|
||||||
uint8_t pin,
|
uint8_t pin,
|
||||||
uint32_t t0h,
|
uint32_t t0h,
|
||||||
uint32_t t1h,
|
uint32_t t1h,
|
||||||
uint32_t period,
|
uint32_t period,
|
||||||
size_t sizePixel,
|
|
||||||
uint32_t tLatch,
|
|
||||||
bool invert);
|
bool invert);
|
||||||
|
|
||||||
|
|
||||||
@ -188,27 +186,25 @@ public:
|
|||||||
const static uint32_t TLatch = 0;
|
const static uint32_t TLatch = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T_SPEED, typename T_INVERTED, bool V_INTER_PIXEL_ISR> class NeoEspBitBangMethodBase
|
template<typename T_SPEED, typename T_INVERTED, bool V_INTER_PIXEL_ISR>
|
||||||
|
class NeoEspBitBangMethodBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef NeoNoSettings SettingsObject;
|
typedef NeoNoSettings SettingsObject;
|
||||||
|
|
||||||
NeoEspBitBangMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
NeoEspBitBangMethodBase(uint8_t pin,
|
||||||
_sizePixel(elementSize),
|
uint16_t pixelCount,
|
||||||
_sizeData(pixelCount * elementSize + settingsSize),
|
[[maybe_unused]] size_t elementSize,
|
||||||
_pin(pin)
|
[[maybe_unused]] size_t settingsSize) :
|
||||||
|
_pin(pin),
|
||||||
|
_pixelCount(pixelCount)
|
||||||
{
|
{
|
||||||
pinMode(pin, OUTPUT);
|
pinMode(pin, OUTPUT);
|
||||||
|
|
||||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
|
||||||
// data cleared later in Begin()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~NeoEspBitBangMethodBase()
|
~NeoEspBitBangMethodBase()
|
||||||
{
|
{
|
||||||
pinMode(_pin, INPUT);
|
pinMode(_pin, INPUT);
|
||||||
|
|
||||||
free(_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsReadyToUpdate() const
|
bool IsReadyToUpdate() const
|
||||||
@ -225,10 +221,18 @@ public:
|
|||||||
_endTime = micros();
|
_endTime = micros();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update(bool)
|
template <typename T_COLOR_OBJECT,
|
||||||
|
typename T_COLOR_FEATURE,
|
||||||
|
typename T_SHADER>
|
||||||
|
void Update(
|
||||||
|
T_COLOR_OBJECT* pixels,
|
||||||
|
size_t countPixels,
|
||||||
|
const typename T_COLOR_FEATURE::SettingsObject& featureSettings,
|
||||||
|
const T_SHADER& shader)
|
||||||
{
|
{
|
||||||
bool done = false;
|
bool wasInterrupted = true;
|
||||||
for (unsigned retries = 0; !done && retries < 4; ++retries)
|
|
||||||
|
for (unsigned retries = 0; wasInterrupted && retries < 4; ++retries)
|
||||||
{
|
{
|
||||||
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
// 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
|
// put a delay at the end of the function, the ending time is noted and
|
||||||
@ -241,45 +245,91 @@ public:
|
|||||||
yield(); // allows for system yield if needed
|
yield(); // allows for system yield if needed
|
||||||
}
|
}
|
||||||
|
|
||||||
done = neoEspBitBangWriteSpacingPixels(_data,
|
wasInterrupted = false;
|
||||||
_data + _sizeData,
|
|
||||||
_pin,
|
const size_t sendDataSize = T_COLOR_FEATURE::SettingsSize >= T_COLOR_FEATURE::PixelSize ? T_COLOR_FEATURE::SettingsSize : T_COLOR_FEATURE::PixelSize;
|
||||||
T_SPEED::T0H,
|
uint8_t sendData[sendDataSize];
|
||||||
T_SPEED::T1H,
|
uint32_t cycleLast = getEspCycleCount();
|
||||||
T_SPEED::Period,
|
|
||||||
_sizePixel,
|
// if there are settings at the front
|
||||||
NeoEspTLatch<T_SPEED, V_INTER_PIXEL_ISR>::TLatch,
|
//
|
||||||
T_INVERTED::IdleLevel);
|
if (T_COLOR_FEATURE::applyFrontSettings(sendData, sendDataSize, featureSettings))
|
||||||
|
{
|
||||||
|
cycleLast = neoEspBitBangWriteSpacingPixels(sendData,
|
||||||
|
T_COLOR_FEATURE::SettingsSize,
|
||||||
|
_pin,
|
||||||
|
T_SPEED::T0H,
|
||||||
|
T_SPEED::T1H,
|
||||||
|
T_SPEED::Period,
|
||||||
|
T_INVERTED::IdleLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send primary color data
|
||||||
|
//
|
||||||
|
T_COLOR_OBJECT* pixel = pixels;
|
||||||
|
const T_COLOR_OBJECT* pixelEnd = pixel + countPixels;
|
||||||
|
uint16_t stripCount = _pixelCount;
|
||||||
|
|
||||||
|
while (!wasInterrupted && stripCount--)
|
||||||
|
{
|
||||||
|
typename T_COLOR_FEATURE::ColorObject color = shader.Apply(*pixel);
|
||||||
|
T_COLOR_FEATURE::applyPixelColor(sendData, sendDataSize, color);
|
||||||
|
|
||||||
|
if ((getEspCycleCount() - cycleLast) > NeoEspTLatch<T_SPEED, V_INTER_PIXEL_ISR>::TLatch)
|
||||||
|
{
|
||||||
|
wasInterrupted = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cycleLast = neoEspBitBangWriteSpacingPixels(sendData,
|
||||||
|
T_COLOR_FEATURE::PixelSize,
|
||||||
|
_pin,
|
||||||
|
T_SPEED::T0H,
|
||||||
|
T_SPEED::T1H,
|
||||||
|
T_SPEED::Period,
|
||||||
|
T_INVERTED::IdleLevel);
|
||||||
|
|
||||||
|
pixel++;
|
||||||
|
if (pixel >= pixelEnd)
|
||||||
|
{
|
||||||
|
// restart at first
|
||||||
|
pixel = pixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are settings at the back
|
||||||
|
//
|
||||||
|
if (!wasInterrupted && T_COLOR_FEATURE::applyBackSettings(sendData, sendDataSize, featureSettings))
|
||||||
|
{
|
||||||
|
if ((getEspCycleCount() - cycleLast) > NeoEspTLatch<T_SPEED, V_INTER_PIXEL_ISR>::TLatch)
|
||||||
|
{
|
||||||
|
wasInterrupted = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cycleLast = neoEspBitBangWriteSpacingPixels(sendData,
|
||||||
|
T_COLOR_FEATURE::SettingsSize,
|
||||||
|
_pin,
|
||||||
|
T_SPEED::T0H,
|
||||||
|
T_SPEED::T1H,
|
||||||
|
T_SPEED::Period,
|
||||||
|
T_INVERTED::IdleLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// save EOD time for latch on next call
|
// save EOD time for latch on next call
|
||||||
_endTime = micros();
|
_endTime = micros();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AlwaysUpdate()
|
|
||||||
{
|
|
||||||
// this method requires update to be called only if changes to buffer
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* getData() const
|
|
||||||
{
|
|
||||||
return _data;
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t getDataSize() const
|
|
||||||
{
|
|
||||||
return _sizeData;
|
|
||||||
};
|
|
||||||
|
|
||||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const size_t _sizePixel; // size of a pixel in _data
|
|
||||||
const size_t _sizeData; // Size of '_data' buffer below
|
|
||||||
const uint8_t _pin; // output pin number
|
const uint8_t _pin; // output pin number
|
||||||
|
const uint16_t _pixelCount; // count of pixels in the strip
|
||||||
|
|
||||||
uint32_t _endTime; // Latch timing reference
|
uint32_t _endTime; // Latch timing reference
|
||||||
uint8_t* _data; // Holds LED color values
|
uint8_t* _data; // Holds LED color values
|
||||||
|
Reference in New Issue
Block a user