500 lines
14 KiB
C
500 lines
14 KiB
C
// 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 "sdkconfig.h" // this sets useful config symbols, like CONFIG_IDF_TARGET_ESP32C3
|
|
|
|
// ESP32C3/S3 I2S is not supported yet due to significant changes to interface
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
|
|
#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"
|
|
|
|
|
|
#if ESP_IDF_VERSION_MAJOR>=4
|
|
#include "esp_intr_alloc.h"
|
|
#else
|
|
#include "esp_intr.h"
|
|
#endif
|
|
|
|
#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"
|
|
#if defined(CONFIG_IDF_TARGET_ESP32)
|
|
/* included here for ESP-IDF v4.x compatibility */
|
|
#include "soc/dport_reg.h"
|
|
#endif
|
|
#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"
|
|
|
|
#if ESP_IDF_VERSION_MAJOR<=4
|
|
#define I2S_BASE_CLK (160000000L)
|
|
#endif
|
|
|
|
#define ESP32_REG(addr) (*((volatile uint32_t*)(0x3FF00000+(addr))))
|
|
|
|
#define I2S_DMA_BLOCK_COUNT_DEFAULT 16
|
|
// 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
|
|
#define I2S_DMA_SILENCE_SIZE 4*1
|
|
#define I2S_DMA_SILENCE_BLOCK_COUNT 3 // two front, one back
|
|
#define I2S_DMA_QUEUE_COUNT 2
|
|
|
|
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;
|
|
volatile uint32_t is_sending_data;
|
|
} i2s_bus_t;
|
|
|
|
// is_sending_data values
|
|
#define I2s_Is_Idle 0
|
|
#define I2s_Is_Pending 1
|
|
#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)
|
|
// (I2S_NUM_MAX == 2)
|
|
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}
|
|
};
|
|
#else
|
|
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}
|
|
};
|
|
#endif
|
|
|
|
void IRAM_ATTR i2sDmaISR(void* arg);
|
|
|
|
bool i2sInitDmaItems(uint8_t bus_num) {
|
|
if (bus_num >= I2S_NUM_MAX) {
|
|
return false;
|
|
}
|
|
if (I2S[bus_num].tx_queue) {// already set
|
|
return true;
|
|
}
|
|
|
|
size_t dmaCount = I2S[bus_num].dma_count;
|
|
|
|
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) {
|
|
log_e("MEM ERROR!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int i, i2;
|
|
i2s_dma_item_t* item = NULL;
|
|
i2s_dma_item_t* itemPrev = NULL;
|
|
|
|
for(i=0; i< dmaCount; i++) {
|
|
itemPrev = item;
|
|
|
|
i2 = (i+1) % dmaCount;
|
|
item = &I2S[bus_num].dma_items[i];
|
|
item->eof = 0;
|
|
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;
|
|
item->buf = NULL;
|
|
}
|
|
itemPrev->eof = 1;
|
|
item->eof = 1;
|
|
|
|
I2S[bus_num].tx_queue = xQueueCreate(I2S_DMA_QUEUE_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;
|
|
}
|
|
|
|
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 >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63) {
|
|
return ESP_FAIL;
|
|
}
|
|
i2s_dev_t* i2s = I2S[bus_num].bus;
|
|
|
|
typeof(i2s->clkm_conf) clkm_conf;
|
|
|
|
clkm_conf.val = 0;
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
|
clkm_conf.clka_en = 0;
|
|
#else
|
|
clkm_conf.clk_sel = 2;
|
|
#endif
|
|
|
|
clkm_conf.clkm_div_a = div_a;
|
|
clkm_conf.clkm_div_b = div_b;
|
|
clkm_conf.clkm_div_num = div_num;
|
|
i2s->clkm_conf.val = clkm_conf.val;
|
|
|
|
typeof(i2s->sample_rate_conf) sample_rate_conf;
|
|
sample_rate_conf.val = 0;
|
|
sample_rate_conf.tx_bck_div_num = bck;
|
|
sample_rate_conf.rx_bck_div_num = bck;
|
|
sample_rate_conf.tx_bits_mod = bits;
|
|
sample_rate_conf.rx_bits_mod = bits;
|
|
i2s->sample_rate_conf.val = sample_rate_conf.val;
|
|
return ESP_OK;
|
|
}
|
|
|
|
void i2sSetPins(uint8_t bus_num, int8_t out, bool invert) {
|
|
if (bus_num >= I2S_NUM_MAX) {
|
|
return;
|
|
}
|
|
|
|
if (out >= 0) {
|
|
if (I2S[bus_num].out != out) {
|
|
if (I2S[bus_num].out >= 0) {
|
|
gpio_matrix_out(I2S[bus_num].out, 0x100, invert, false);
|
|
}
|
|
I2S[bus_num].out = out;
|
|
pinMode(out, OUTPUT);
|
|
|
|
int i2sSignal;
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
|
// (I2S_NUM_MAX == 2)
|
|
if (bus_num == 1) {
|
|
i2sSignal = I2S1O_DATA_OUT23_IDX;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
i2sSignal = I2S0O_DATA_OUT23_IDX;
|
|
}
|
|
|
|
gpio_matrix_out(out, i2sSignal, invert, false);
|
|
}
|
|
} else if (I2S[bus_num].out >= 0) {
|
|
gpio_matrix_out(I2S[bus_num].out, 0x100, invert, false);
|
|
I2S[bus_num].out = -1;
|
|
}
|
|
|
|
}
|
|
|
|
bool i2sWriteDone(uint8_t bus_num) {
|
|
if (bus_num >= I2S_NUM_MAX) {
|
|
return false;
|
|
}
|
|
|
|
return (I2S[bus_num].is_sending_data == I2s_Is_Idle);
|
|
}
|
|
|
|
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 >= I2S_NUM_MAX) {
|
|
return;
|
|
}
|
|
|
|
I2S[bus_num].dma_count = dma_count + I2S_DMA_SILENCE_BLOCK_COUNT; // an extra two for looping silence
|
|
I2S[bus_num].dma_buf_len = dma_len & 0xFFF;
|
|
|
|
if (!i2sInitDmaItems(bus_num)) {
|
|
return;
|
|
}
|
|
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
|
// (I2S_NUM_MAX == 2)
|
|
if (bus_num) {
|
|
periph_module_enable(PERIPH_I2S1_MODULE);
|
|
} else
|
|
#endif
|
|
{
|
|
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
|
|
typeof(i2s->lc_conf) lc_conf;
|
|
lc_conf.val = 0;
|
|
lc_conf.out_eof_mode = 1;
|
|
i2s->lc_conf.val = lc_conf.val;
|
|
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
|
i2s->pdm_conf.pcm2pdm_conv_en = 0;
|
|
i2s->pdm_conf.pdm2pcm_conv_en = 0;
|
|
#endif
|
|
// 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;
|
|
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.tx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel
|
|
fifo_conf.rx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel
|
|
i2s->fifo_conf.val = fifo_conf.val;
|
|
|
|
typeof(i2s->conf) conf;
|
|
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;
|
|
conf2.val = 0;
|
|
conf2.lcd_en = (bits_per_sample == 8);
|
|
i2s->conf2.val = conf2.val;
|
|
|
|
i2s->fifo_conf.tx_fifo_mod_force_en = 1;
|
|
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
|
i2s->pdm_conf.rx_pdm_en = 0;
|
|
i2s->pdm_conf.tx_pdm_en = 0;
|
|
#endif
|
|
|
|
i2sSetSampleRate(bus_num, sample_rate, bits_per_sample);
|
|
|
|
// enable intr in cpu //
|
|
int i2sIntSource;
|
|
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
|
// (I2S_NUM_MAX == 2)
|
|
if (bus_num == 1) {
|
|
i2sIntSource = ETS_I2S1_INTR_SOURCE;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
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);
|
|
// 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 >= I2S_NUM_MAX) {
|
|
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;
|
|
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");
|
|
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_bus_t* dev = (i2s_bus_t*)(arg);
|
|
|
|
if (dev->bus->int_st.out_eof)
|
|
{
|
|
// i2s_dma_item_t* item = (i2s_dma_item_t*)(dev->bus->out_eof_des_addr);
|
|
if (dev->is_sending_data == I2s_Is_Pending)
|
|
{
|
|
dev->is_sending_data = I2s_Is_Idle;
|
|
}
|
|
else if (dev->is_sending_data == I2s_Is_Sending)
|
|
{
|
|
// loop the silent items
|
|
i2s_dma_item_t* itemSilence = &dev->dma_items[1];
|
|
itemSilence->next = &dev->dma_items[0];
|
|
|
|
dev->is_sending_data = I2s_Is_Pending;
|
|
}
|
|
}
|
|
|
|
dev->bus->int_clr.val = dev->bus->int_st.val;
|
|
}
|
|
|
|
size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent) {
|
|
if (bus_num >= I2S_NUM_MAX || !I2S[bus_num].tx_queue) {
|
|
return 0;
|
|
}
|
|
size_t blockSize = len;
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
xQueueReset(I2S[bus_num].tx_queue);
|
|
xQueueSend(I2S[bus_num].tx_queue, (void*)&I2S[bus_num].dma_items[0], 10);
|
|
|
|
return len;
|
|
}
|
|
|
|
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
|
|
#endif // defined(ARDUINO_ARCH_ESP32)
|
|
|