Esp32 Hardware support using I2s (#208)

This commit is contained in:
Michael Miller
2018-06-13 10:41:23 -07:00
committed by GitHub
parent 7ebfcb7fe2
commit 36650a44fc
5 changed files with 750 additions and 5 deletions

View File

@@ -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"

522
src/internal/Esp32_i2s.c Normal file
View File

@@ -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 <string.h>
#include <stdio.h>
#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; i<I2S[bus_num].dma_count; i++) {
i2 = (i+1) % I2S[bus_num].dma_count;
item = &I2S[bus_num].dma_items[i];
item->eof = 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; a<i; a++) {
free(I2S[bus_num].dma_items[i].buf);
}
free(I2S[bus_num].dma_items);
I2S[bus_num].dma_items = NULL;
return false;
}
} else {
item->buf = 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

38
src/internal/Esp32_i2s.h Normal file
View File

@@ -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

View File

@@ -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
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#ifdef ARDUINO_ARCH_ESP32
extern "C"
{
#include <Arduino.h>
#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<typename T_SPEED, typename T_BUS> 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<NeoEsp32I2sSpeedWs2813, NeoEsp32I2sBusZero> NeoEsp32I2s0Ws2813Method;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusZero> NeoEsp32I2s0800KbpsMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusZero> NeoEsp32I2s0400KbpsMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedWs2813, NeoEsp32I2sBusOne> NeoEsp32I2s1Ws2813Method;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusOne> NeoEsp32I2s1800KbpsMethod;
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusOne> NeoEsp32I2s1400KbpsMethod;
// I2s Bus 1 method is the default method for Esp32
typedef NeoEsp32I2s1Ws2813Method NeoWs2813Method;
typedef NeoEsp32I2s1800KbpsMethod Neo800KbpsMethod;
typedef NeoEsp32I2s1400KbpsMethod Neo400KbpsMethod;
#endif

View File

@@ -162,11 +162,6 @@ typedef NeoEspBitBangMethodBase<NeoEspBitBangSpeedWs2813> NeoEsp32BitBangWs2813M
typedef NeoEspBitBangMethodBase<NeoEspBitBangSpeed800Kbps> NeoEsp32BitBang800KbpsMethod;
typedef NeoEspBitBangMethodBase<NeoEspBitBangSpeed400Kbps> NeoEsp32BitBang400KbpsMethod;
// Bitbang method is the default method for Esp32
typedef NeoEsp32BitBangWs2813Method NeoWs2813Method;
typedef NeoEsp32BitBang800KbpsMethod Neo800KbpsMethod;
typedef NeoEsp32BitBang400KbpsMethod Neo400KbpsMethod;
#else
typedef NeoEspBitBangMethodBase<NeoEspBitBangSpeedWs2813> NeoEsp8266BitBangWs2813Method;