Add Library

This commit is contained in:
Siwat Sirichai 2019-08-09 09:01:56 +07:00
parent e365b9dbd9
commit 3c47103b39
318 changed files with 56465 additions and 0 deletions

View file

@ -0,0 +1,110 @@
/*
Author: bitluni 2019
License:
Creative Commons Attribution ShareAlike 4.0
https://creativecommons.org/licenses/by-sa/4.0/
For further details check out:
https://youtube.com/bitlunislab
https://github.com/bitluni
http://bitluni.net
*/
#pragma once
#include "../Tools/Log.h"
class DMABufferDescriptor : protected lldesc_t
{
public:
static void *allocateBuffer(int bytes, bool clear = true, unsigned long clearValue = 0)
{
bytes = (bytes + 3) & 0xfffffffc;
void *b = heap_caps_malloc(bytes, MALLOC_CAP_DMA);
if (!b)
DEBUG_PRINTLN("Failed to alloc dma buffer");
if (clear)
for (int i = 0; i < bytes / 4; i++)
((unsigned long *)b)[i] = clearValue;
return b;
}
static void **allocateDMABufferArray(int count, int bytes, bool clear = true, unsigned long clearValue = 0)
{
void **arr = (void **)malloc(count * sizeof(void *));
if(!arr)
ERROR("Not enough DMA memory");
for (int i = 0; i < count; i++)
{
arr[i] = DMABufferDescriptor::allocateBuffer(bytes, true, clearValue);
if(!arr[i])
ERROR("Not enough DMA memory");
}
return arr;
}
void setBuffer(void *buffer, int bytes)
{
length = bytes;
size = length;
buf = (uint8_t *)buffer;
}
void *buffer() const
{
return (void *)buf;
}
void init()
{
length = 0;
size = 0;
owner = 1;
sosf = 0;
buf = (uint8_t *)0;
offset = 0;
empty = 0;
eof = 1;
qe.stqe_next = 0;
}
static DMABufferDescriptor *allocateDescriptors(int count)
{
DMABufferDescriptor *b = (DMABufferDescriptor *)heap_caps_malloc(sizeof(DMABufferDescriptor) * count, MALLOC_CAP_DMA);
if (!b)
DEBUG_PRINTLN("Failed to alloc DMABufferDescriptors");
for (int i = 0; i < count; i++)
b[i].init();
return b;
}
static DMABufferDescriptor *allocateDescriptor(int bytes, bool allocBuffer = true, bool clear = true, unsigned long clearValue = 0)
{
bytes = (bytes + 3) & 0xfffffffc;
DMABufferDescriptor *b = (DMABufferDescriptor *)heap_caps_malloc(sizeof(DMABufferDescriptor), MALLOC_CAP_DMA);
if (!b)
DEBUG_PRINTLN("Failed to alloc DMABufferDescriptor");
b->init();
if (allocateBuffer)
b->setBuffer(allocateBuffer(bytes, clear, clearValue), bytes);
return b;
}
void next(DMABufferDescriptor &next)
{
qe.stqe_next = &next;
}
int sampleCount() const
{
return length / 4;
}
void destroy()
{
if (buf)
{
free((void *)buf);
buf = 0;
}
free(this);
}
};

View file

