forked from Makuna/NeoPixelBus
Esp32 i2s x (#630)
ESP32 x8 and X16 parallel support --------- Co-authored-by: Awawa <69086569+awawa-dev@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// NeoPixel_ESP32_I2sParallel -
|
||||||
|
// This sketch demonstrates the use of the I2S Parallel method allowing upto 8 hardware updated channels
|
||||||
|
// This example only works on the ESP32
|
||||||
|
//
|
||||||
|
// The key part of the method name is Esp32I2s1X8,
|
||||||
|
// E2p32 (platform specific method),
|
||||||
|
// I2s Channel 1 (most commonly available),
|
||||||
|
// X8 (8 parallel channel mode)
|
||||||
|
//
|
||||||
|
// In this example, it demonstrates different ColorFeatures, Method specification, and count per strip
|
||||||
|
//
|
||||||
|
#include <NeoPixelBus.h>
|
||||||
|
|
||||||
|
// Demonstrating the use of the first four channels, but the method used allows for eight
|
||||||
|
NeoPixelBus<NeoBgrFeature, NeoEsp32I2s1X8Ws2811Method> strip1(120, 15); // note: older WS2811 and longer strip
|
||||||
|
NeoPixelBus<NeoGrbFeature, NeoEsp32I2s1X8Ws2812xMethod> strip2(100, 2); // note: modern WS2812 with letter like WS2812b
|
||||||
|
NeoPixelBus<NeoGrbFeature, NeoEsp32I2s1X8Ws2812xInvertedMethod> strip3(100, 4); // note: inverted
|
||||||
|
NeoPixelBus<NeoGrbwFeature, NeoEsp32I2s1X8Sk6812Method> strip4(50, 16); // note: RGBW and Sk6812 and smaller strip
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial); // wait for serial attach
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
Serial.println("Initializing...");
|
||||||
|
Serial.flush();
|
||||||
|
|
||||||
|
// must call begin on all the strips
|
||||||
|
strip1.Begin();
|
||||||
|
strip2.Begin();
|
||||||
|
strip3.Begin();
|
||||||
|
strip4.Begin();
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
Serial.println("Running...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
// draw on the strips
|
||||||
|
strip1.SetPixelColor(0, RgbColor(255, 0, 0)); // red
|
||||||
|
strip2.SetPixelColor(0, RgbColor(0, 127, 0)); // green
|
||||||
|
strip3.SetPixelColor(0, RgbColor(0, 0, 53)); // blue
|
||||||
|
strip4.SetPixelColor(0, RgbwColor(0, 0, 128, 255)); // white channel with a little blue
|
||||||
|
|
||||||
|
// show them,
|
||||||
|
// only on the last show, no matter the order, will the data be sent
|
||||||
|
strip1.Show();
|
||||||
|
strip2.Show();
|
||||||
|
strip3.Show();
|
||||||
|
strip4.Show();
|
||||||
|
}
|
@@ -97,6 +97,7 @@ License along with NeoPixel. If not, see
|
|||||||
#include "internal/NeoEsp32RmtMethod.h"
|
#include "internal/NeoEsp32RmtMethod.h"
|
||||||
#include "internal/NeoEspBitBangMethod.h"
|
#include "internal/NeoEspBitBangMethod.h"
|
||||||
#include "internal/DotStarEsp32DmaSpiMethod.h"
|
#include "internal/DotStarEsp32DmaSpiMethod.h"
|
||||||
|
#include "internal/NeoEsp32I2sXMethod.h"
|
||||||
|
|
||||||
#elif defined(ARDUINO_ARCH_NRF52840) // must be before __arm__
|
#elif defined(ARDUINO_ARCH_NRF52840) // must be before __arm__
|
||||||
|
|
||||||
@@ -195,7 +196,7 @@ public:
|
|||||||
|
|
||||||
void Show(bool maintainBufferConsistency = true)
|
void Show(bool maintainBufferConsistency = true)
|
||||||
{
|
{
|
||||||
if (!IsDirty())
|
if (!IsDirty() && !_method.AlwaysUpdate())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -177,6 +177,12 @@ public:
|
|||||||
ESP_ERROR_CHECK(ret);
|
ESP_ERROR_CHECK(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data + _sizeStartFrame;
|
return _data + _sizeStartFrame;
|
||||||
|
@@ -104,6 +104,12 @@ public:
|
|||||||
_wire.endTransaction();
|
_wire.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
@@ -6,7 +6,6 @@
|
|||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
@@ -40,6 +39,7 @@
|
|||||||
#include "esp_intr.h"
|
#include "esp_intr.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "rom/lldesc.h"
|
||||||
#include "soc/gpio_reg.h"
|
#include "soc/gpio_reg.h"
|
||||||
#include "soc/gpio_sig_map.h"
|
#include "soc/gpio_sig_map.h"
|
||||||
#include "soc/io_mux_reg.h"
|
#include "soc/io_mux_reg.h"
|
||||||
@@ -60,55 +60,36 @@
|
|||||||
#include "Esp32_i2s.h"
|
#include "Esp32_i2s.h"
|
||||||
#include "esp32-hal.h"
|
#include "esp32-hal.h"
|
||||||
|
|
||||||
|
esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits_per_sample);
|
||||||
|
esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, bool parallel_mode, size_t bytes_per_sample);
|
||||||
|
|
||||||
|
#define MATRIX_DETACH_OUT_SIG 0x100
|
||||||
|
|
||||||
#if ESP_IDF_VERSION_MAJOR<=4
|
#if ESP_IDF_VERSION_MAJOR<=4
|
||||||
#define I2S_BASE_CLK (160000000L)
|
#define I2S_BASE_CLK (160000000L)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ESP32_REG(addr) (*((volatile uint32_t*)(0x3FF00000+(addr))))
|
#define I2S_DMA_BLOCK_COUNT_DEFAULT 0
|
||||||
|
// 20 bytes gives us enough time if we use single stage idle
|
||||||
|
// But it can't be longer due to non-parrallel mode and 50us reset time
|
||||||
|
// there just isn't enough silence at the end to fill more than 20 bytes
|
||||||
|
#define I2S_DMA_SILENCE_SIZE 20 // 4 byte increments
|
||||||
|
#define I2S_DMA_SILENCE_BLOCK_COUNT_FRONT 2 // two front
|
||||||
|
#define I2S_DMA_SILENCE_BLOCK_COUNT_BACK 1 // one back, required for non parallel
|
||||||
|
|
||||||
#define I2S_DMA_BLOCK_COUNT_DEFAULT 16
|
typedef struct
|
||||||
// 24 bytes gives us enough time if we use single stage idle
|
{
|
||||||
// with the two stage idle we can use the minimum of 4 bytes
|
i2s_dev_t* bus;
|
||||||
#define I2S_DMA_SILENCE_SIZE 4*1
|
int8_t ws;
|
||||||
#define I2S_DMA_SILENCE_BLOCK_COUNT 3 // two front, one back
|
int8_t bck;
|
||||||
#define I2S_DMA_QUEUE_COUNT 2
|
int8_t out;
|
||||||
|
int8_t in;
|
||||||
|
|
||||||
typedef struct i2s_dma_item_s {
|
intr_handle_t isr_handle;
|
||||||
uint32_t blocksize: 12; // datalen
|
lldesc_t* dma_items;
|
||||||
uint32_t datalen : 12; // len*(bits_per_sample/8)*2 => max 2047*8bit/1023*16bit samples
|
size_t dma_count;
|
||||||
uint32_t unused : 5; // 0
|
|
||||||
uint32_t sub_sof : 1; // 0
|
|
||||||
uint32_t eof : 1; // 1 => last?
|
|
||||||
uint32_t owner : 1; // 1
|
|
||||||
|
|
||||||
void* data; // malloc(datalen)
|
volatile uint32_t is_sending_data;
|
||||||
struct i2s_dma_item_s* next;
|
|
||||||
|
|
||||||
// if this pointer is not null, it will be freed
|
|
||||||
void* free_ptr;
|
|
||||||
|
|
||||||
// if DMA buffers are preallocated
|
|
||||||
uint8_t* buf;
|
|
||||||
} i2s_dma_item_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
i2s_dev_t* bus;
|
|
||||||
int8_t ws;
|
|
||||||
int8_t bck;
|
|
||||||
int8_t out;
|
|
||||||
int8_t in;
|
|
||||||
uint32_t rate;
|
|
||||||
intr_handle_t isr_handle;
|
|
||||||
xQueueHandle tx_queue;
|
|
||||||
|
|
||||||
uint8_t* silence_buf;
|
|
||||||
size_t silence_len;
|
|
||||||
|
|
||||||
i2s_dma_item_t* dma_items;
|
|
||||||
size_t dma_count;
|
|
||||||
uint32_t dma_buf_len :12;
|
|
||||||
uint32_t unused :20;
|
|
||||||
volatile uint32_t is_sending_data;
|
|
||||||
} i2s_bus_t;
|
} i2s_bus_t;
|
||||||
|
|
||||||
// is_sending_data values
|
// is_sending_data values
|
||||||
@@ -116,103 +97,144 @@ typedef struct {
|
|||||||
#define I2s_Is_Pending 1
|
#define I2s_Is_Pending 1
|
||||||
#define I2s_Is_Sending 2
|
#define I2s_Is_Sending 2
|
||||||
|
|
||||||
static uint8_t i2s_silence_buf[I2S_DMA_SILENCE_SIZE] = { 0 };
|
|
||||||
|
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
// (I2S_NUM_MAX == 2)
|
// (I2S_NUM_MAX == 2)
|
||||||
static i2s_bus_t I2S[I2S_NUM_MAX] = {
|
static i2s_bus_t I2S[I2S_NUM_MAX] =
|
||||||
{&I2S0, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_SIZE, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, 0, 0, I2s_Is_Idle},
|
{
|
||||||
{&I2S1, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_SIZE, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, 0, 0, I2s_Is_Idle}
|
{&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
|
#else
|
||||||
static i2s_bus_t I2S[I2S_NUM_MAX] = {
|
static i2s_bus_t I2S[I2S_NUM_MAX] =
|
||||||
{&I2S0, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_SIZE, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, 0, 0, I2s_Is_Idle}
|
{
|
||||||
|
{&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void IRAM_ATTR i2sDmaISR(void* arg);
|
void IRAM_ATTR i2sDmaISR(void* arg);
|
||||||
|
|
||||||
|
inline void dmaItemInit(lldesc_t* item, uint8_t* posData, size_t sizeData, lldesc_t* itemNext)
|
||||||
|
{
|
||||||
|
item->eof = 0;
|
||||||
|
item->owner = 1;
|
||||||
|
item->sosf = 0;
|
||||||
|
item->offset = 0;
|
||||||
|
item->buf = posData;
|
||||||
|
item->size = sizeData;
|
||||||
|
item->length = sizeData;
|
||||||
|
item->qe.stqe_next = itemNext;
|
||||||
|
}
|
||||||
|
|
||||||
bool i2sInitDmaItems(uint8_t bus_num) {
|
bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize)
|
||||||
if (bus_num >= I2S_NUM_MAX) {
|
{
|
||||||
|
if (bus_num >= I2S_NUM_MAX)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (I2S[bus_num].tx_queue) {// already set
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t dmaCount = I2S[bus_num].dma_count;
|
size_t dmaCount = I2S[bus_num].dma_count;
|
||||||
|
|
||||||
if (I2S[bus_num].dma_items == NULL) {
|
if (I2S[bus_num].dma_items == NULL)
|
||||||
I2S[bus_num].dma_items = (i2s_dma_item_t*)heap_caps_malloc(dmaCount * sizeof(i2s_dma_item_t), MALLOC_CAP_DMA);
|
{
|
||||||
if (I2S[bus_num].dma_items == NULL) {
|
I2S[bus_num].dma_items = (lldesc_t*)heap_caps_malloc(dmaCount * sizeof(lldesc_t), MALLOC_CAP_DMA);
|
||||||
|
if (I2S[bus_num].dma_items == NULL)
|
||||||
|
{
|
||||||
log_e("MEM ERROR!");
|
log_e("MEM ERROR!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int i, i2;
|
lldesc_t* itemFirst = &I2S[bus_num].dma_items[0];
|
||||||
i2s_dma_item_t* item = NULL;
|
lldesc_t* item = itemFirst;
|
||||||
i2s_dma_item_t* itemPrev = NULL;
|
// lldesc_t* itemsEnd = itemFirst + I2S[bus_num].dma_count;
|
||||||
|
lldesc_t* itemNext = item + 1;
|
||||||
|
size_t dataLeft = dataSize;
|
||||||
|
uint8_t* pos = data;
|
||||||
|
// at the end of the data is the encoded silence reset
|
||||||
|
uint8_t* posSilence = data + dataSize - I2S_DMA_SILENCE_SIZE;
|
||||||
|
|
||||||
for(i=0; i< dmaCount; i++) {
|
// front two are silent items used for looping to micmic single fire
|
||||||
itemPrev = item;
|
// default to looping
|
||||||
|
dmaItemInit(item, posSilence, I2S_DMA_SILENCE_SIZE, itemNext);
|
||||||
|
dmaItemInit(itemNext, posSilence, I2S_DMA_SILENCE_SIZE, item);
|
||||||
|
item = itemNext;
|
||||||
|
itemNext++;
|
||||||
|
|
||||||
i2 = (i+1) % dmaCount;
|
// init blocks with avialable data
|
||||||
item = &I2S[bus_num].dma_items[i];
|
//
|
||||||
item->eof = 0;
|
while (dataLeft)
|
||||||
item->owner = 1;
|
{
|
||||||
item->sub_sof = 0;
|
item = itemNext;
|
||||||
item->unused = 0;
|
itemNext++;
|
||||||
item->data = I2S[bus_num].silence_buf;
|
|
||||||
item->blocksize = I2S[bus_num].silence_len;
|
size_t blockSize = dataLeft;
|
||||||
item->datalen = I2S[bus_num].silence_len;
|
if (blockSize > I2S_DMA_MAX_DATA_LEN)
|
||||||
item->next = &I2S[bus_num].dma_items[i2];
|
{
|
||||||
item->free_ptr = NULL;
|
blockSize = I2S_DMA_MAX_DATA_LEN;
|
||||||
item->buf = NULL;
|
}
|
||||||
|
dataLeft -= blockSize;
|
||||||
|
|
||||||
|
dmaItemInit(item, pos, blockSize, itemNext);
|
||||||
|
|
||||||
|
pos += blockSize;
|
||||||
}
|
}
|
||||||
itemPrev->eof = 1;
|
|
||||||
|
// last data item is EOF to manage send state using EOF ISR
|
||||||
item->eof = 1;
|
item->eof = 1;
|
||||||
|
|
||||||
I2S[bus_num].tx_queue = xQueueCreate(I2S_DMA_QUEUE_COUNT, sizeof(i2s_dma_item_t*));
|
// last block, the back silent item, loops to front
|
||||||
if (I2S[bus_num].tx_queue == NULL) {// memory error
|
item = itemNext;
|
||||||
log_e("MEM ERROR!");
|
dmaItemInit(item, posSilence, I2S_DMA_SILENCE_SIZE, itemFirst);
|
||||||
heap_caps_free(I2S[bus_num].dma_items);
|
|
||||||
I2S[bus_num].dma_items = NULL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool i2sDeinitDmaItems(uint8_t bus_num) {
|
bool i2sDeinitDmaItems(uint8_t bus_num)
|
||||||
if (bus_num >= I2S_NUM_MAX) {
|
{
|
||||||
|
if (bus_num >= I2S_NUM_MAX)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!I2S[bus_num].tx_queue) {
|
|
||||||
return false; // nothing to deinit
|
|
||||||
}
|
|
||||||
|
|
||||||
vQueueDelete(I2S[bus_num].tx_queue);
|
|
||||||
I2S[bus_num].tx_queue = NULL;
|
|
||||||
heap_caps_free(I2S[bus_num].dma_items);
|
heap_caps_free(I2S[bus_num].dma_items);
|
||||||
I2S[bus_num].dma_items = NULL;
|
I2S[bus_num].dma_items = NULL;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits) {
|
// normal 4, 10, 63, 12, 16
|
||||||
if (bus_num >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63) {
|
|
||||||
|
esp_err_t i2sSetClock(uint8_t bus_num,
|
||||||
|
uint8_t div_num, // 4 13
|
||||||
|
uint8_t div_b, // 10 20
|
||||||
|
uint8_t div_a, // 63 63
|
||||||
|
uint8_t bck, // 12 60 or 7
|
||||||
|
uint8_t bits) // 16 8
|
||||||
|
{
|
||||||
|
if (bus_num >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63)
|
||||||
|
{
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//log_i("i2sSetClock bus %u, clkm_div_num %u, clk_div_a %u, clk_div_b %u, bck_div_num %u, bits_mod %u",
|
||||||
|
// bus_num,
|
||||||
|
// div_num,
|
||||||
|
// div_a,
|
||||||
|
// div_b,
|
||||||
|
// bck,
|
||||||
|
// bits);
|
||||||
|
|
||||||
i2s_dev_t* i2s = I2S[bus_num].bus;
|
i2s_dev_t* i2s = I2S[bus_num].bus;
|
||||||
|
|
||||||
typeof(i2s->clkm_conf) clkm_conf;
|
typeof(i2s->clkm_conf) clkm_conf;
|
||||||
|
|
||||||
clkm_conf.val = 0;
|
clkm_conf.val = 0;
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
clkm_conf.clka_en = 0;
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
clkm_conf.clk_sel = 2; // APPL = 1 APB = 2
|
||||||
|
clkm_conf.clk_en = 1; // examples of i2s show this being set if sel is set to 2
|
||||||
#else
|
#else
|
||||||
clkm_conf.clk_sel = 2;
|
clkm_conf.clka_en = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
clkm_conf.clkm_div_a = div_a;
|
clkm_conf.clkm_div_a = div_a;
|
||||||
@@ -227,50 +249,101 @@ esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t d
|
|||||||
sample_rate_conf.tx_bits_mod = bits;
|
sample_rate_conf.tx_bits_mod = bits;
|
||||||
sample_rate_conf.rx_bits_mod = bits;
|
sample_rate_conf.rx_bits_mod = bits;
|
||||||
i2s->sample_rate_conf.val = sample_rate_conf.val;
|
i2s->sample_rate_conf.val = sample_rate_conf.val;
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void i2sSetPins(uint8_t bus_num, int8_t out, bool invert)
|
void i2sSetPins(uint8_t bus_num,
|
||||||
|
int8_t out,
|
||||||
|
int8_t parallel,
|
||||||
|
int8_t busSampleSize,
|
||||||
|
bool invert)
|
||||||
{
|
{
|
||||||
if (bus_num >= I2S_NUM_MAX)
|
if (bus_num >= I2S_NUM_MAX)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int8_t outOld = I2S[bus_num].out;
|
|
||||||
|
|
||||||
I2S[bus_num].out = out;
|
|
||||||
|
|
||||||
// disable old pin
|
|
||||||
if (outOld >= 0)
|
|
||||||
{
|
|
||||||
gpio_matrix_out(outOld, 0x100, false, false);
|
|
||||||
pinMode(outOld, INPUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out >= 0)
|
if (out >= 0)
|
||||||
{
|
{
|
||||||
|
uint32_t i2sSignal;
|
||||||
|
|
||||||
pinMode(out, OUTPUT);
|
pinMode(out, OUTPUT);
|
||||||
|
|
||||||
int i2sSignal;
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
// (I2S_NUM_MAX == 2)
|
// S2 only has one bus
|
||||||
if (bus_num == 1)
|
//
|
||||||
{
|
// in parallel mode
|
||||||
i2sSignal = I2S1O_DATA_OUT23_IDX;
|
// 8bit mode : I2S0O_DATA_OUT16_IDX ~I2S0O_DATA_OUT23_IDX
|
||||||
}
|
// 16bit mode : I2S0O_DATA_OUT8_IDX ~I2S0O_DATA_OUT23_IDX
|
||||||
else
|
// 24bit mode : I2S0O_DATA_OUT0_IDX ~I2S0O_DATA_OUT23_IDX
|
||||||
#endif
|
if (parallel == -1)
|
||||||
{
|
{
|
||||||
i2sSignal = I2S0O_DATA_OUT23_IDX;
|
i2sSignal = I2S0O_DATA_OUT23_IDX;
|
||||||
}
|
}
|
||||||
|
else if (busSampleSize == 1)
|
||||||
|
{
|
||||||
|
i2sSignal = I2S0O_DATA_OUT16_IDX + parallel;
|
||||||
|
}
|
||||||
|
else if (busSampleSize == 2)
|
||||||
|
{
|
||||||
|
i2sSignal = I2S0O_DATA_OUT8_IDX + parallel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i2sSignal = I2S0O_DATA_OUT0_IDX + parallel;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
if (bus_num == 0)
|
||||||
|
{
|
||||||
|
// in parallel mode
|
||||||
|
// 0-7 bits : I2S0O_DATA_OUT16_IDX ~I2S0O_DATA_OUT23_IDX
|
||||||
|
// 8-15 bits : I2S0O_DATA_OUT8_IDX ~I2S0O_DATA_OUT23_IDX
|
||||||
|
// 16-23 bits : I2S0O_DATA_OUT0_IDX ~I2S0O_DATA_OUT23_IDX
|
||||||
|
if (parallel == -1)
|
||||||
|
{
|
||||||
|
i2sSignal = I2S0O_DATA_OUT23_IDX;
|
||||||
|
}
|
||||||
|
else if (parallel < 8)
|
||||||
|
{
|
||||||
|
i2sSignal = I2S0O_DATA_OUT16_IDX + parallel;
|
||||||
|
}
|
||||||
|
else if (parallel < 16)
|
||||||
|
{
|
||||||
|
i2sSignal = I2S0O_DATA_OUT8_IDX + parallel - 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i2sSignal = I2S0O_DATA_OUT0_IDX + parallel - 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (parallel == -1)
|
||||||
|
{
|
||||||
|
i2sSignal = I2S1O_DATA_OUT23_IDX;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i2sSignal = I2S1O_DATA_OUT0_IDX + parallel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
//log_i("i2sSetPins bus %u, i2sSignal %u, pin %u, mux %u",
|
||||||
|
// bus_num,
|
||||||
|
// i2sSignal,
|
||||||
|
// out,
|
||||||
|
// parallel);
|
||||||
gpio_matrix_out(out, i2sSignal, invert, false);
|
gpio_matrix_out(out, i2sSignal, invert, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool i2sWriteDone(uint8_t bus_num) {
|
bool i2sWriteDone(uint8_t bus_num)
|
||||||
if (bus_num >= I2S_NUM_MAX) {
|
{
|
||||||
|
if (bus_num >= I2S_NUM_MAX)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,28 +351,36 @@ bool i2sWriteDone(uint8_t bus_num) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void i2sInit(uint8_t bus_num,
|
void i2sInit(uint8_t bus_num,
|
||||||
uint32_t bits_per_sample,
|
bool parallel_mode,
|
||||||
|
size_t bytes_per_sample,
|
||||||
uint32_t sample_rate,
|
uint32_t sample_rate,
|
||||||
i2s_tx_chan_mod_t chan_mod,
|
i2s_tx_chan_mod_t chan_mod,
|
||||||
i2s_tx_fifo_mod_t fifo_mod,
|
i2s_tx_fifo_mod_t fifo_mod,
|
||||||
size_t dma_count,
|
size_t dma_count,
|
||||||
size_t dma_len) {
|
uint8_t* data,
|
||||||
if (bus_num >= I2S_NUM_MAX) {
|
size_t dataSize)
|
||||||
|
{
|
||||||
|
if (bus_num >= I2S_NUM_MAX)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
I2S[bus_num].dma_count = dma_count + I2S_DMA_SILENCE_BLOCK_COUNT; // an extra two for looping silence
|
I2S[bus_num].dma_count = dma_count +
|
||||||
I2S[bus_num].dma_buf_len = dma_len & 0xFFF;
|
I2S_DMA_SILENCE_BLOCK_COUNT_FRONT +
|
||||||
|
I2S_DMA_SILENCE_BLOCK_COUNT_BACK;
|
||||||
|
|
||||||
if (!i2sInitDmaItems(bus_num)) {
|
if (!i2sInitDmaItems(bus_num, data, dataSize))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
// (I2S_NUM_MAX == 2)
|
// (I2S_NUM_MAX == 2)
|
||||||
if (bus_num) {
|
if (bus_num)
|
||||||
|
{
|
||||||
periph_module_enable(PERIPH_I2S1_MODULE);
|
periph_module_enable(PERIPH_I2S1_MODULE);
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
periph_module_enable(PERIPH_I2S0_MODULE);
|
periph_module_enable(PERIPH_I2S0_MODULE);
|
||||||
@@ -313,12 +394,6 @@ void i2sInit(uint8_t bus_num,
|
|||||||
i2s->int_clr.val = 0xFFFFFFFF;
|
i2s->int_clr.val = 0xFFFFFFFF;
|
||||||
i2s->fifo_conf.dscr_en = 0;
|
i2s->fifo_conf.dscr_en = 0;
|
||||||
|
|
||||||
// reset fifo
|
|
||||||
i2s->conf.rx_fifo_reset = 1;
|
|
||||||
i2s->conf.rx_fifo_reset = 0;
|
|
||||||
i2s->conf.tx_fifo_reset = 1;
|
|
||||||
i2s->conf.tx_fifo_reset = 0;
|
|
||||||
|
|
||||||
// reset i2s
|
// reset i2s
|
||||||
i2s->conf.tx_reset = 1;
|
i2s->conf.tx_reset = 1;
|
||||||
i2s->conf.tx_reset = 0;
|
i2s->conf.tx_reset = 0;
|
||||||
@@ -331,11 +406,29 @@ void i2sInit(uint8_t bus_num,
|
|||||||
i2s->lc_conf.out_rst = 1;
|
i2s->lc_conf.out_rst = 1;
|
||||||
i2s->lc_conf.out_rst = 0;
|
i2s->lc_conf.out_rst = 0;
|
||||||
|
|
||||||
|
// reset fifo
|
||||||
|
i2s->conf.rx_fifo_reset = 1;
|
||||||
|
i2s->conf.rx_fifo_reset = 0;
|
||||||
|
i2s->conf.tx_fifo_reset = 1;
|
||||||
|
i2s->conf.tx_fifo_reset = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// set parallel (LCD) mode
|
||||||
|
{
|
||||||
|
typeof(i2s->conf2) conf2;
|
||||||
|
conf2.val = 0;
|
||||||
|
conf2.lcd_en = parallel_mode;
|
||||||
|
conf2.lcd_tx_wrx2_en = 0; // parallel_mode; // ((parallel_mode) && (bytes_per_sample == 2));
|
||||||
|
i2s->conf2.val = conf2.val;
|
||||||
|
}
|
||||||
|
|
||||||
// Enable and configure DMA
|
// Enable and configure DMA
|
||||||
typeof(i2s->lc_conf) lc_conf;
|
{
|
||||||
lc_conf.val = 0;
|
typeof(i2s->lc_conf) lc_conf;
|
||||||
lc_conf.out_eof_mode = 1;
|
lc_conf.val = 0;
|
||||||
i2s->lc_conf.val = lc_conf.val;
|
lc_conf.out_eof_mode = 1;
|
||||||
|
i2s->lc_conf.val = lc_conf.val;
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
i2s->pdm_conf.pcm2pdm_conv_en = 0;
|
i2s->pdm_conf.pcm2pdm_conv_en = 0;
|
||||||
@@ -343,37 +436,62 @@ void i2sInit(uint8_t bus_num,
|
|||||||
#endif
|
#endif
|
||||||
// SET_PERI_REG_BITS(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, 0x1, RTC_CNTL_SOC_CLK_SEL_S);
|
// SET_PERI_REG_BITS(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, 0x1, RTC_CNTL_SOC_CLK_SEL_S);
|
||||||
|
|
||||||
typeof(i2s->conf_chan) conf_chan;
|
{
|
||||||
conf_chan.val = 0;
|
typeof(i2s->fifo_conf) fifo_conf;
|
||||||
conf_chan.tx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left
|
|
||||||
conf_chan.rx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left
|
|
||||||
i2s->conf_chan.val = conf_chan.val;
|
|
||||||
|
|
||||||
typeof(i2s->fifo_conf) fifo_conf;
|
fifo_conf.val = 0;
|
||||||
fifo_conf.val = 0;
|
fifo_conf.rx_fifo_mod_force_en = 1;
|
||||||
fifo_conf.tx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel
|
fifo_conf.tx_fifo_mod_force_en = 1;
|
||||||
fifo_conf.rx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel
|
fifo_conf.tx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel
|
||||||
i2s->fifo_conf.val = fifo_conf.val;
|
fifo_conf.rx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel
|
||||||
|
fifo_conf.rx_data_num = 32; //Thresholds.
|
||||||
|
fifo_conf.tx_data_num = 32;
|
||||||
|
|
||||||
typeof(i2s->conf) conf;
|
i2s->fifo_conf.val = fifo_conf.val;
|
||||||
conf.val = 0;
|
}
|
||||||
conf.tx_msb_shift = (bits_per_sample != 8);// 0:DAC/PCM, 1:I2S
|
|
||||||
conf.tx_right_first = (bits_per_sample == 8);
|
|
||||||
i2s->conf.val = conf.val;
|
|
||||||
|
|
||||||
typeof(i2s->conf2) conf2;
|
// $REVIEW old code didn't set this
|
||||||
conf2.val = 0;
|
{
|
||||||
conf2.lcd_en = (bits_per_sample == 8);
|
typeof(i2s->conf1) conf1;
|
||||||
i2s->conf2.val = conf2.val;
|
conf1.val = 0;
|
||||||
|
conf1.tx_stop_en = 0;
|
||||||
|
conf1.tx_pcm_bypass = 1;
|
||||||
|
i2s->conf1.val = conf1.val;
|
||||||
|
}
|
||||||
|
|
||||||
i2s->fifo_conf.tx_fifo_mod_force_en = 1;
|
{
|
||||||
|
typeof(i2s->conf_chan) conf_chan;
|
||||||
|
conf_chan.val = 0;
|
||||||
|
conf_chan.tx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left
|
||||||
|
conf_chan.rx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left
|
||||||
|
i2s->conf_chan.val = conf_chan.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
typeof(i2s->conf) conf;
|
||||||
|
conf.val = 0;
|
||||||
|
conf.tx_msb_shift = !parallel_mode; // 0:DAC/PCM, 1:I2S
|
||||||
|
conf.tx_right_first = 0; // parallel_mode?;
|
||||||
|
i2s->conf.val = conf.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2s->timing.val = 0;
|
||||||
|
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
i2s->pdm_conf.rx_pdm_en = 0;
|
i2s->pdm_conf.rx_pdm_en = 0;
|
||||||
i2s->pdm_conf.tx_pdm_en = 0;
|
i2s->pdm_conf.tx_pdm_en = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
i2sSetSampleRate(bus_num, sample_rate, parallel_mode, bytes_per_sample);
|
||||||
|
|
||||||
i2sSetSampleRate(bus_num, sample_rate, bits_per_sample);
|
/* */
|
||||||
|
//Reset FIFO/DMA -> needed? Doesn't dma_reset/fifo_reset do this?
|
||||||
|
i2s->lc_conf.in_rst=1; i2s->lc_conf.out_rst=1; i2s->lc_conf.ahbm_rst=1; i2s->lc_conf.ahbm_fifo_rst=1;
|
||||||
|
i2s->lc_conf.in_rst=0; i2s->lc_conf.out_rst=0; i2s->lc_conf.ahbm_rst=0; i2s->lc_conf.ahbm_fifo_rst=0;
|
||||||
|
i2s->conf.tx_reset=1; i2s->conf.tx_fifo_reset=1; i2s->conf.rx_fifo_reset=1;
|
||||||
|
i2s->conf.tx_reset=0; i2s->conf.tx_fifo_reset=0; i2s->conf.rx_fifo_reset=0;
|
||||||
|
/* */
|
||||||
|
|
||||||
// enable intr in cpu //
|
// enable intr in cpu //
|
||||||
int i2sIntSource;
|
int i2sIntSource;
|
||||||
@@ -389,12 +507,21 @@ void i2sInit(uint8_t bus_num,
|
|||||||
i2sIntSource = ETS_I2S0_INTR_SOURCE;
|
i2sIntSource = ETS_I2S0_INTR_SOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
esp_intr_alloc(i2sIntSource, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, &i2sDmaISR, &I2S[bus_num], &I2S[bus_num].isr_handle);
|
esp_intr_alloc(i2sIntSource, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, &i2sDmaISR, &I2S[bus_num], &I2S[bus_num].isr_handle);
|
||||||
// enable send intr
|
// enable send intr
|
||||||
i2s->int_ena.out_eof = 1;
|
i2s->int_ena.out_eof = 1;
|
||||||
i2s->int_ena.out_dscr_err = 1;
|
i2s->int_ena.out_dscr_err = 1;
|
||||||
|
|
||||||
|
/* ??? */
|
||||||
|
// Enable and configure DMA
|
||||||
|
{
|
||||||
|
typeof(i2s->lc_conf) lc_conf;
|
||||||
|
lc_conf.val = 0;
|
||||||
|
lc_conf.out_data_burst_en = 1;
|
||||||
|
lc_conf.indscr_burst_en = 1;
|
||||||
|
i2s->lc_conf.val = lc_conf.val;
|
||||||
|
}
|
||||||
|
/* */
|
||||||
i2s->fifo_conf.dscr_en = 1;// enable dma
|
i2s->fifo_conf.dscr_en = 1;// enable dma
|
||||||
i2s->out_link.start = 0;
|
i2s->out_link.start = 0;
|
||||||
i2s->out_link.addr = (uint32_t)(&I2S[bus_num].dma_items[0]); // loads dma_struct to dma
|
i2s->out_link.addr = (uint32_t)(&I2S[bus_num].dma_items[0]); // loads dma_struct to dma
|
||||||
@@ -404,126 +531,103 @@ void i2sInit(uint8_t bus_num,
|
|||||||
esp_intr_enable(I2S[bus_num].isr_handle);
|
esp_intr_enable(I2S[bus_num].isr_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void i2sDeinit(uint8_t bus_num) {
|
void i2sDeinit(uint8_t bus_num)
|
||||||
|
{
|
||||||
i2sDeinitDmaItems(bus_num);
|
i2sDeinitDmaItems(bus_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, uint8_t bits) {
|
esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, bool parallel_mode, size_t bytes_per_sample)
|
||||||
if (bus_num >= I2S_NUM_MAX) {
|
{
|
||||||
|
if (bus_num >= I2S_NUM_MAX)
|
||||||
|
{
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (I2S[bus_num].rate == rate) {
|
uint8_t bck = 12;
|
||||||
return ESP_OK;
|
|
||||||
|
// parallel mode needs a higher sample rate
|
||||||
|
//
|
||||||
|
if (parallel_mode)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
rate *= bytes_per_sample;
|
||||||
|
bck *= bytes_per_sample;
|
||||||
|
|
||||||
|
//rate /= bytes_per_sample;
|
||||||
|
//bck /= bytes_per_sample;
|
||||||
|
#else
|
||||||
|
rate *= bytes_per_sample;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int clkmInteger, clkmDecimals, bck = 0;
|
// 160,000,000L / (100,000 * 384)
|
||||||
double denom = (double)1 / 63;
|
double clkmdiv = (double)I2S_BASE_CLK / ((rate * 384) + 1);
|
||||||
int channel = 2;
|
if (clkmdiv > 256.0)
|
||||||
|
{
|
||||||
// double mclk;
|
|
||||||
double clkmdiv;
|
|
||||||
|
|
||||||
int factor;
|
|
||||||
|
|
||||||
if (bits == 8) {
|
|
||||||
factor = 120;
|
|
||||||
} else {
|
|
||||||
factor = (256 % bits) ? 384 : 256;
|
|
||||||
}
|
|
||||||
|
|
||||||
clkmdiv = (double)I2S_BASE_CLK / (rate* factor);
|
|
||||||
if (clkmdiv > 256) {
|
|
||||||
log_e("rate is too low");
|
log_e("rate is too low");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
I2S[bus_num].rate = rate;
|
else if (clkmdiv < 2.0)
|
||||||
|
{
|
||||||
clkmInteger = clkmdiv;
|
log_e("rate is too fast, clkmdiv = %f (%u, %u, %u)",
|
||||||
clkmDecimals = ((clkmdiv - clkmInteger) / denom);
|
clkmdiv,
|
||||||
|
rate,
|
||||||
if (bits == 8) {
|
parallel_mode,
|
||||||
// mclk = rate* factor;
|
bytes_per_sample);
|
||||||
bck = 60;
|
return ESP_FAIL;
|
||||||
bits = 16;
|
|
||||||
} else {
|
|
||||||
// mclk = (double)clkmInteger + (denom* clkmDecimals);
|
|
||||||
bck = factor/(bits* channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i2sSetClock(bus_num, clkmInteger, clkmDecimals, 63, bck, bits);
|
// calc integer and franctional for more precise timing
|
||||||
|
//
|
||||||
|
uint8_t clkmInteger = clkmdiv;
|
||||||
|
uint8_t clkmFraction = (clkmdiv - clkmInteger) * 63.0;
|
||||||
|
|
||||||
|
i2sSetClock(bus_num, clkmInteger, clkmFraction, 63, bck, bytes_per_sample * 8);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void IRAM_ATTR i2sDmaISR(void* arg)
|
void IRAM_ATTR i2sDmaISR(void* arg)
|
||||||
{
|
{
|
||||||
i2s_bus_t* dev = (i2s_bus_t*)(arg);
|
i2s_bus_t* i2s = (i2s_bus_t*)(arg);
|
||||||
|
|
||||||
if (dev->bus->int_st.out_eof)
|
if (i2s->bus->int_st.out_eof)
|
||||||
{
|
{
|
||||||
// i2s_dma_item_t* item = (i2s_dma_item_t*)(dev->bus->out_eof_des_addr);
|
// lldesc_t* item = (lldesc_t*)(i2s->bus->out_eof_des_addr);
|
||||||
if (dev->is_sending_data == I2s_Is_Pending)
|
if (i2s->is_sending_data != I2s_Is_Idle)
|
||||||
{
|
{
|
||||||
dev->is_sending_data = I2s_Is_Idle;
|
// the second item (last of the two front silent items) is
|
||||||
}
|
// silent looping item
|
||||||
else if (dev->is_sending_data == I2s_Is_Sending)
|
lldesc_t* itemLoop = &i2s->dma_items[0];
|
||||||
{
|
lldesc_t* itemLoopBreaker = itemLoop + 1;
|
||||||
// loop the silent items
|
// set to loop on silent items
|
||||||
i2s_dma_item_t* itemSilence = &dev->dma_items[1];
|
itemLoopBreaker->qe.stqe_next = itemLoop;
|
||||||
itemSilence->next = &dev->dma_items[0];
|
|
||||||
|
|
||||||
dev->is_sending_data = I2s_Is_Pending;
|
i2s->is_sending_data = I2s_Is_Idle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->bus->int_clr.val = dev->bus->int_st.val;
|
i2s->bus->int_clr.val = i2s->bus->int_st.val;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent) {
|
bool i2sWrite(uint8_t bus_num)
|
||||||
if (bus_num >= I2S_NUM_MAX || !I2S[bus_num].tx_queue) {
|
{
|
||||||
return 0;
|
if (bus_num >= I2S_NUM_MAX)
|
||||||
}
|
{
|
||||||
size_t blockSize = len;
|
return false;
|
||||||
|
|
||||||
i2s_dma_item_t* item = &I2S[bus_num].dma_items[0];
|
|
||||||
size_t dataLeft = len;
|
|
||||||
uint8_t* pos = data;
|
|
||||||
|
|
||||||
// skip front two silent items
|
|
||||||
item += 2;
|
|
||||||
|
|
||||||
while (dataLeft) {
|
|
||||||
|
|
||||||
blockSize = dataLeft;
|
|
||||||
if (blockSize > I2S_DMA_MAX_DATA_LEN) {
|
|
||||||
blockSize = I2S_DMA_MAX_DATA_LEN;
|
|
||||||
}
|
|
||||||
dataLeft -= blockSize;
|
|
||||||
|
|
||||||
// data is constant. no need to copy
|
|
||||||
item->data = pos;
|
|
||||||
item->blocksize = blockSize;
|
|
||||||
item->datalen = blockSize;
|
|
||||||
|
|
||||||
item++;
|
|
||||||
|
|
||||||
pos += blockSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the second item (last of the two front silent items) is
|
||||||
|
// silent looping item
|
||||||
|
lldesc_t* itemLoopBreaker = &I2S[bus_num].dma_items[1];
|
||||||
|
lldesc_t* itemLoopNext = itemLoopBreaker + 1;
|
||||||
|
|
||||||
|
// set to NOT loop on silent items
|
||||||
|
itemLoopBreaker->qe.stqe_next = itemLoopNext;
|
||||||
|
|
||||||
// reset silence item to not loop
|
|
||||||
item = &I2S[bus_num].dma_items[1];
|
|
||||||
item->next = &I2S[bus_num].dma_items[2];
|
|
||||||
I2S[bus_num].is_sending_data = I2s_Is_Sending;
|
I2S[bus_num].is_sending_data = I2s_Is_Sending;
|
||||||
|
|
||||||
|
|
||||||
xQueueReset(I2S[bus_num].tx_queue);
|
return true;
|
||||||
xQueueSend(I2S[bus_num].tx_queue, (void*)&I2S[bus_num].dma_items[0], 10);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
@@ -20,21 +20,21 @@ typedef enum {
|
|||||||
} i2s_tx_fifo_mod_t;
|
} i2s_tx_fifo_mod_t;
|
||||||
|
|
||||||
void i2sInit(uint8_t bus_num,
|
void i2sInit(uint8_t bus_num,
|
||||||
uint32_t bits_per_sample,
|
bool parallel_mode,
|
||||||
|
size_t bytes_per_sample,
|
||||||
uint32_t sample_rate,
|
uint32_t sample_rate,
|
||||||
i2s_tx_chan_mod_t chan_mod,
|
i2s_tx_chan_mod_t chan_mod,
|
||||||
i2s_tx_fifo_mod_t fifo_mod,
|
i2s_tx_fifo_mod_t fifo_mod,
|
||||||
size_t dma_count,
|
size_t dma_count,
|
||||||
size_t dma_len);
|
uint8_t* data,
|
||||||
|
size_t dataSize);
|
||||||
void i2sDeinit(uint8_t bus_num);
|
void i2sDeinit(uint8_t bus_num);
|
||||||
|
void i2sSetPins(uint8_t bus_num,
|
||||||
void i2sSetPins(uint8_t bus_num, int8_t out, bool invert);
|
int8_t out,
|
||||||
|
int8_t parallel,
|
||||||
esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits_per_sample);
|
int8_t busSampleSize,
|
||||||
esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, uint8_t bits_per_sample);
|
bool invert);
|
||||||
|
bool i2sWrite(uint8_t bus_num);
|
||||||
size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent);
|
|
||||||
bool i2sWriteDone(uint8_t bus_num);
|
bool i2sWriteDone(uint8_t bus_num);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@@ -100,6 +100,12 @@ public:
|
|||||||
_wire.endTransaction();
|
_wire.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
@@ -100,6 +100,12 @@ public:
|
|||||||
_wire.endTransaction();
|
_wire.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
@@ -91,6 +91,12 @@ public:
|
|||||||
_endTime = micros();
|
_endTime = micros();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
@@ -187,6 +187,12 @@ public:
|
|||||||
_endTime = micros();
|
_endTime = micros();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
@@ -170,7 +170,7 @@ public:
|
|||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
i2sSetPins(_bus.I2sBusNumber, -1, false);
|
i2sSetPins(_bus.I2sBusNumber, -1, -1, -1, false);
|
||||||
i2sDeinit(_bus.I2sBusNumber);
|
i2sDeinit(_bus.I2sBusNumber);
|
||||||
free(_data);
|
free(_data);
|
||||||
heap_caps_free(_i2sBuffer);
|
heap_caps_free(_i2sBuffer);
|
||||||
@@ -186,13 +186,15 @@ public:
|
|||||||
size_t dmaBlockCount = (_i2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN;
|
size_t dmaBlockCount = (_i2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN;
|
||||||
|
|
||||||
i2sInit(_bus.I2sBusNumber,
|
i2sInit(_bus.I2sBusNumber,
|
||||||
16,
|
false,
|
||||||
|
2, // bytes per sample
|
||||||
T_SPEED::I2sSampleRate,
|
T_SPEED::I2sSampleRate,
|
||||||
I2S_CHAN_STEREO,
|
I2S_CHAN_STEREO,
|
||||||
I2S_FIFO_16BIT_DUAL,
|
I2S_FIFO_16BIT_DUAL,
|
||||||
dmaBlockCount,
|
dmaBlockCount,
|
||||||
0);
|
_i2sBuffer,
|
||||||
i2sSetPins(_bus.I2sBusNumber, _pin, T_INVERT::Inverted);
|
_i2sBufferSize);
|
||||||
|
i2sSetPins(_bus.I2sBusNumber, _pin, -1, -1, T_INVERT::Inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update(bool)
|
void Update(bool)
|
||||||
@@ -205,7 +207,13 @@ public:
|
|||||||
|
|
||||||
FillBuffers();
|
FillBuffers();
|
||||||
|
|
||||||
i2sWrite(_bus.I2sBusNumber, _i2sBuffer, _i2sBufferSize, false, false);
|
i2sWrite(_bus.I2sBusNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
@@ -229,7 +237,7 @@ private:
|
|||||||
|
|
||||||
uint8_t* _data; // Holds LED color values
|
uint8_t* _data; // Holds LED color values
|
||||||
|
|
||||||
uint32_t _i2sBufferSize; // total size of _i2sBuffer
|
size_t _i2sBufferSize; // total size of _i2sBuffer
|
||||||
uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc
|
uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc
|
||||||
|
|
||||||
void construct(uint16_t pixelCount, size_t elementSize, size_t settingsSize)
|
void construct(uint16_t pixelCount, size_t elementSize, size_t settingsSize)
|
||||||
|
796
src/internal/NeoEsp32I2sXMethod.h
Normal file
796
src/internal/NeoEsp32I2sXMethod.h
Normal file
@@ -0,0 +1,796 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
NeoPixel library helper functions for Esp32.
|
||||||
|
|
||||||
|
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/>.
|
||||||
|
-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
// ESP32C3/S3 I2S is not supported yet due to significant changes to interface
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "Esp32_i2s.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ESP32 Endian Map
|
||||||
|
// uint16_t
|
||||||
|
// 1234
|
||||||
|
// 3412
|
||||||
|
// uint32_t
|
||||||
|
// 12345678
|
||||||
|
// 78563412
|
||||||
|
// uint64_t
|
||||||
|
// 0123456789abcdef
|
||||||
|
// efcdab8967452301
|
||||||
|
|
||||||
|
//
|
||||||
|
// true size of mux channel, 8 bit
|
||||||
|
//
|
||||||
|
class NeoEspI2sMuxBusSize8Bit
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NeoEspI2sMuxBusSize8Bit() {};
|
||||||
|
|
||||||
|
const static size_t MuxBusDataSize = 1;
|
||||||
|
|
||||||
|
static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
// 1234 - order
|
||||||
|
// 3412 = actual due to endianness
|
||||||
|
// 00000001
|
||||||
|
const uint32_t EncodedZeroBit = 0x00000100;
|
||||||
|
// 00010101
|
||||||
|
const uint32_t EncodedOneBit = 0x01000101;
|
||||||
|
#else
|
||||||
|
// 8 channel bits layout for DMA 32bit value
|
||||||
|
// note, right to left
|
||||||
|
// mux bus bit/id 76543210 76543210 76543210 76543210
|
||||||
|
// encode bit # 3 2 1 0
|
||||||
|
// value zero 0 0 0 1
|
||||||
|
// value one 0 1 1 1
|
||||||
|
//
|
||||||
|
// due to indianness between peripheral and cpu, bytes within the words are swapped in the const
|
||||||
|
// 1234 - order
|
||||||
|
// 3412 = actual due to endianness
|
||||||
|
// 00000001
|
||||||
|
const uint32_t EncodedZeroBit = 0x00010000;
|
||||||
|
// 00010101
|
||||||
|
const uint32_t EncodedOneBit = 0x01010001;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t* pDma = reinterpret_cast<uint32_t*>(dmaBuffer);
|
||||||
|
const uint8_t* pEnd = data + sizeData;
|
||||||
|
|
||||||
|
for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++)
|
||||||
|
{
|
||||||
|
uint8_t value = *pPixel;
|
||||||
|
|
||||||
|
for (uint8_t bit = 0; bit < 8; bit++)
|
||||||
|
{
|
||||||
|
uint32_t dma = *(pDma);
|
||||||
|
|
||||||
|
dma |= (((value & 0x80) ? EncodedOneBit : EncodedZeroBit) << (muxId));
|
||||||
|
*(pDma++) = dma;
|
||||||
|
value <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// true size of mux channel, 16 bit
|
||||||
|
//
|
||||||
|
class NeoEspI2sMuxBusSize16Bit
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NeoEspI2sMuxBusSize16Bit() {};
|
||||||
|
|
||||||
|
const static size_t MuxBusDataSize = 2;
|
||||||
|
|
||||||
|
static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
// 1234 5678 - order
|
||||||
|
// 3412 7856 = actual due to endianness
|
||||||
|
// not swap 0000000000000001
|
||||||
|
const uint64_t EncodedZeroBit64 = 0x0000000000010000;
|
||||||
|
// no swap 0000000100010001
|
||||||
|
const uint64_t EncodedOneBit64 = 0x0001000000010001;
|
||||||
|
// can be shifted by 8!
|
||||||
|
Fillx16(dmaBuffer,
|
||||||
|
data,
|
||||||
|
sizeData,
|
||||||
|
muxId,
|
||||||
|
EncodedZeroBit64,
|
||||||
|
EncodedOneBit64);
|
||||||
|
#else
|
||||||
|
|
||||||
|
// 16 channel bits layout for DMA 64bit value
|
||||||
|
// note, right to left, destination is 32bit chunks
|
||||||
|
// due to indianness between peripheral and cpu,
|
||||||
|
// bytes within the words are swapped and words within dwords
|
||||||
|
// in the literal constants
|
||||||
|
// { } { }
|
||||||
|
// 0123 4567 89ab cdef - order of bytes in literal constant
|
||||||
|
// efcd ab89 6745 2301 - order of memory on ESP32 due to Endianness
|
||||||
|
// 6745 2301 efcd ab89 - 32bit dest means only map using 32bits so swap upper and lower
|
||||||
|
//
|
||||||
|
// Due to final bit locations, can't shift encoded one bit
|
||||||
|
// either left more than 7 or right more than 7 so we have to
|
||||||
|
// split the updates and use different encodings
|
||||||
|
if (muxId < 8)
|
||||||
|
{
|
||||||
|
// endian + dest swap 0000000000000001
|
||||||
|
const uint64_t EncodedZeroBit64 = 0x0000000001000000;
|
||||||
|
// endian + dest swap 0000000100010001
|
||||||
|
const uint64_t EncodedOneBit64 = 0x0100000001000100;
|
||||||
|
// cant be shifted by 8!
|
||||||
|
Fillx16(dmaBuffer,
|
||||||
|
data,
|
||||||
|
sizeData,
|
||||||
|
muxId,
|
||||||
|
EncodedZeroBit64,
|
||||||
|
EncodedOneBit64);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// endian + dest swap 0000000000000001
|
||||||
|
// then pre shift by 8 0000000000000100
|
||||||
|
const uint64_t EncodedZeroBit64 = 0x0000000000010000;
|
||||||
|
// endian + dest swap 0000000100010001
|
||||||
|
// then pre shift by 8 0000010001000100
|
||||||
|
const uint64_t EncodedOneBit64 = 0x0001000000010001;
|
||||||
|
Fillx16(dmaBuffer,
|
||||||
|
data,
|
||||||
|
sizeData,
|
||||||
|
muxId - 8, // preshifted
|
||||||
|
EncodedZeroBit64,
|
||||||
|
EncodedOneBit64);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void Fillx16(uint8_t* dmaBuffer,
|
||||||
|
const uint8_t* data,
|
||||||
|
size_t sizeData,
|
||||||
|
uint8_t muxShift,
|
||||||
|
const uint64_t EncodedZeroBit64,
|
||||||
|
const uint64_t EncodedOneBit64)
|
||||||
|
{
|
||||||
|
uint64_t* pDma64 = reinterpret_cast<uint64_t*>(dmaBuffer);
|
||||||
|
const uint8_t* pEnd = data + sizeData;
|
||||||
|
|
||||||
|
for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++)
|
||||||
|
{
|
||||||
|
uint8_t value = *pPixel;
|
||||||
|
|
||||||
|
for (uint8_t bit = 0; bit < 8; bit++)
|
||||||
|
{
|
||||||
|
uint64_t dma64 = *(pDma64);
|
||||||
|
|
||||||
|
dma64 |= (((value & 0x80) ? EncodedOneBit64 : EncodedZeroBit64) << (muxShift));
|
||||||
|
*(pDma64++) = dma64;
|
||||||
|
value <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// tracks mux channels used and if updated
|
||||||
|
//
|
||||||
|
// T_FLAG - type used to store bit flags, UINT8_t for 8 channels, UINT16_t for 16
|
||||||
|
// T_MUXSIZE - true size of mux channel = NeoEspI2sMuxBusSize8Bit or NeoEspI2sMuxBusSize16Bit
|
||||||
|
//
|
||||||
|
template<typename T_FLAG, typename T_MUXSIZE> class NeoEspI2sMuxMap : public T_MUXSIZE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const static uint8_t InvalidMuxId = -1;
|
||||||
|
const static size_t BusMaxCount = sizeof(T_FLAG) * 8;
|
||||||
|
|
||||||
|
size_t MaxBusDataSize; // max size of stream data from any single mux bus
|
||||||
|
T_FLAG UpdateMap; // bitmap flags of mux buses to track update state
|
||||||
|
T_FLAG UpdateMapMask; // mask to used bits in s_UpdateMap
|
||||||
|
T_FLAG BusCount; // count of mux buses
|
||||||
|
|
||||||
|
// 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
|
||||||
|
NeoEspI2sMuxMap()
|
||||||
|
// //:
|
||||||
|
// //MaxBusDataSize(0),
|
||||||
|
// //UpdateMap(0),
|
||||||
|
// //UpdateMapMask(0),
|
||||||
|
// //BusCount(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RegisterNewMuxBus(const size_t dataSize)
|
||||||
|
{
|
||||||
|
// find first available bus id
|
||||||
|
uint8_t muxId = 0;
|
||||||
|
while (muxId < BusMaxCount)
|
||||||
|
{
|
||||||
|
T_FLAG muxIdField = (1 << muxId);
|
||||||
|
if ((UpdateMapMask & muxIdField) == 0)
|
||||||
|
{
|
||||||
|
// complete registration
|
||||||
|
BusCount++;
|
||||||
|
UpdateMapMask |= muxIdField;
|
||||||
|
if (dataSize > MaxBusDataSize)
|
||||||
|
{
|
||||||
|
MaxBusDataSize = dataSize;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
muxId++;
|
||||||
|
}
|
||||||
|
if (muxId == BusMaxCount)
|
||||||
|
{
|
||||||
|
log_e("exceded channel limit of %u on bus", BusMaxCount);
|
||||||
|
}
|
||||||
|
return muxId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DeregisterMuxBus(uint8_t muxId)
|
||||||
|
{
|
||||||
|
T_FLAG muxIdField = (1 << muxId);
|
||||||
|
if (UpdateMapMask & muxIdField)
|
||||||
|
{
|
||||||
|
// complete deregistration
|
||||||
|
BusCount--;
|
||||||
|
UpdateMapMask &= ~muxIdField;
|
||||||
|
if (UpdateMapMask == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAllMuxBusesUpdated()
|
||||||
|
{
|
||||||
|
return (UpdateMap == UpdateMapMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNoMuxBusesUpdate()
|
||||||
|
{
|
||||||
|
return (UpdateMap == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkMuxBusUpdated(uint8_t muxId)
|
||||||
|
{
|
||||||
|
UpdateMap |= (1 << muxId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetMuxBusesUpdated()
|
||||||
|
{
|
||||||
|
UpdateMap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
MaxBusDataSize = 0;
|
||||||
|
UpdateMap = 0;
|
||||||
|
UpdateMapMask = 0;
|
||||||
|
BusCount = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Implementation of the low level interface into i2s mux bus
|
||||||
|
//
|
||||||
|
// T_BUSCONTEXT - the context to use, currently only NeoEspI2sDblBuffContext but there is
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
template<typename T_BUSCONTEXT, typename T_BUS> class NeoEsp32I2sMuxBus
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NeoEsp32I2sMuxBus() :
|
||||||
|
_muxId(s_context.MuxMap.InvalidMuxId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterNewMuxBus(size_t dataSize)
|
||||||
|
{
|
||||||
|
_muxId = s_context.MuxMap.RegisterNewMuxBus(dataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(uint8_t pin, uint32_t i2sSampleRate, bool invert)
|
||||||
|
{
|
||||||
|
s_context.Construct(T_BUS::I2sBusNumber, i2sSampleRate);
|
||||||
|
i2sSetPins(T_BUS::I2sBusNumber, pin, _muxId, s_context.MuxMap.MuxBusDataSize, invert);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeregisterMuxBus()
|
||||||
|
{
|
||||||
|
if (s_context.MuxMap.DeregisterMuxBus(_muxId))
|
||||||
|
{
|
||||||
|
s_context.Destruct(T_BUS::I2sBusNumber);
|
||||||
|
}
|
||||||
|
// disconnect muxed pin?
|
||||||
|
_muxId = s_context.MuxMap.InvalidMuxId;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsWriteDone()
|
||||||
|
{
|
||||||
|
return i2sWriteDone(T_BUS::I2sBusNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkUpdated()
|
||||||
|
{
|
||||||
|
s_context.MuxMap.MarkMuxBusUpdated(_muxId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static T_BUSCONTEXT s_context;
|
||||||
|
uint8_t _muxId;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T_BUSCONTEXT, typename T_BUS> T_BUSCONTEXT NeoEsp32I2sMuxBus<T_BUSCONTEXT, T_BUS>::s_context = T_BUSCONTEXT();
|
||||||
|
|
||||||
|
//
|
||||||
|
// wrapping layer of the i2s mux bus as a NeoMethod
|
||||||
|
//
|
||||||
|
// T_SPEED - NeoEsp32I2sSpeed* (ex NeoEsp32I2sSpeedWs2812x) used to define output signal form
|
||||||
|
// T_BUS - NeoEsp32I2sMuxBus, the bus to use
|
||||||
|
// T_INVERT - NeoEsp32I2sNotInverted or NeoEsp32I2sInverted, will invert output signal
|
||||||
|
//
|
||||||
|
template<typename T_SPEED, typename T_BUS, typename T_INVERT> class NeoEsp32I2sXMethodBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef NeoNoSettings SettingsObject;
|
||||||
|
|
||||||
|
NeoEsp32I2sXMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||||
|
_sizeData(pixelCount * elementSize + settingsSize),
|
||||||
|
_pin(pin),
|
||||||
|
_bus()
|
||||||
|
{
|
||||||
|
_bus.RegisterNewMuxBus(_sizeData + T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
~NeoEsp32I2sXMethodBase()
|
||||||
|
{
|
||||||
|
while (!_bus.IsWriteDone())
|
||||||
|
{
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
_bus.DeregisterMuxBus();
|
||||||
|
|
||||||
|
free(_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsReadyToUpdate() const
|
||||||
|
{
|
||||||
|
return _bus.IsWriteDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize()
|
||||||
|
{
|
||||||
|
_bus.Initialize(_pin, T_SPEED::I2sSampleRate, T_INVERT::Inverted);
|
||||||
|
|
||||||
|
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||||
|
if (_data == nullptr)
|
||||||
|
{
|
||||||
|
log_e("front buffer memory allocation failure");
|
||||||
|
}
|
||||||
|
// data cleared later in Begin()
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(bool)
|
||||||
|
{
|
||||||
|
_bus.FillBuffers(_data, _sizeData);
|
||||||
|
_bus.StartWrite(); // only triggers actual write after all mux busses have updated
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called even if no changes to method buffer
|
||||||
|
// as edit buffer is always cleared and then copied to send buffer and all
|
||||||
|
// mux bus needs to included
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* getData() const
|
||||||
|
{
|
||||||
|
return _data;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t getDataSize() const
|
||||||
|
{
|
||||||
|
return _sizeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const size_t _sizeData; // Size of '_data' buffer
|
||||||
|
const uint8_t _pin; // output pin number
|
||||||
|
|
||||||
|
T_BUS _bus; // holds instance for mux bus support
|
||||||
|
uint8_t* _data; // Holds LED color values
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
|
||||||
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
||||||
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
||||||
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
|
||||||
|
|
||||||
|
|
||||||
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux8Bus;
|
||||||
|
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux16Bus;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NORMAL
|
||||||
|
//
|
||||||
|
|
||||||
|
// I2s0x8
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Ws2812xMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Sk6812Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Tm1814Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Tm1829Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Tm1914Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8800KbpsMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8400KbpsMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Apa106Method;
|
||||||
|
|
||||||
|
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2813Method;
|
||||||
|
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2812dMethod;
|
||||||
|
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2811Method;
|
||||||
|
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2816Method;
|
||||||
|
typedef NeoEsp32I2s0X8800KbpsMethod NeoEsp32I2s0X8Ws2812Method;
|
||||||
|
typedef NeoEsp32I2s0X8Sk6812Method NeoEsp32I2s0X8Sk6812Method;
|
||||||
|
typedef NeoEsp32I2s0X8Tm1814Method NeoEsp32I2s0X8Tm1814Method;
|
||||||
|
typedef NeoEsp32I2s0X8Tm1829Method NeoEsp32I2s0X8Tm1829Method;
|
||||||
|
typedef NeoEsp32I2s0X8Tm1914Method NeoEsp32I2s0X8Tm1914Method;
|
||||||
|
typedef NeoEsp32I2s0X8Sk6812Method NeoEsp32I2s0X8Lc8812Method;
|
||||||
|
typedef NeoEsp32I2s0X8Apa106Method NeoEsp32I2s0X8Apa106Method;
|
||||||
|
|
||||||
|
// I2s0x16
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Ws2812xMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Sk6812Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Tm1814Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Tm1829Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Tm1914Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16800KbpsMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16400KbpsMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Apa106Method;
|
||||||
|
|
||||||
|
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2813Method;
|
||||||
|
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2812dMethod;
|
||||||
|
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2811Method;
|
||||||
|
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2816Method;
|
||||||
|
typedef NeoEsp32I2s0X16800KbpsMethod NeoEsp32I2s0X16Ws2812Method;
|
||||||
|
typedef NeoEsp32I2s0X16Sk6812Method NeoEsp32I2s0X16Sk6812Method;
|
||||||
|
typedef NeoEsp32I2s0X16Tm1814Method NeoEsp32I2s0X16Tm1814Method;
|
||||||
|
typedef NeoEsp32I2s0X16Tm1829Method NeoEsp32I2s0X16Tm1829Method;
|
||||||
|
typedef NeoEsp32I2s0X16Tm1914Method NeoEsp32I2s0X16Tm1914Method;
|
||||||
|
typedef NeoEsp32I2s0X16Sk6812Method NeoEsp32I2s0X16Lc8812Method;
|
||||||
|
typedef NeoEsp32I2s0X16Apa106Method NeoEsp32I2s0X16Apa106Method;
|
||||||
|
|
||||||
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
|
||||||
|
// I2s1x8
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2812xMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Sk6812Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Tm1814Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Tm1829Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Tm1914Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8800KbpsMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8400KbpsMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Apa106Method;
|
||||||
|
|
||||||
|
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2813Method;
|
||||||
|
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2812dMethod;
|
||||||
|
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2811Method;
|
||||||
|
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2816Method;
|
||||||
|
typedef NeoEsp32I2s1X8800KbpsMethod NeoEsp32I2s1X8Ws2812Method;
|
||||||
|
typedef NeoEsp32I2s1X8Sk6812Method NeoEsp32I2s1X8Sk6812Method;
|
||||||
|
typedef NeoEsp32I2s1X8Tm1814Method NeoEsp32I2s1X8Tm1814Method;
|
||||||
|
typedef NeoEsp32I2s1X8Tm1829Method NeoEsp32I2s1X8Tm1829Method;
|
||||||
|
typedef NeoEsp32I2s1X8Tm1914Method NeoEsp32I2s1X8Tm1914Method;
|
||||||
|
typedef NeoEsp32I2s1X8Sk6812Method NeoEsp32I2s1X8Lc8812Method;
|
||||||
|
typedef NeoEsp32I2s1X8Apa106Method NeoEsp32I2s1X8Apa106Method;
|
||||||
|
|
||||||
|
// I2s1x16
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Ws2812xMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Sk6812Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Tm1814Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Tm1829Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Tm1914Method;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16800KbpsMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16400KbpsMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Apa106Method;
|
||||||
|
|
||||||
|
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2813Method;
|
||||||
|
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2812dMethod;
|
||||||
|
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2811Method;
|
||||||
|
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2816Method;
|
||||||
|
typedef NeoEsp32I2s1X16800KbpsMethod NeoEsp32I2s1X16Ws2812Method;
|
||||||
|
typedef NeoEsp32I2s1X16Sk6812Method NeoEsp32I2s1X16Sk6812Method;
|
||||||
|
typedef NeoEsp32I2s1X16Tm1814Method NeoEsp32I2s1X16Tm1814Method;
|
||||||
|
typedef NeoEsp32I2s1X16Tm1829Method NeoEsp32I2s1X16Tm1829Method;
|
||||||
|
typedef NeoEsp32I2s1X16Tm1914Method NeoEsp32I2s1X16Tm1914Method;
|
||||||
|
typedef NeoEsp32I2s1X16Sk6812Method NeoEsp32I2s1X16Lc8812Method;
|
||||||
|
typedef NeoEsp32I2s1X16Apa106Method NeoEsp32I2s1X16Apa106Method;
|
||||||
|
|
||||||
|
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
|
||||||
|
// INVERTED
|
||||||
|
//
|
||||||
|
// I2s0x8 INVERTED
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Ws2812xInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Sk6812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Tm1814InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Tm1829InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Tm1914InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8800KbpsInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8400KbpsInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Apa106InvertedMethod;
|
||||||
|
|
||||||
|
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2813InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2812xInvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2811InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2816InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X8800KbpsInvertedMethod NeoEsp32I2s0X8Ws2812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X8Sk6812InvertedMethod NeoEsp32I2s0X8Sk6812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X8Tm1814InvertedMethod NeoEsp32I2s0X8Tm1814InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X8Tm1829InvertedMethod NeoEsp32I2s0X8Tm1829InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X8Tm1914InvertedMethod NeoEsp32I2s0X8Tm1914InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X8Sk6812InvertedMethod NeoEsp32I2s0X8Lc8812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X8Apa106InvertedMethod NeoEsp32I2s0X8Apa106InvertedMethod;
|
||||||
|
|
||||||
|
// I2s0x16 INVERTED
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Ws2812xInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Sk6812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Tm1814InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Tm1829InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Tm1914InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16800KbpsInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16400KbpsInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Apa106InvertedMethod;
|
||||||
|
|
||||||
|
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2813InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2812xInvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2811InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2816InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X16800KbpsInvertedMethod NeoEsp32I2s0X16Ws2812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X16Sk6812InvertedMethod NeoEsp32I2s0X16Sk6812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X16Tm1814InvertedMethod NeoEsp32I2s0X16Tm1814InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X16Tm1829InvertedMethod NeoEsp32I2s0X16Tm1829InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X16Tm1914InvertedMethod NeoEsp32I2s0X16Tm1914InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X16Sk6812InvertedMethod NeoEsp32I2s0X16Lc8812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s0X16Apa106InvertedMethod NeoEsp32I2s0X16Apa106InvertedMethod;
|
||||||
|
|
||||||
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
|
||||||
|
// I2s1x8 INVERTED
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Ws2812xInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Sk6812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Tm1814InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Tm1829InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Tm1914InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8800KbpsInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8400KbpsInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Apa106InvertedMethod;
|
||||||
|
|
||||||
|
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2813InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2812xInvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2811InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2816InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X8800KbpsInvertedMethod NeoEsp32I2s1X8Ws2812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X8Sk6812InvertedMethod NeoEsp32I2s1X8Sk6812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X8Tm1814InvertedMethod NeoEsp32I2s1X8Tm1814InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X8Tm1829InvertedMethod NeoEsp32I2s1X8Tm1829InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X8Tm1914InvertedMethod NeoEsp32I2s1X8Tm1914InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X8Sk6812InvertedMethod NeoEsp32I2s1X8Lc8812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X8Apa106InvertedMethod NeoEsp32I2s1X8Apa106InvertedMethod;
|
||||||
|
|
||||||
|
// I2s1x16 INVERTED
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Ws2812xInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Sk6812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Tm1814InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Tm1829InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Tm1914InvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16800KbpsInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16400KbpsInvertedMethod;
|
||||||
|
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Apa106InvertedMethod;
|
||||||
|
|
||||||
|
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2813InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2812xInvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2811InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2816InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X16800KbpsInvertedMethod NeoEsp32I2s1X16Ws2812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X16Sk6812InvertedMethod NeoEsp32I2s1X16Sk6812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X16Tm1814InvertedMethod NeoEsp32I2s1X16Tm1814InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X16Tm1829InvertedMethod NeoEsp32I2s1X16Tm1829InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X16Tm1914InvertedMethod NeoEsp32I2s1X16Tm1914InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X16Sk6812InvertedMethod NeoEsp32I2s1X16Lc8812InvertedMethod;
|
||||||
|
typedef NeoEsp32I2s1X16Apa106InvertedMethod NeoEsp32I2s1X16Apa106InvertedMethod;
|
||||||
|
|
||||||
|
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
|
||||||
|
#endif // defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
@@ -587,6 +587,12 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _dataEditing;
|
return _dataEditing;
|
||||||
|
@@ -261,6 +261,12 @@ public:
|
|||||||
_dmaState = NeoDmaState_Pending;
|
_dmaState = NeoDmaState_Pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
@@ -198,6 +198,12 @@ public:
|
|||||||
_dmaState = NeoDmaState_Pending;
|
_dmaState = NeoDmaState_Pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data + T_SPEED::HeaderSize;
|
return _data + T_SPEED::HeaderSize;
|
||||||
|
@@ -396,6 +396,12 @@ public:
|
|||||||
this->UpdateUart(maintainBufferConsistency);
|
this->UpdateUart(maintainBufferConsistency);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return this->_data;
|
return this->_data;
|
||||||
|
@@ -308,6 +308,12 @@ public:
|
|||||||
_endTime = micros();
|
_endTime = micros();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
@@ -400,6 +400,12 @@ public:
|
|||||||
dmaStart();
|
dmaStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
@@ -96,6 +96,12 @@ public:
|
|||||||
_wire.endTransaction();
|
_wire.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
@@ -164,6 +164,12 @@ public:
|
|||||||
digitalWrite(_pinOutputEnable, LOW);
|
digitalWrite(_pinOutputEnable, LOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
@@ -102,6 +102,12 @@ public:
|
|||||||
_endTime = micros();
|
_endTime = micros();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AlwaysUpdate()
|
||||||
|
{
|
||||||
|
// this method requires update to be called only if changes to buffer
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getData() const
|
uint8_t* getData() const
|
||||||
{
|
{
|
||||||
return _data;
|
return _data;
|
||||||
|
Reference in New Issue
Block a user