diff --git a/src/NeoPixelBus.h b/src/NeoPixelBus.h index f6a6b7ba..eaf88c6c 100644 --- a/src/NeoPixelBus.h +++ b/src/NeoPixelBus.h @@ -71,6 +71,7 @@ License along with NeoPixel. If not, see #elif defined(ARDUINO_ARCH_ESP32) +#include "internal/NeoEsp32I2sMethod.h" #include "internal/NeoEspBitBangMethod.h" #include "internal/DotStarGenericMethod.h" diff --git a/src/internal/Esp32_i2s.c b/src/internal/Esp32_i2s.c new file mode 100644 index 00000000..93c98e22 --- /dev/null +++ b/src/internal/Esp32_i2s.c @@ -0,0 +1,522 @@ +// WARNING: This file contains code that is more than likely already +// exposed from the Esp32 Arduino API. It will be removed once integration is complete. +// +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if defined(ARDUINO_ARCH_ESP32) + +#include +#include +#include "stdlib.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" + +#include "esp_intr.h" +#include "rom/ets_sys.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/io_mux_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/i2s_struct.h" +#include "soc/dport_reg.h" +#include "soc/sens_reg.h" +#include "driver/gpio.h" +#include "driver/i2s.h" +#include "driver/dac.h" +#include "esp32_i2s.h" +#include "esp32-hal.h" + +#define I2S_BASE_CLK (160000000L) +#define ESP32_REG(addr) (*((volatile uint32_t*)(0x3FF00000+(addr)))) + +#define I2S_DMA_QUEUE_SIZE 16 +#define I2S_DMA_MAX_DATA_LEN 4092// maximum bytes in one dma item +#define I2S_DMA_SILENCE_LEN 256 // bytes + +typedef struct i2s_dma_item_s { + uint32_t blocksize: 12; // datalen + uint32_t datalen : 12; // len*(bits_per_sample/8)*2 => max 2047*8bit/1023*16bit samples + 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) + 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; +} i2s_bus_t; + +static uint8_t i2s_silence_buf[I2S_DMA_SILENCE_LEN]; + +static i2s_bus_t I2S[2] = { + {&I2S0, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_LEN, NULL, I2S_DMA_QUEUE_SIZE, 0, 0}, + {&I2S1, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_LEN, NULL, I2S_DMA_QUEUE_SIZE, 0, 0} +}; + +void IRAM_ATTR i2sDmaISR(void* arg); +bool i2sInitDmaItems(uint8_t bus_num); + +bool i2sInitDmaItems(uint8_t bus_num) { + if (bus_num > 1) { + return false; + } + if (I2S[bus_num].tx_queue) {// already set + return true; + } + + if (I2S[bus_num].dma_items == NULL) { + I2S[bus_num].dma_items = (i2s_dma_item_t*)malloc(I2S[bus_num].dma_count* sizeof(i2s_dma_item_t)); + if (I2S[bus_num].dma_items == NULL) { + log_e("MEM ERROR!"); + return false; + } + } + + int i, i2, a; + i2s_dma_item_t* item; + + for(i=0; ieof = 1; + item->owner = 1; + item->sub_sof = 0; + item->unused = 0; + item->data = I2S[bus_num].silence_buf; + item->blocksize = I2S[bus_num].silence_len; + item->datalen = I2S[bus_num].silence_len; + item->next = &I2S[bus_num].dma_items[i2]; + item->free_ptr = NULL; + if (I2S[bus_num].dma_buf_len) { + item->buf = (uint8_t*)malloc(I2S[bus_num].dma_buf_len); + if (item->buf == NULL) { + log_e("MEM ERROR!"); + for(a=0; abuf = NULL; + } + } + + I2S[bus_num].tx_queue = xQueueCreate(I2S[bus_num].dma_count, sizeof(i2s_dma_item_t*)); + if (I2S[bus_num].tx_queue == NULL) {// memory error + log_e("MEM ERROR!"); + free(I2S[bus_num].dma_items); + I2S[bus_num].dma_items = NULL; + return false; + } + return true; +} + +void i2sSetSilenceBuf(uint8_t bus_num, uint8_t* data, size_t len) { + if (bus_num > 1 || !data || !len) { + return; + } + I2S[bus_num].silence_buf = data; + I2S[bus_num].silence_len = len; +} + +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) { + if (bus_num > 1 || div_a > 63 || div_b > 63 || bck > 63) { + return ESP_FAIL; + } + i2s_dev_t* i2s = I2S[bus_num].bus; + i2s->clkm_conf.clka_en = 0; + i2s->clkm_conf.clkm_div_a = div_a; + i2s->clkm_conf.clkm_div_b = div_b; + i2s->clkm_conf.clkm_div_num = div_num; + i2s->sample_rate_conf.tx_bck_div_num = bck; + i2s->sample_rate_conf.rx_bck_div_num = bck; + i2s->sample_rate_conf.tx_bits_mod = bits; + i2s->sample_rate_conf.rx_bits_mod = bits; + return ESP_OK; +} + +void i2sSetTxDataMode(uint8_t bus_num, i2s_tx_chan_mod_t chan_mod, i2s_tx_fifo_mod_t fifo_mod) { + if (bus_num > 1) { + return; + } + + I2S[bus_num].bus->conf_chan.tx_chan_mod = chan_mod; // 0:dual channel; 1:right channel; 2:left channel; 3:left channel constant; 4:right channel constant; (channels flipped if tx_msb_right == 1) + I2S[bus_num].bus->fifo_conf.tx_fifo_mod = fifo_mod; // 0:16-bit dual channel; 1:16-bit single channel; 2:32-bit dual channel; 3:32-bit single channel data +} + +void i2sSetDac(uint8_t bus_num, bool right, bool left) { + if (bus_num > 1) { + return; + } + + if (!right && !left) { + dac_output_disable(1); + dac_output_disable(2); + dac_i2s_disable(); + I2S[bus_num].bus->conf2.lcd_en = 0; + I2S[bus_num].bus->conf.tx_right_first = 0; + I2S[bus_num].bus->conf2.camera_en = 0; + I2S[bus_num].bus->conf.tx_msb_shift = 1;// I2S signaling + return; + } + + i2sSetPins(bus_num, -1, -1, -1, -1); + I2S[bus_num].bus->conf2.lcd_en = 1; + I2S[bus_num].bus->conf.tx_right_first = 0; + I2S[bus_num].bus->conf2.camera_en = 0; + I2S[bus_num].bus->conf.tx_msb_shift = 0; + dac_i2s_enable(); + + if (right) {// DAC1, right channel, GPIO25 + dac_output_enable(1); + } + if (left) { // DAC2, left channel, GPIO26 + dac_output_enable(2); + } +} + +void i2sSetPins(uint8_t bus_num, int8_t out, int8_t ws, int8_t bck, int8_t in) { + if (bus_num > 1) { + return; + } + + if ((ws >= 0 && I2S[bus_num].ws == -1) || (bck >= 0 && I2S[bus_num].bck == -1) || (out >= 0 && I2S[bus_num].out == -1)) { + i2sSetDac(bus_num, false, false); + } + + if (ws >= 0) { + if (I2S[bus_num].ws != ws) { + if (I2S[bus_num].ws >= 0) { + gpio_matrix_out(I2S[bus_num].ws, 0x100, false, false); + } + I2S[bus_num].ws = ws; + pinMode(ws, OUTPUT); + gpio_matrix_out(ws, bus_num?I2S1O_WS_OUT_IDX:I2S0O_WS_OUT_IDX, false, false); + } + } else if (I2S[bus_num].ws >= 0) { + gpio_matrix_out(I2S[bus_num].ws, 0x100, false, false); + I2S[bus_num].ws = -1; + } + + if (bck >= 0) { + if (I2S[bus_num].bck != bck) { + if (I2S[bus_num].bck >= 0) { + gpio_matrix_out(I2S[bus_num].bck, 0x100, false, false); + } + I2S[bus_num].bck = bck; + pinMode(bck, OUTPUT); + gpio_matrix_out(bck, bus_num?I2S1O_BCK_OUT_IDX:I2S0O_BCK_OUT_IDX, false, false); + } + } else if (I2S[bus_num].bck >= 0) { + gpio_matrix_out(I2S[bus_num].bck, 0x100, false, false); + I2S[bus_num].bck = -1; + } + + if (out >= 0) { + if (I2S[bus_num].out != out) { + if (I2S[bus_num].out >= 0) { + gpio_matrix_out(I2S[bus_num].out, 0x100, false, false); + } + I2S[bus_num].out = out; + pinMode(out, OUTPUT); + gpio_matrix_out(out, bus_num?I2S1O_DATA_OUT23_IDX:I2S0O_DATA_OUT23_IDX, false, false); + } + } else if (I2S[bus_num].out >= 0) { + gpio_matrix_out(I2S[bus_num].out, 0x100, false, false); + I2S[bus_num].out = -1; + } + +} + +bool i2sWriteDone(uint8_t bus_num) { + if (bus_num > 1) { + return false; + } + return (I2S[bus_num].dma_items[0].data == I2S[bus_num].silence_buf && I2S[bus_num].dma_items[1].data == I2S[bus_num].silence_buf); +} + +void i2sInit(uint8_t bus_num, uint32_t bits_per_sample, uint32_t sample_rate, i2s_tx_chan_mod_t chan_mod, i2s_tx_fifo_mod_t fifo_mod, size_t dma_count, size_t dma_len) { + if (bus_num > 1) { + return; + } + + I2S[bus_num].dma_count = dma_count; + I2S[bus_num].dma_buf_len = dma_len & 0xFFF; + + if (!i2sInitDmaItems(bus_num)) { + return; + } + + if (bus_num) { + periph_module_enable(PERIPH_I2S1_MODULE); + } else { + periph_module_enable(PERIPH_I2S0_MODULE); + } + + esp_intr_disable(I2S[bus_num].isr_handle); + i2s_dev_t* i2s = I2S[bus_num].bus; + i2s->out_link.stop = 1; + i2s->conf.tx_start = 0; + i2s->int_ena.val = 0; + i2s->int_clr.val = 0xFFFFFFFF; + 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 + i2s->conf.tx_reset = 1; + i2s->conf.tx_reset = 0; + i2s->conf.rx_reset = 1; + i2s->conf.rx_reset = 0; + + // reset dma + i2s->lc_conf.in_rst = 1; + i2s->lc_conf.in_rst = 0; + i2s->lc_conf.out_rst = 1; + i2s->lc_conf.out_rst = 0; + + // Enable and configure DMA + i2s->lc_conf.check_owner = 0; + i2s->lc_conf.out_loop_test = 0; + i2s->lc_conf.out_auto_wrback = 0; + i2s->lc_conf.out_data_burst_en = 0; + i2s->lc_conf.outdscr_burst_en = 0; + i2s->lc_conf.out_no_restart_clr = 0; + i2s->lc_conf.indscr_burst_en = 0; + i2s->lc_conf.out_eof_mode = 1; + + i2s->pdm_conf.pcm2pdm_conv_en = 0; + i2s->pdm_conf.pdm2pcm_conv_en = 0; + // SET_PERI_REG_BITS(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, 0x1, RTC_CNTL_SOC_CLK_SEL_S); + + + i2s->conf_chan.tx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left + i2s->conf_chan.rx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left + i2s->fifo_conf.tx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel + i2s->fifo_conf.rx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel + + i2s->conf.tx_mono = 0; + i2s->conf.rx_mono = 0; + + i2s->conf.tx_start = 0; + i2s->conf.rx_start = 0; + + i2s->conf.tx_short_sync = 0; + i2s->conf.rx_short_sync = 0; + i2s->conf.tx_msb_shift = (bits_per_sample != 8);// 0:DAC/PCM, 1:I2S + i2s->conf.rx_msb_shift = 0; + + i2s->conf.tx_slave_mod = 0; // Master + + i2s->conf.tx_msb_right = 0; + i2s->conf.tx_right_first = (bits_per_sample == 8); + i2s->conf2.lcd_en = (bits_per_sample == 8); + i2s->conf2.camera_en = 0; + + i2s->fifo_conf.tx_fifo_mod_force_en = 1; + + i2s->pdm_conf.rx_pdm_en = 0; + i2s->pdm_conf.tx_pdm_en = 0; + + i2sSetSampleRate(bus_num, sample_rate, bits_per_sample); + + // enable intr in cpu // + esp_intr_alloc(bus_num?ETS_I2S1_INTR_SOURCE:ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, &i2sDmaISR, &I2S[bus_num], &I2S[bus_num].isr_handle); + // enable send intr + i2s->int_ena.out_eof = 1; + i2s->int_ena.out_dscr_err = 1; + + i2s->fifo_conf.dscr_en = 1;// enable dma + 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.start = 1; // starts dma + i2s->conf.tx_start = 1;// Start I2s module + + esp_intr_enable(I2S[bus_num].isr_handle); +} + +esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, uint8_t bits) { + if (bus_num > 1) { + return ESP_FAIL; + } + + if (I2S[bus_num].rate == rate) { + return ESP_OK; + } + + int clkmInteger, clkmDecimals, bck = 0; + double denom = (double)1 / 63; + int channel = 2; + + double mclk, 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"); + return ESP_FAIL; + } + I2S[bus_num].rate = rate; + + clkmInteger = clkmdiv; + clkmDecimals = ((clkmdiv - clkmInteger) / denom); + + if (bits == 8) { + mclk = rate* factor; + bck = 60; + bits = 16; + } else { + mclk = (double)clkmInteger + (denom* clkmDecimals); + bck = factor/(bits* channel); + } + + i2sSetClock(bus_num, clkmInteger, clkmDecimals, 63, bck, bits); + + return ESP_OK; +} + +void IRAM_ATTR i2sDmaISR(void* arg) +{ + i2s_dma_item_t* dummy = NULL; + i2s_bus_t* dev = (i2s_bus_t*)arg; + portBASE_TYPE hpTaskAwoken = 0; + + if (dev->bus->int_st.out_dscr_err) { + ets_printf("out_dscr_err\n"); + } + while (dev->bus->int_st.out_eof) { + i2s_dma_item_t* item = (i2s_dma_item_t*)dev->bus->out_eof_des_addr; + dev->bus->int_clr.out_eof = 1; + + if (item->data != dev->silence_buf) { + item->data = dev->silence_buf; + item->blocksize = dev->silence_len; + item->datalen = dev->silence_len; + } + if (xQueueIsQueueFullFromISR(dev->tx_queue) == pdTRUE && xQueueReceiveFromISR(dev->tx_queue, &dummy, &hpTaskAwoken) == pdTRUE && dummy->free_ptr) { + free(dummy->free_ptr); + dummy->free_ptr = NULL; + } + xQueueSendFromISR(dev->tx_queue, (void*)&item, &hpTaskAwoken); + } + dev->bus->int_clr.val = dev->bus->int_st.val; + if (hpTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent) { + if (bus_num > 1 || !I2S[bus_num].tx_queue) { + return 0; + } + size_t index = 0; + size_t toSend = len; + size_t limit = I2S_DMA_MAX_DATA_LEN; + i2s_dma_item_t* item = NULL; + + if (I2S[bus_num].dma_buf_len) { + limit = I2S[bus_num].dma_buf_len; + } + + while (len) { + if (xQueueReceive(I2S[bus_num].tx_queue, &item, portMAX_DELAY) == pdFALSE) { + log_e("xQueueReceive failed\n"); + break; + } + if (item->free_ptr) { + free(item->free_ptr); + item->free_ptr = NULL; + } + + toSend = len; + + if (toSend > limit) { + toSend = limit; + } + + if (!copy) { + // data is constant. no need to copy + item->data = data+index; + item->blocksize = toSend; + item->datalen = toSend; + if (free_when_sent && (toSend == len)) { + // free the full data when done + item->free_ptr = data; + } + } else if (item->buf) { + // dma buffers are preallocated + memcpy(item->buf, data+index, toSend); + item->data = item->buf; + item->blocksize = toSend; + item->datalen = toSend; + } else { + // copy data to a new buffer (dma buffers are NULL) + uint8_t* buf = (uint8_t*)malloc(toSend); + if (!buf) { + log_e("buf = NULL; %u/%u (%u/%u)\n", index, len, toSend, esp_get_free_heap_size()); + break; + } + memcpy(buf, data+index, toSend); + item->free_ptr = buf; + item->data = buf; + item->blocksize = toSend; + item->datalen = toSend; + } + len -= toSend; + index += toSend; + } + return index; +} + +#endif diff --git a/src/internal/Esp32_i2s.h b/src/internal/Esp32_i2s.h new file mode 100644 index 00000000..a779e8cf --- /dev/null +++ b/src/internal/Esp32_i2s.h @@ -0,0 +1,38 @@ +#pragma once + +#if defined(ARDUINO_ARCH_ESP32) + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" + +typedef enum { + I2S_CHAN_STEREO, I2S_CHAN_RIGHT_TO_LEFT, I2S_CHAN_LEFT_TO_RIGHT, I2S_CHAN_RIGHT_ONLY, I2S_CHAN_LEFT_ONLY +} i2s_tx_chan_mod_t; + +typedef enum { + I2S_FIFO_16BIT_DUAL, I2S_FIFO_16BIT_SINGLE, I2S_FIFO_32BIT_DUAL, I2S_FIFO_32BIT_SINGLE +} i2s_tx_fifo_mod_t; + +void i2sInit(uint8_t bus_num, uint32_t bits_per_sample, uint32_t sample_rate, i2s_tx_chan_mod_t chan_mod, i2s_tx_fifo_mod_t fifo_mod, size_t dma_count, size_t dma_len); + +void i2sSetPins(uint8_t bus_num, int8_t out, int8_t ws, int8_t bck, int8_t in); +void i2sSetDac(uint8_t bus_num, bool right, bool left); + +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, uint8_t bits_per_sample); + +void i2sSetTxDataMode(uint8_t bus_num, i2s_tx_chan_mod_t chan_mod, i2s_tx_fifo_mod_t fifo_mod); + +void i2sSetSilenceBuf(uint8_t bus_num, uint8_t* data, size_t len); + +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); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/internal/NeoEsp32I2sMethod.h b/src/internal/NeoEsp32I2sMethod.h new file mode 100644 index 00000000..ba5a9008 --- /dev/null +++ b/src/internal/NeoEsp32I2sMethod.h @@ -0,0 +1,189 @@ +/*------------------------------------------------------------------------- +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 +. +-------------------------------------------------------------------------*/ + +#pragma once + +#ifdef ARDUINO_ARCH_ESP32 + +extern "C" +{ +#include +#include "Esp32_i2s.h" +} + +const uint16_t c_dmaBytesPerPixelBytes = 4; +const uint16_t c_dmaBytesPer50us = 20; +const uint32_t c_dmaI2sSampleRate = 100000; + +class NeoEsp32I2sSpeedWs2813 +{ +public: + const static uint16_t I2sSampleRateDiv = 1; + const static uint16_t ResetTimeUs = 250; +}; + +class NeoEsp32I2sSpeed800Kbps +{ +public: + const static uint16_t I2sSampleRateDiv = 1; + const static uint16_t ResetTimeUs = 50; +}; + +class NeoEsp32I2sSpeed400Kbps +{ +public: + const static uint16_t I2sSampleRateDiv = 2; + const static uint16_t ResetTimeUs = 50; +}; + +class NeoEsp32I2sBusZero +{ +public: + const static uint8_t I2sBusNumber = 0; +}; + +class NeoEsp32I2sBusOne +{ +public: + const static uint8_t I2sBusNumber = 1; +}; + +template class NeoEsp32I2sMethodBase +{ +public: + NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize) : + _pin(pin) + { + uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize; + uint16_t resetSize = (c_dmaBytesPer50us * T_SPEED::ResetTimeUs / 50 / T_SPEED::I2sSampleRateDiv); + + _pixelsSize = pixelCount * elementSize; + _i2sBufferSize = pixelCount * dmaPixelSize + resetSize; + + // must have a 4 byte aligned buffer for i2s + uint32_t alignment = _i2sBufferSize % 4; + if (alignment) + { + _i2sBufferSize += 4 - alignment; + } + + _pixels = (uint8_t*)malloc(_pixelsSize); + memset(_pixels, 0x00, _pixelsSize); + + _i2sBuffer = (uint8_t*)malloc(_i2sBufferSize); + memset(_i2sBuffer, 0x00, _i2sBufferSize); + } + + ~NeoEsp32I2sMethodBase() + { + while (!IsReadyToUpdate()) + { + yield(); + } + + pinMode(_pin, INPUT); + + free(_pixels); + free(_i2sBuffer); + } + + bool IsReadyToUpdate() const + { + return (i2sWriteDone(T_BUS::I2sBusNumber)); + } + + void Initialize() + { + i2sInit(T_BUS::I2sBusNumber, 16, c_dmaI2sSampleRate / T_SPEED::I2sSampleRateDiv, I2S_CHAN_STEREO, I2S_FIFO_16BIT_DUAL, 2, 0); + i2sSetPins(T_BUS::I2sBusNumber, _pin, -1, -1, -1); + } + + void Update() + { + // wait for not actively sending data + while (!IsReadyToUpdate()) + { + yield(); + } + + FillBuffers(); + + i2sWrite(T_BUS::I2sBusNumber, _i2sBuffer, _i2sBufferSize, false, false); + } + + uint8_t* getPixels() const + { + return _pixels; + }; + + size_t getPixelsSize() const + { + return _pixelsSize; + } + +private: + const uint8_t _pin; // output pin number + + size_t _pixelsSize; // Size of '_pixels' buffer + uint8_t* _pixels; // Holds LED color values + + uint32_t _i2sBufferSize; // total size of _i2sBuffer + uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc + + void FillBuffers() + { + const uint16_t bitpatterns[16] = + { + 0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110, + 0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110, + 0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110, + 0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110, + }; + + uint16_t* pDma = (uint16_t*)_i2sBuffer; + uint8_t* pPixelsEnd = _pixels + _pixelsSize; + for (uint8_t* pPixel = _pixels; pPixel < pPixelsEnd; pPixel++) + { + *(pDma++) = bitpatterns[((*pPixel) & 0x0f)]; + *(pDma++) = bitpatterns[((*pPixel) >> 4) & 0x0f]; + } + } +}; + +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Ws2813Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0800KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0400KbpsMethod; + +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Ws2813Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1800KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1400KbpsMethod; + +// I2s Bus 1 method is the default method for Esp32 +typedef NeoEsp32I2s1Ws2813Method NeoWs2813Method; +typedef NeoEsp32I2s1800KbpsMethod Neo800KbpsMethod; +typedef NeoEsp32I2s1400KbpsMethod Neo400KbpsMethod; + +#endif \ No newline at end of file diff --git a/src/internal/NeoEspBitBangMethod.h b/src/internal/NeoEspBitBangMethod.h index 51bb26bb..a398f2b5 100644 --- a/src/internal/NeoEspBitBangMethod.h +++ b/src/internal/NeoEspBitBangMethod.h @@ -162,11 +162,6 @@ typedef NeoEspBitBangMethodBase NeoEsp32BitBangWs2813M typedef NeoEspBitBangMethodBase NeoEsp32BitBang800KbpsMethod; typedef NeoEspBitBangMethodBase NeoEsp32BitBang400KbpsMethod; -// Bitbang method is the default method for Esp32 -typedef NeoEsp32BitBangWs2813Method NeoWs2813Method; -typedef NeoEsp32BitBang800KbpsMethod Neo800KbpsMethod; -typedef NeoEsp32BitBang400KbpsMethod Neo400KbpsMethod; - #else typedef NeoEspBitBangMethodBase NeoEsp8266BitBangWs2813Method;