@ -0,0 +1,360 @@
/*
Author: bitluni 2019
License:
Creative Commons Attribution ShareAlike 4.0
https://creativecommons.org/licenses/by-sa/4.0/
For further details check out:
https://youtube.com/bitlunislab
https://github.com/bitluni
http://bitluni.net
*/
#include "I2S.h"
#include "../Tools/Log.h"
#include <soc/rtc.h>
i2s_dev_t *i2sDevices[] = {&I2S0, &I2S1};
I2S::I2S(const int i2sIndex)
{
this->i2sIndex = i2sIndex;
interruptHandle = 0;
dmaBufferDescriptorCount = 0;
dmaBufferDescriptorActive = 0;
dmaBufferDescriptors = 0;
stopSignal = false;
}
void IRAM_ATTR I2S::interruptStatic(void *arg)
{
volatile i2s_dev_t &i2s = *i2sDevices[((I2S *)arg)->i2sIndex];
i2s.int_clr.val = i2s.int_raw.val;
((I2S *)arg)->interrupt();
}
void I2S::reset()
{
volatile i2s_dev_t &i2s = *i2sDevices[i2sIndex];
const unsigned long lc_conf_reset_flags = I2S_IN_RST_M | I2S_OUT_RST_M | I2S_AHBM_RST_M | I2S_AHBM_FIFO_RST_M;
i2s.lc_conf.val |= lc_conf_reset_flags;
i2s.lc_conf.val &= ~lc_conf_reset_flags;
const uint32_t conf_reset_flags = I2S_RX_RESET_M | I2S_RX_FIFO_RESET_M | I2S_TX_RESET_M | I2S_TX_FIFO_RESET_M;
i2s.conf.val |= conf_reset_flags;
i2s.conf.val &= ~conf_reset_flags;
while (i2s.state.rx_fifo_reset_back)
;
}
void I2S::i2sStop()
{
volatile i2s_dev_t &i2s = *i2sDevices[i2sIndex];
esp_intr_disable(interruptHandle);
reset();
i2s.conf.rx_start = 0;
i2s.conf.tx_start = 0;
}
void I2S::startTX()
{
volatile i2s_dev_t &i2s = *i2sDevices[i2sIndex];
DEBUG_PRINTLN("I2S TX");
esp_intr_disable(interruptHandle);
reset();
i2s.lc_conf.val = I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN;
dmaBufferDescriptorActive = 0;
i2s.out_link.addr = (uint32_t)firstDescriptorAddress();
i2s.out_link.start = 1;
i2s.int_clr.val = i2s.int_raw.val;
i2s.int_ena.val = 0;
if(useInterrupt())
{
i2s.int_ena.out_eof = 1;
//enable interrupt
esp_intr_enable(interruptHandle);
}
//start transmission
i2s.conf.tx_start = 1;
}
void I2S::startRX()
{
volatile i2s_dev_t &i2s = *i2sDevices[i2sIndex];
DEBUG_PRINTLN("I2S RX");
esp_intr_disable(interruptHandle);
reset();
dmaBufferDescriptorActive = 0;
i2s.rx_eof_num = dmaBufferDescriptors[0].sampleCount(); //TODO: replace with cont of sample to be recorded
i2s.in_link.addr = (uint32_t)firstDescriptorAddress();
i2s.in_link.start = 1;
i2s.int_clr.val = i2s.int_raw.val;
i2s.int_ena.val = 0;
i2s.int_ena.in_done = 1;
esp_intr_enable(interruptHandle);
i2s.conf.rx_start = 1;
}
void I2S::resetDMA()
{
volatile i2s_dev_t &i2s = *i2sDevices[i2sIndex];
i2s.lc_conf.in_rst = 1;
i2s.lc_conf.in_rst = 0;
i2s.lc_conf.out_rst = 1;
i2s.lc_conf.out_rst = 0;
}
void I2S::resetFIFO()
{
volatile i2s_dev_t &i2s = *i2sDevices[i2sIndex];
i2s.conf.rx_fifo_reset = 1;
i2s.conf.rx_fifo_reset = 0;
i2s.conf.tx_fifo_reset = 1;
i2s.conf.tx_fifo_reset = 0;
}
DMABufferDescriptor *I2S::firstDescriptorAddress() const
{
return &dmaBufferDescriptors[0];
}
bool I2S::useInterrupt()
{
return false;
};
bool I2S::initParallelInputMode(const int *pinMap, long sampleRate, const int bitCount, int wordSelect, int baseClock)
{
volatile i2s_dev_t &i2s = *i2sDevices[i2sIndex];
//route peripherals
const int deviceBaseIndex[] = {I2S0I_DATA_IN0_IDX, I2S1I_DATA_IN0_IDX};
const int deviceClockIndex[] = {I2S0I_BCK_IN_IDX, I2S1I_BCK_IN_IDX};
const int deviceWordSelectIndex[] = {I2S0I_WS_IN_IDX, I2S1I_WS_IN_IDX};
const periph_module_t deviceModule[] = {PERIPH_I2S0_MODULE, PERIPH_I2S1_MODULE};
//works only since indices of the pads are sequential
for (int i = 0; i < bitCount; i++)
if (pinMap[i] > -1)
{
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[pinMap[i]], PIN_FUNC_GPIO);
gpio_set_direction((gpio_num_t)pinMap[i], (gpio_mode_t)GPIO_MODE_DEF_INPUT);
gpio_matrix_in(pinMap[i], deviceBaseIndex[i2sIndex] + i, false);
}
if (baseClock > -1)
gpio_matrix_in(baseClock, deviceClockIndex[i2sIndex], false);
if (wordSelect > -1)
gpio_matrix_in(wordSelect, deviceWordSelectIndex[i2sIndex], false);
//enable I2S peripheral
periph_module_enable(deviceModule[i2sIndex]);
//reset i2s
i2s.conf.rx_reset = 1;
i2s.conf.rx_reset = 0;
i2s.conf.tx_reset = 1;
i2s.conf.tx_reset = 0;
resetFIFO();
resetDMA();
//parallel mode
i2s.conf2.val = 0;
i2s.conf2.lcd_en = 1;
//from technical datasheet figure 64
//i2s.conf2.lcd_tx_sdx2_en = 1;
//i2s.conf2.lcd_tx_wrx2_en = 1;
i2s.sample_rate_conf.val = 0;
i2s.sample_rate_conf.rx_bits_mod = 16;
//maximum rate
i2s.clkm_conf.val = 0;
i2s.clkm_conf.clka_en = 0;
i2s.clkm_conf.clkm_div_num = 6; //3//80000000L / sampleRate;
i2s.clkm_conf.clkm_div_a = 6; // 0;
i2s.clkm_conf.clkm_div_b = 1; // 0;
i2s.sample_rate_conf.rx_bck_div_num = 2;
i2s.fifo_conf.val = 0;
i2s.fifo_conf.rx_fifo_mod_force_en = 1;
i2s.fifo_conf.rx_fifo_mod = 1; //byte packing 0A0B_0B0C = 0, 0A0B_0C0D = 1, 0A00_0B00 = 3,
i2s.fifo_conf.rx_data_num = 32;
i2s.fifo_conf.dscr_en = 1; //fifo will use dma
i2s.conf1.val = 0;
i2s.conf1.tx_stop_en = 1;
i2s.conf1.tx_pcm_bypass = 1;
i2s.conf_chan.val = 0;
i2s.conf_chan.rx_chan_mod = 0;
//high or low (stereo word order)
i2s.conf.rx_right_first = 1;
i2s.timing.val = 0;
//clear serial mode flags
i2s.conf.rx_msb_right = 0;
i2s.conf.rx_msb_shift = 0;
i2s.conf.rx_mono = 0;
i2s.conf.rx_short_sync = 0;
//allocate disabled i2s interrupt
const int interruptSource[] = {ETS_I2S0_INTR_SOURCE, ETS_I2S1_INTR_SOURCE};
if(useInterrupt())
esp_intr_alloc(interruptSource[i2sIndex], ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3 | ESP_INTR_FLAG_IRAM, &interruptStatic, this, &interruptHandle);
return true;
}
bool I2S::initParallelOutputMode(const int *pinMap, long sampleRate, const int bitCount, int wordSelect, int baseClock)
{
volatile i2s_dev_t &i2s = *i2sDevices[i2sIndex];
//route peripherals
//in parallel mode only upper 16 bits are interesting in this case
const int deviceBaseIndex[] = {I2S0O_DATA_OUT0_IDX, I2S1O_DATA_OUT0_IDX};
const int deviceClockIndex[] = {I2S0O_BCK_OUT_IDX, I2S1O_BCK_OUT_IDX};
const int deviceWordSelectIndex[] = {I2S0O_WS_OUT_IDX, I2S1O_WS_OUT_IDX};
const periph_module_t deviceModule[] = {PERIPH_I2S0_MODULE, PERIPH_I2S1_MODULE};
//works only since indices of the pads are sequential
for (int i = 0; i < bitCount; i++)
if (pinMap[i] > -1)
{
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[pinMap[i]], PIN_FUNC_GPIO);
gpio_set_direction((gpio_num_t)pinMap[i], (gpio_mode_t)GPIO_MODE_DEF_OUTPUT);
if(i2sIndex == 1)
{
if(bitCount == 16)
gpio_matrix_out(pinMap[i], deviceBaseIndex[i2sIndex] + i + 8, false, false);
else
gpio_matrix_out(pinMap[i], deviceBaseIndex[i2sIndex] + i, false, false);
}
else
{
//there is something odd going on here in the two different I2S
//the configuration seems to differ. Use i2s1 for high frequencies.
gpio_matrix_out(pinMap[i], deviceBaseIndex[i2sIndex] + i + 24 - bitCount, false, false);
}
}
if (baseClock > -1)
gpio_matrix_out(baseClock, deviceClockIndex[i2sIndex], false, false);
if (wordSelect > -1)
gpio_matrix_out(wordSelect, deviceWordSelectIndex[i2sIndex], false, false);
//enable I2S peripheral
periph_module_enable(deviceModule[i2sIndex]);
//reset i2s
i2s.conf.tx_reset = 1;
i2s.conf.tx_reset = 0;
i2s.conf.rx_reset = 1;
i2s.conf.rx_reset = 0;
resetFIFO();
resetDMA();
//parallel mode
i2s.conf2.val = 0;
i2s.conf2.lcd_en = 1;
//from technical datasheet figure 64
i2s.conf2.lcd_tx_wrx2_en = 1;
i2s.conf2.lcd_tx_sdx2_en = 0;
i2s.sample_rate_conf.val = 0;
i2s.sample_rate_conf.tx_bits_mod = bitCount;
//clock setup
//xtal is 40M
//chip revision 0
//fxtal * (sdm2 + 4) / (2 * (odir + 2))
//chip revision 1
//fxtal * (sdm2 + (sdm1 / 256) + (sdm0 / 65536) + 4) / (2 * (odir + 2))
//fxtal * (sdm2 + (sdm1 / 256) + (sdm0 / 65536) + 4) needs to be btween 350M and 500M
//rtc_clk_apll_enable(enable, sdm0, sdm1, sdm2, odir);
// 0-255 0-255 0-63 0-31
//sdm seems to be simply a fixpoint number with 16bits fractional part
//freq = 40000000L * (4 + sdm) / (2 * (odir + 2))
//sdm = freq / (20000000L / (odir + 2)) - 4;
long freq = sampleRate * 2 * (bitCount / 8);
int sdm, sdmn;
int odir = -1;
do
{
odir++;
sdm = long((double(freq) / (20000000. / (odir + 2))) * 0x10000) - 0x40000;
sdmn = long((double(freq) / (20000000. / (odir + 2 + 1))) * 0x10000) - 0x40000;
}while(sdm < 0x8c0ecL && odir < 31 && sdmn < 0xA1fff); //0xA7fffL doesn't work on all mcus
//DEBUG_PRINTLN(sdm & 255);
//DEBUG_PRINTLN((sdm >> 8) & 255);
//DEBUG_PRINTLN(sdm >> 16);
//DEBUG_PRINTLN(odir);
//sdm = 0xA1fff;
//odir = 0;
if(sdm > 0xA1fff) sdm = 0xA1fff;
rtc_clk_apll_enable(true, sdm & 255, (sdm >> 8) & 255, sdm >> 16, odir);
i2s.clkm_conf.val = 0;
i2s.clkm_conf.clka_en = 1;
i2s.clkm_conf.clkm_div_num = 2; //clockN;
i2s.clkm_conf.clkm_div_a = 1; //clockA;
i2s.clkm_conf.clkm_div_b = 0; //clockB;
i2s.sample_rate_conf.tx_bck_div_num = 1;
i2s.fifo_conf.val = 0;
i2s.fifo_conf.tx_fifo_mod_force_en = 1;
i2s.fifo_conf.tx_fifo_mod = 1; //byte packing 0A0B_0B0C = 0, 0A0B_0C0D = 1, 0A00_0B00 = 3,
i2s.fifo_conf.tx_data_num = 32; //fifo length
i2s.fifo_conf.dscr_en = 1; //fifo will use dma
i2s.conf1.val = 0;
i2s.conf1.tx_stop_en = 0;
i2s.conf1.tx_pcm_bypass = 1;
i2s.conf_chan.val = 0;
i2s.conf_chan.tx_chan_mod = 1;
//high or low (stereo word order)
i2s.conf.tx_right_first = 1;
i2s.timing.val = 0;
//clear serial mode flags
i2s.conf.tx_msb_right = 0;
i2s.conf.tx_msb_shift = 0;
i2s.conf.tx_mono = 0;
i2s.conf.tx_short_sync = 0;
//allocate disabled i2s interrupt
const int interruptSource[] = {ETS_I2S0_INTR_SOURCE, ETS_I2S1_INTR_SOURCE};
if(useInterrupt())
esp_intr_alloc(interruptSource[i2sIndex], ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3 | ESP_INTR_FLAG_IRAM, &interruptStatic, this, &interruptHandle);
return true;
}
/// simple ringbuffer of blocks of size bytes each
void I2S::allocateDMABuffers(int count, int bytes)
{
dmaBufferDescriptorCount = count;
dmaBufferDescriptors = DMABufferDescriptor::allocateDescriptors(count);
for (int i = 0; i < dmaBufferDescriptorCount; i++)
{
dmaBufferDescriptors[i].setBuffer(DMABufferDescriptor::allocateBuffer(bytes, true), bytes);
if (i)
dmaBufferDescriptors[i - 1].next(dmaBufferDescriptors[i]);
}
dmaBufferDescriptors[dmaBufferDescriptorCount - 1].next(dmaBufferDescriptors[0]);
}
void I2S::deleteDMABuffers()
{
if (!dmaBufferDescriptors)
return;
for (int i = 0; i < dmaBufferDescriptorCount; i++)
free(dmaBufferDescriptors[i].buffer());
free(dmaBufferDescriptors);
dmaBufferDescriptors = 0;
dmaBufferDescriptorCount = 0;
}
void I2S::stop()
{
stopSignal = true;
while (stopSignal)
;
}

