Files
ESP8266lib/io/teensy/I2S2.h
kazu 422610c21c many many small changes
switched to the new logging here and there
some cleanups
worked  on i2S
base class for files
id3 parsing
2021-02-28 20:44:01 +01:00

173 lines
6.5 KiB
C++

#pragma once
// https://github.com/Jean-MarcHarvengt/VGA_t4
// https://forum.pjrc.com/threads/63243-Writing-Directly-to-SGTL5000-CODEC-DACs
// https://community.nxp.com/t5/Kinetis-Microcontrollers/is-there-any-demo-code-for-using-I2S/m-p/196195/highlight/true
// https://github.com/PaulStoffregen/Audio/blob/99b9472afd24bea13efc799742c0ea432ef2303a/output_i2s.cpp
#include "../../Debug.h"
#define IS2_MODE_DMA 1
#define I2S_MODE_IRQ 2
#ifndef I2S_MODE
#error "I2S_MODE must be defined: IS2_MODE_DMA or IS2_MODE_IRQ"
#endif
// the buffer has a size of X samples,
// and we receive an interrupt whenever ONE HALF was transmitted
// 1152 = MP3 decode size, *2 (stereo), *2 (2 buffer halfs)
static constexpr const uint32_t I2S2_BUFFER_SAMPLES = 1152*2*2;
#if I2S_MODE == IS2_MODE_DMA
#include "I2SBaseDMA.h"
#elif I2S_MODE == I2S_MODE_IRQ
#include "I2SBaseIRQ.h"
#else
#error "invalid value for I2S_MODE"
#endif
class I2S2 : public I2SBase<I2S2> {
private:
static constexpr const char* NAME = "I2S2";
public:
/** start the i2s transmission with the given values */
static void setup(uint8_t channels, uint32_t sampleRate_hz) {
Log::addInfo(NAME, "setup(%d, %d)", channels, sampleRate_hz);
// zero out the audio data buffer
clearBuffer();
// configure the I2S unit
config_sai2(sampleRate_hz);
#if I2S_MODE == IS2_MODE_DMA
state.dma.begin(true); // Allocate the DMA channel first
state.dma.TCD->SADDR = buffer; // source address
state.dma.TCD->SOFF = 2; // source buffer address increment per transfer in bytes
state.dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); // specifies 16 bit source and destination
state.dma.TCD->NBYTES_MLNO = 2; // bytes to transfer for each service request///////////////////////////////////////////////////////////////////
state.dma.TCD->SLAST = -sizeof(buffer); // last source address adjustment
state.dma.TCD->DOFF = 0; // increments at destination
state.dma.TCD->CITER_ELINKNO = sizeof(buffer) / 2;
state.dma.TCD->DLASTSGA = 0; // destination address offset
state.dma.TCD->BITER_ELINKNO = sizeof(buffer) / 2;
state.dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; // enables interrupt when transfers half complete. SET TO 0 to disable DMA interrupts
state.dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2); // I2S2 register DMA writes to
state.dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); // i2s channel that will trigger the DMA transfer when ready for data
state.dma.enable();
state.dma.attachInterrupt(isrDMA);
Log::addInfo(NAME, "DMA configured");
#endif
}
private:
static void config_sai2(uint32_t sampleRate_hz) {
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
double fs = sampleRate_hz;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = (fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
setAudioClock(c0, c1, c2, true);
// clear SAI2_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
//n1 = n1 / 2; //Double Speed for TDM
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
| CCM_CS2CDR_SAI2_CLK_PRED(n1 - 1)
| CCM_CS2CDR_SAI2_CLK_PODF(n2 - 1);
// kazu: OK WITHOUT??
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK)) | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
// configure transmitter
const int rsync = 0;
const int tsync = 1;
uint16_t by = 32; // ??
// configure transmitter
I2S2_TMR = 0;
I2S2_TCR1 = I2S_TCR1_RFW(1);
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); // sync=0; tx is async;
I2S2_TCR3 = I2S_TCR3_TCE;
I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((by-1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S2_TCR5 = I2S_TCR5_WNW((by-1)) | I2S_TCR5_W0W((by-1)) | I2S_TCR5_FBT((by-1)); // page 1995
I2S2_RMR = 0;
I2S2_RCR1 = I2S_RCR1_RFW(1);
I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP | (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); // sync=0; rx is async;
I2S2_RCR3 = I2S_RCR3_RCE;
I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((by-1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S2_RCR5 = I2S_RCR5_WNW((by-1)) | I2S_RCR5_W0W((by-1)) | I2S_RCR5_FBT((by-1));
// configure pins (2,3,4) to their I2S functionality
CORE_PIN4_CONFIG = 2; // RX_BCLK
CORE_PIN3_CONFIG = 2; // RX_SYNC (left/right)
CORE_PIN2_CONFIG = 2; // TX_DATA0
I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
// START
#if I2S_MODE == IS2_MODE_DMA
I2S2_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; // | I2S_TCSR_FR ???
#elif I2S_MODE == IS2_MODE_IRQ
I2S2_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE;
#endif
}
FLASHMEM static void setAudioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) {// sets PLL4
if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;
CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE
| CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
| CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
CCM_ANALOG_PLL_AUDIO_NUM = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;
CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL
while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock
const int div_post_pll = 1; // other values: 2,4
CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB);
if(div_post_pll>1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB;
if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;//Disable Bypass
}
};
// init static class members
#ifdef I2S2_IMPLEMENTATION
//template<> DMAMEM __attribute__((aligned(32))) int16_t I2SBase<I2S2>::buffer[I2S2_BUFFER_SAMPLES];
//template<> I2SBase<I2S2>::State I2SBase<I2S2>::state;
DMAMEM __attribute__((aligned(32))) int16_t I2S2::buffer[I2S2_BUFFER_SAMPLES];
I2S2::State I2S2::state;
#endif