forked from Makuna/NeoPixelBus
Esp32 i2s parallel uses a single back buffer (#793)
This commit is contained in:
@@ -85,6 +85,16 @@ esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, bool parallel_
|
||||
#define I2S_DMA_SILENCE_BLOCK_COUNT_FRONT 2 // two front
|
||||
#define I2S_DMA_SILENCE_BLOCK_COUNT_BACK 1 // one back, required for non parallel
|
||||
|
||||
// compatibility shim between versions of the IDF
|
||||
// note that I2S_NUM_MAX is an enum element, so we check for
|
||||
// existence of the new SOC_I2S_NUM
|
||||
//
|
||||
#if defined(SOC_I2S_NUM)
|
||||
#define NEO_I2S_COUNT (SOC_I2S_NUM)
|
||||
#else
|
||||
#define NEO_I2S_COUNT (I2S_NUM_MAX)
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
i2s_dev_t* bus;
|
||||
@@ -106,14 +116,14 @@ typedef struct
|
||||
#define I2s_Is_Sending 2
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// (SOC_I2S_NUM == 2)
|
||||
static i2s_bus_t I2S[SOC_I2S_NUM] =
|
||||
// (NEO_I2S_COUNT == 2)
|
||||
static i2s_bus_t I2S[NEO_I2S_COUNT] =
|
||||
{
|
||||
{&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle},
|
||||
{&I2S1, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle}
|
||||
};
|
||||
#else
|
||||
static i2s_bus_t I2S[SOC_I2S_NUM] =
|
||||
static i2s_bus_t I2S[NEO_I2S_COUNT] =
|
||||
{
|
||||
{&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle}
|
||||
};
|
||||
@@ -135,7 +145,7 @@ inline void dmaItemInit(lldesc_t* item, uint8_t* posData, size_t sizeData, lldes
|
||||
|
||||
bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize, bool parallel_mode, size_t bytes_per_sample)
|
||||
{
|
||||
if (bus_num >= SOC_I2S_NUM)
|
||||
if (bus_num >= NEO_I2S_COUNT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -206,7 +216,7 @@ bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize, bool paral
|
||||
|
||||
bool i2sDeinitDmaItems(uint8_t bus_num)
|
||||
{
|
||||
if (bus_num >= SOC_I2S_NUM)
|
||||
if (bus_num >= NEO_I2S_COUNT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -224,7 +234,7 @@ esp_err_t i2sSetClock(uint8_t bus_num,
|
||||
uint8_t bck,
|
||||
uint8_t bits)
|
||||
{
|
||||
if (bus_num >= SOC_I2S_NUM || div_a > 63 || div_b > 63 || bck > 63)
|
||||
if (bus_num >= NEO_I2S_COUNT || div_a > 63 || div_b > 63 || bck > 63)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@@ -273,7 +283,7 @@ void i2sSetPins(uint8_t bus_num,
|
||||
int8_t busSampleSize,
|
||||
bool invert)
|
||||
{
|
||||
if (bus_num >= SOC_I2S_NUM)
|
||||
if (bus_num >= NEO_I2S_COUNT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -361,7 +371,7 @@ void i2sSetClkWsPins(uint8_t bus_num,
|
||||
bool invertWs)
|
||||
{
|
||||
|
||||
if (bus_num >= SOC_I2S_NUM)
|
||||
if (bus_num >= NEO_I2S_COUNT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -392,7 +402,7 @@ void i2sSetClkWsPins(uint8_t bus_num,
|
||||
|
||||
bool i2sWriteDone(uint8_t bus_num)
|
||||
{
|
||||
if (bus_num >= SOC_I2S_NUM)
|
||||
if (bus_num >= NEO_I2S_COUNT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -410,7 +420,7 @@ void i2sInit(uint8_t bus_num,
|
||||
uint8_t* data,
|
||||
size_t dataSize)
|
||||
{
|
||||
if (bus_num >= SOC_I2S_NUM)
|
||||
if (bus_num >= NEO_I2S_COUNT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -425,7 +435,7 @@ void i2sInit(uint8_t bus_num,
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// (SOC_I2S_NUM == 2)
|
||||
// (NEO_I2S_COUNT == 2)
|
||||
if (bus_num)
|
||||
{
|
||||
periph_module_enable(PERIPH_I2S1_MODULE);
|
||||
@@ -550,7 +560,7 @@ void i2sInit(uint8_t bus_num,
|
||||
int i2sIntSource;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// (SOC_I2S_NUM == 2)
|
||||
// (NEO_I2S_COUNT == 2)
|
||||
if (bus_num == 1)
|
||||
{
|
||||
i2sIntSource = ETS_I2S1_INTR_SOURCE;
|
||||
@@ -712,7 +722,7 @@ esp_err_t i2sSetSampleRate(uint8_t bus_num,
|
||||
bool parallel_mode,
|
||||
size_t bytes_per_sample)
|
||||
{
|
||||
if (bus_num >= SOC_I2S_NUM)
|
||||
if (bus_num >= NEO_I2S_COUNT)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@@ -789,7 +799,7 @@ void IRAM_ATTR i2sDmaISR(void* arg)
|
||||
|
||||
bool i2sWrite(uint8_t bus_num)
|
||||
{
|
||||
if (bus_num >= SOC_I2S_NUM)
|
||||
if (bus_num >= NEO_I2S_COUNT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -918,7 +928,7 @@ void DumpI2s_fifo_conf(const char* label, i2s_dev_t* bus)
|
||||
|
||||
bool i2sDump(uint8_t bus_num)
|
||||
{
|
||||
if (bus_num >= SOC_I2S_NUM)
|
||||
if (bus_num >= NEO_I2S_COUNT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1014,7 +1024,7 @@ bool i2sGetClks(uint8_t bus_num,
|
||||
uint8_t* clkm_div_b,
|
||||
uint8_t* clkm_div_a)
|
||||
{
|
||||
if (bus_num >= SOC_I2S_NUM)
|
||||
if (bus_num >= NEO_I2S_COUNT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@@ -307,6 +307,140 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Implementation of a Single Buffered version of a I2sContext
|
||||
// Manages the underlying I2S details including the buffer
|
||||
// This creates only a actively sending back buffer,
|
||||
// Note that the back buffer must be DMA memory, a limited resource
|
||||
//
|
||||
// T_MUXMAP - NeoEspI2sMuxMap - tracking class for mux state
|
||||
//
|
||||
template<typename T_MUXMAP>
|
||||
class NeoEspI2sMonoBuffContext
|
||||
{
|
||||
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
|
||||
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
|
||||
NeoEspI2sMonoBuffContext()
|
||||
//:
|
||||
//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);
|
||||
|
||||
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);
|
||||
|
||||
heap_caps_free(I2sBuffer);
|
||||
|
||||
I2sBufferSize = 0;
|
||||
I2sBuffer = nullptr;
|
||||
|
||||
MuxMap.Reset();
|
||||
}
|
||||
|
||||
void StartWrite(uint8_t i2sBusNumber)
|
||||
{
|
||||
if (MuxMap.IsAllMuxBusesUpdated())
|
||||
{
|
||||
MuxMap.ResetMuxBusesUpdated();
|
||||
i2sWrite(i2sBusNumber);
|
||||
}
|
||||
}
|
||||
|
||||
void FillBuffers(const uint8_t* data,
|
||||
size_t sizeData,
|
||||
uint8_t muxId,
|
||||
uint8_t i2sBusNumber)
|
||||
{
|
||||
// wait for not actively sending data
|
||||
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,
|
||||
sizeData,
|
||||
muxId);
|
||||
|
||||
MuxMap.MarkMuxBusUpdated(muxId);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Implementation of a Double Buffered version of a I2sContext
|
||||
// Manages the underlying I2S details including the buffer(s)
|
||||
@@ -317,8 +451,8 @@ public:
|
||||
//
|
||||
// T_MUXMAP - NeoEspI2sMuxMap - tracking class for mux state
|
||||
//
|
||||
template<typename T_MUXMAP>
|
||||
class NeoEspI2sDblBuffContext
|
||||
template<typename T_MUXMAP>
|
||||
class NeoEspI2sDblBuffContext
|
||||
{
|
||||
public:
|
||||
const static size_t DmaBitsPerPixelBit = 4;
|
||||
@@ -332,12 +466,12 @@ public:
|
||||
// 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()
|
||||
NeoEspI2sDblBuffContext()
|
||||
//:
|
||||
//I2sBufferSize(0),
|
||||
//I2sBuffer(nullptr),
|
||||
//I2sEditBuffer(nullptr),
|
||||
//MuxMap()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -381,12 +515,12 @@ public:
|
||||
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
|
||||
// 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
|
||||
// but they won't work on ESP32 in parallel mode, but these will
|
||||
I2S_CHAN_RIGHT_TO_LEFT,
|
||||
I2S_FIFO_16BIT_SINGLE,
|
||||
#endif
|
||||
@@ -415,8 +549,50 @@ public:
|
||||
|
||||
MuxMap.Reset();
|
||||
}
|
||||
|
||||
void StartWrite(uint8_t i2sBusNumber)
|
||||
{
|
||||
if (MuxMap.IsAllMuxBusesUpdated())
|
||||
{
|
||||
MuxMap.ResetMuxBusesUpdated();
|
||||
|
||||
// wait for not actively sending data
|
||||
while (!i2sWriteDone(i2sBusNumber))
|
||||
{
|
||||
yield();
|
||||
}
|
||||
|
||||
// copy edit buffer to sending buffer
|
||||
memcpy(I2sBuffer, I2sEditBuffer, I2sBufferSize);
|
||||
|
||||
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
|
||||
//
|
||||
@@ -462,19 +638,7 @@ public:
|
||||
|
||||
void StartWrite()
|
||||
{
|
||||
if (s_context.MuxMap.IsAllMuxBusesUpdated())
|
||||
{
|
||||
s_context.MuxMap.ResetMuxBusesUpdated();
|
||||
|
||||
// wait for not actively sending data
|
||||
while (!IsWriteDone())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
// copy edit buffer to sending buffer
|
||||
memcpy(s_context.I2sBuffer, s_context.I2sEditBuffer, s_context.I2sBufferSize);
|
||||
i2sWrite(T_BUS::I2sBusNumber);
|
||||
}
|
||||
s_context.StartWrite(T_BUS::I2sBusNumber);
|
||||
}
|
||||
|
||||
bool IsWriteDone() const
|
||||
@@ -484,18 +648,7 @@ public:
|
||||
|
||||
void FillBuffers(const uint8_t* data, size_t sizeData)
|
||||
{
|
||||
if (s_context.MuxMap.IsNoMuxBusesUpdate())
|
||||
{
|
||||
// clear all the data in preperation for each mux channel to add
|
||||
memset(s_context.I2sEditBuffer, 0x00, s_context.I2sBufferSize);
|
||||
}
|
||||
|
||||
s_context.MuxMap.EncodeIntoDma(s_context.I2sEditBuffer,
|
||||
data,
|
||||
sizeData,
|
||||
_muxId );
|
||||
|
||||
s_context.MuxMap.MarkMuxBusUpdated(_muxId);
|
||||
s_context.FillBuffers(data, sizeData, _muxId, T_BUS::I2sBusNumber);
|
||||
}
|
||||
|
||||
void MarkUpdated()
|
||||
@@ -598,17 +751,23 @@ private:
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
||||
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
|
||||
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
|
||||
|
||||
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux8Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux16Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux8Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sMonoBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux16Bus;
|
||||
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1DblMux8Bus;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -654,6 +813,8 @@ typedef NeoEsp32I2s0X16Sk6812Method NeoEsp32I2s0X16Lc8812Method;
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
// I2s1x8
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1DblMux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8DblWs2812xMethod;
|
||||
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2812xMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2805, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2805Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Sk6812Method;
|
||||
|
Reference in New Issue
Block a user