View file

@ -0,0 +1,60 @@
/*
Author: bitluni 2019
License:
Creative Commons Attribution ShareAlike 4.0
https://creativecommons.org/licenses/by-sa/4.0/
For further details check out:
https://youtube.com/bitlunislab
https://github.com/bitluni
http://bitluni.net
*/
#pragma once
#include "esp_heap_caps.h"
#include "soc/soc.h"
#include "soc/gpio_sig_map.h"
#include "soc/i2s_reg.h"
#include "soc/i2s_struct.h"
#include "soc/io_mux_reg.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "rom/lldesc.h"
#include "DMABufferDescriptor.h"
class I2S
{
public:
int i2sIndex;
intr_handle_t interruptHandle;
int dmaBufferDescriptorCount;
int dmaBufferDescriptorActive;
DMABufferDescriptor *dmaBufferDescriptors;
volatile bool stopSignal;
/// hardware index [0, 1]
I2S(const int i2sIndex = 0);
void reset();
void stop();
void i2sStop();
void startTX();
void startRX();
void resetDMA();
void resetFIFO();
bool initParallelOutputMode(const int *pinMap, long APLLFreq = 1000000, const int bitCount = 8, int wordSelect = -1, int baseClock = -1);
bool initParallelInputMode(const int *pinMap, long sampleRate = 1000000, const int bitCount = 8, int wordSelect = -1, int baseClock = -1);
virtual DMABufferDescriptor *firstDescriptorAddress() const;
void allocateDMABuffers(int count, int bytes);
void deleteDMABuffers();
protected:
virtual void interrupt() = 0;
virtual bool useInterrupt();
private:
static void IRAM_ATTR interruptStatic(void *arg);
};