// I2S input test with DMA
//
// some missing macros/definitions
#define CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(n) ((uint32_t)(((n) & 0x03)<<19))
#define CCM_ANALOG_PLL_AUDIO_BYPASS ((uint32_t)(1<<16))
#define CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC(n) ((uint32_t)(((n) & 0x03)<<14))
#define CCM_ANALOG_PLL_AUDIO_ENABLE ((uint32_t)(1<<13))
#define CCM_ANALOG_PLL_AUDIO_POWERDOWN ((uint32_t)(1<<12))
#define CCM_ANALOG_PLL_AUDIO_DIV_SELECT(n) ((uint32_t)((n) & ((1<<6)-1)))
#define CCM_ANALOG_MISC2_DIV_MSB (1u<<23)
#define CCM_ANALOG_MISC2_DIV_LSB (1u<<15)
#define CCM_ANALOG_PLL_AUDIO_NUM_MASK (((1<<29)-1))
#define CCM_ANALOG_PLL_AUDIO_DENOM_MASK (((1<<29)-1))
#define CCM_CSCMR1_SAI1_CLK_SEL_MASK (CCM_CSCMR1_SAI1_CLK_SEL(0x03))
#define CCM_CS1CDR_SAI1_CLK_PRED_MASK (CCM_CS1CDR_SAI1_CLK_PRED(0x07))
#define CCM_CS1CDR_SAI1_CLK_PODF_MASK (CCM_CS1CDR_SAI1_CLK_PODF(0x3f))
#define CCM_CSCMR1_SAI2_CLK_SEL_MASK (CCM_CSCMR1_SAI2_CLK_SEL(0x03))
#define CCM_CS2CDR_SAI2_CLK_PRED_MASK (CCM_CS2CDR_SAI2_CLK_PRED(0x07))
#define CCM_CS2CDR_SAI2_CLK_PODF_MASK (CCM_CS2CDR_SAI2_CLK_PODF(0x3f))
#define CCM_CSCMR1_SAI3_CLK_SEL_MASK (CCM_CSCMR1_SAI3_CLK_SEL(0x03))
#define CCM_CS1CDR_SAI3_CLK_PRED_MASK (CCM_CS1CDR_SAI3_CLK_PRED(0x07))
#define CCM_CS1CDR_SAI3_CLK_PODF_MASK (CCM_CS1CDR_SAI3_CLK_PODF(0x3f))
//
//
void set_audioClock(int nfact = 27, int32_t mult=0, uint32_t div=1)
{
CCM_ANALOG_PLL_AUDIO = 0;
//CCM_ANALOG_PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_BYPASS;
CCM_ANALOG_PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_ENABLE;
CCM_ANALOG_PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2); // 0: 1/4; 1: 1/2; 0: 1/1
CCM_ANALOG_PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
CCM_ANALOG_PLL_AUDIO_NUM = mult &CCM_ANALOG_PLL_AUDIO_NUM_MASK;
CCM_ANALOG_PLL_AUDIO_DENOM = div &CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
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;
}
//
void sai1_setClock(int n1, int n2)
{
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
IOMUXC_GPR_GPR1 |= (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));
// clear SAI1_CLK register locations
CCM_CSCMR1 &= ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK);
CCM_CS1CDR &= ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK);
//
CCM_CSCMR1 |= CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
CCM_CS1CDR |= CCM_CS1CDR_SAI1_CLK_PRED(n1-1); // &0x07
CCM_CS1CDR |= CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f
}
//
void sai1_configurePorts(int iconf=0)
{
CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BITCLK
CORE_PIN20_CONFIG = 3; //1:RX_FS
CORE_PIN7_CONFIG = 3; //1:RX_DATA0
CORE_PIN8_CONFIG = 3; //1:RX_DATA1 // not used
CORE_PIN9_CONFIG = 3; //1:RX_DATA2 // not used
CORE_PIN32_CONFIG = 3; //1:RX_DATA3 // not used
}
//subset of missing definitions
#define I2S_RCR1_RFW(n) ((uint32_t)n & 0x1f) // Receive FIFO watermark
#define I2S_RCR2_DIV(n) ((uint32_t)n & 0xff) // Bit clock divide by (DIV+1)*2
#define I2S_RCR2_BCD ((uint32_t)1<<24) // Bit clock direction
#define I2S_RCR2_MSEL(n) ((uint32_t)(n & 3)<<26) // MCLK select, 0=bus clock, 1=I2S0_MCLK
#define I2S_RCR2_SYNC(n) ((uint32_t)(n & 3)<<30) // 0=async 1=sync with receiver
#define I2S_RCR3_RCE ((uint32_t)0x10000) // receive channel enable
#define I2S_RCR4_FSD ((uint32_t)1) // Frame Sync Direction
#define I2S_RCR4_FSE ((uint32_t)8) // Frame Sync Early
#define I2S_RCR4_MF ((uint32_t)0x10) // MSB First
#define I2S_RCR4_SYWD(n) ((uint32_t)(n & 0x1f)<<8) // Sync Width
#define I2S_RCR4_FRSZ(n) ((uint32_t)(n & 0x0f)<<16) // Frame Size
#define I2S_RCR5_FBT(n) ((uint32_t)(n & 0x1f)<<8) // First Bit Shifted
#define I2S_RCR5_W0W(n) ((uint32_t)(n & 0x1f)<<16) // Word 0 Width
#define I2S_RCR5_WNW(n) ((uint32_t)(n & 0x1f)<<24) // Word N Width
#define I2S_RCSR_RE ((uint32_t)0x80000000) // Receiver Enable
#define I2S_RCSR_FR ((uint32_t)0x02000000) // FIFO Reset
#define I2S_RCSR_FRDE ((uint32_t)0x00000001) // FIFO Request DMA Enable
#define I2S_RCSR_BCE ((uint32_t)0x10000000) // Bit Clock Enable
typedef struct
{
uint32_t CSR;
uint32_t CR1,CR2,CR3,CR4,CR5;
uint32_t DR[8];
uint32_t FR[8];
uint32_t MR;
} I2S_PORT;
typedef struct
{
uint32_t VERID;
uint32_t PARAM;
I2S_PORT TX;
uint32_t unused[9];
I2S_PORT RX;
} I2S_STRUCT;
I2S_STRUCT *I2S1 = ((I2S_STRUCT *)0x40384000);
I2S_STRUCT *I2S2 = ((I2S_STRUCT *)0x40388000);
I2S_STRUCT *I2S3 = ((I2S_STRUCT *)0x4038C000);
//
#define NBITS 32
#define NW 2 // number of words / channel
#define NCH 1 // number of channels // allowed 1,2,4
#define TDM 0 // use short conv-flag
/*
* e.g.
* single stereo: NW = 2; NCH = 1;
* dual stereo: NW = 2; NCH = 2;
* quad stereo: NW = 2; NCH = 4;
* 8-ch TDM: NW = 8; NCH = 1;
* 4*8 ch TDM: NW = 8; NCH = 4;
*/
#define NDAT 128 // number of samples / (words * channel)
#define NBUF (2*NW*NCH*NDAT)
int32_t rx_buffer[NBUF] __attribute__( ( aligned ( 0x1000 ) ) );
int32_t rx_data1 = (uint32_t)&rx_buffer[0];
int32_t rx_data2 = (uint32_t)&rx_buffer[NBUF/2];
void sai_rxConfig(int ndiv, int nch)
{
I2S1->RX.MR = 0;
I2S1->RX.CR1 = I2S_RCR1_RFW(1);
I2S1->RX.CR2 = I2S_RCR2_SYNC(0);// | I2S_RCR2_BCP ; // sync=0; rx is async;
I2S1->RX.CR2 |= (I2S_RCR2_BCD | I2S_RCR2_DIV(ndiv-1) | I2S_RCR2_MSEL(1));
I2S1->RX.CR3 = 0;
for(int ii=0; ii<nch; ii++)
I2S1->RX.CR3 |= I2S_RCR3_RCE<<ii; // mark rx channel
Serial.println(I2S1->RX.CR3,HEX);
//
#if (TDM==0)
int ns = NBITS;
#elif (TDM==1)
int ns = 1;
#endif
I2S1->RX.CR4 = I2S_RCR4_FRSZ(NW-1)
| I2S_RCR4_SYWD(ns-1)
| I2S_RCR4_MF
#if (TDM==1)
| I2S_RCR4_FSE
#endif
| I2S_RCR4_FSD;
I2S1->RX.CR5 = I2S_RCR5_WNW(NBITS-1) | I2S_RCR5_W0W(NBITS-1) | I2S_RCR5_FBT(NBITS-1);
}
IMXRT_DMA_TCD_t *dma ((IMXRT_DMA_TCD_t *)0x400E9000);
uint32_t *dma_cfg ((uint32_t *)0x400EC000);
volatile uint32_t dma_ch=0;
/****************************************************************/
#define SW_MODE 2 // SW_MODE=1: works SW_MODE=2 does not work
void sai_rx_isr(void); // forward declaration
void sai_setupInput(void * buffer, int nch, int nloop)
{
//
CCM_CCGR5 |= CCM_CCGR5_DMA(CCM_CCGR_ON);
DMA_CR = 0;
DMA_CR = DMA_CR_GRP1PRI | DMA_CR_EMLM | DMA_CR_EDBG;
#if SW_MODE==1 // this works
DMAMUX_CHCFG0 = 0;
DMAMUX_CHCFG0 = (DMAMUX_SOURCE_SAI1_RX & 0x7F) | DMAMUX_CHCFG_ENBL;
// clear DMA registers
DMA_TCD0_CSR = 0;
DMA_TCD0_ATTR = 0;
//
// major loop
DMA_TCD0_BITER = nloop;
DMA_TCD0_CITER = DMA_TCD0_BITER;
//
// minor loop
DMA_TCD0_NBYTES = 4*nch;
#if NCH>1
DMA_TCD0_NBYTES |= DMA_TCD_NBYTES_SMLOE ;
DMA_TCD0_NBYTES |= DMA_TCD_NBYTES_MLOFFYES_MLOFF(-4*nch);
#endif
//
// Source
DMA_TCD0_SADDR = (void *)&I2S1_RDR0;
DMA_TCD0_ATTR |= DMA_TCD_ATTR_SSIZE(2);
#if NCH>1
DMA_TCD0_SOFF = 4;
DMA_TCD0_SLAST = -4*nch;
#else
DMA_TCD0_SOFF = 0;
DMA_TCD0_SLAST = 0;
#endif
//
// Destination
DMA_TCD0_DADDR = buffer;
DMA_TCD0_ATTR |= DMA_TCD_ATTR_DSIZE(2);
DMA_TCD0_DOFF = 4;
DMA_TCD0_DLASTSGA = -4*nch*nloop;
//
DMA_TCD0_CSR |= DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
attachInterruptVector(IRQ_DMA_CH0, sai_rx_isr);
NVIC_ENABLE_IRQ(IRQ_DMA_CH0);
NVIC_SET_PRIORITY(IRQ_DMA_CH0, 7*16); // 8 is normal priority
//
I2S1->RX.CSR |= I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S1->RX.CSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
DMA_SERQ = dma_ch = 0;
DMA_TCD0_CSR |= DMA_TCD_CSR_START;
/*******************************************************************************/
#elif SW_MODE==2 // // to be fixed
DMAMUX_CHCFG1 = 0;
DMAMUX_CHCFG1 = (DMAMUX_SOURCE_SAI1_RX & 0x7F) | DMAMUX_CHCFG_ENBL;
DMA_CERQ = 1;
DMA_CERR = 1;
DMA_CEEI = 1;
DMA_CINT = 1;
// clear DMA registers
DMA_TCD1_CSR = 0;
DMA_TCD1_ATTR = 0;
//
// major loop
DMA_TCD1_BITER = nloop;
DMA_TCD1_CITER = DMA_TCD1_BITER;
//
// minor loop
DMA_TCD1_NBYTES = 4*nch;
#if NCH>1
DMA_TCD1_NBYTES |= DMA_TCD_NBYTES_SMLOE ;
DMA_TCD1_NBYTES |= DMA_TCD_NBYTES_MLOFFYES_MLOFF(-4*nch);
#endif
//
// Source
DMA_TCD1_SADDR = (void *)&I2S1_RDR0;
DMA_TCD1_ATTR |= DMA_TCD_ATTR_SSIZE(2);
#if NCH>1
DMA_TCD1_SOFF = 4;
DMA_TCD1_SLAST = -4*nch;
#else
DMA_TCD1_SOFF = 0;
DMA_TCD1_SLAST = 0;
#endif
//
// Destination
DMA_TCD1_DADDR = buffer;
DMA_TCD1_ATTR |= DMA_TCD_ATTR_DSIZE(2);
DMA_TCD1_DOFF = 4;
DMA_TCD1_DLASTSGA = -4*nch*nloop;
//
DMA_TCD1_CSR |= DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
attachInterruptVector(IRQ_DMA_CH1, sai_rx_isr);
NVIC_ENABLE_IRQ(IRQ_DMA_CH1);
NVIC_SET_PRIORITY(IRQ_DMA_CH1, 7*16); // 8 is normal priority
//
I2S1->RX.CSR |= I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S1->RX.CSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
DMA_SERQ = dma_ch = 1;
DMA_TCD1_CSR |= DMA_TCD_CSR_START;
#endif
}
#define I2S_PIN 3
void sai_rxProcessing(void * taddr); // forward declaration
int32_t i2sCount1=0;
int32_t i2sCount2=0;
uint32_t maxPtr=0;
uint32_t minPtr=0x40000000;
void sai_rx_isr(void)
{ uint32_t daddr, taddr;
//
DMA_CINT = dma_ch;
//
daddr = (uint32_t) (DMA_TCD0_DADDR);
if(daddr>maxPtr) maxPtr=daddr;
if(daddr<minPtr) minPtr=daddr;
if (daddr < (uint32_t)rx_data2)
{
// DMA is receiving to the first half of the buffer
// need to process data from the second half
taddr=(uint32_t) rx_data2;
//
i2sCount1++;
digitalWrite(I2S_PIN, HIGH);
}
else
{
// DMA is receiving to the second half of the buffer
// need to process data from the first half
taddr=(uint32_t) rx_data1;
//
i2sCount2++;
digitalWrite(I2S_PIN, LOW);
}
//up call
sai_rxProcessing((void *) taddr);
}
uint32_t rxCount = 0;
uint32_t maxVal=0;
void sai_rxProcessing(void * taddr)
{
uint32_t *data = (uint32_t *) taddr;
// do something useful
rxCount++;
// for(int ii=0; ii<NCH*NDAT;ii++)
// if(data[ii]>maxVal) maxVal=data[ii];
}
void setup() {
// put your setup code here, to run once:
while(!Serial);
Serial.println("T4_Test");
sai1_configurePorts();
int fs = 192000;
int bit_clk = fs*(NW*NBITS);
int nov = 4; // factor of oversampling MCKL/BCKL
int fs_mclk = nov*bit_clk; // here 49.152 MHz
Serial.printf("bits / word : %d\n",NBITS);
Serial.printf("words / channel : %d\n",NW);
Serial.printf("number of channel : %d\n",NCH);
#if (TDM==1)
Serial.printf("Mode: TDM\n");
#else
Serial.printf("Mode: I2S\n");
#endif
Serial.printf("Frame rate : %9d Hz\n",fs);
Serial.printf("Bit Clock : %9d Hz\n",bit_clk);
Serial.printf("Master Clock: %9d Hz\n",fs_mclk);
//
int c0, c1, c2;
int n1, n2;
n1 = 4; // to ensure that input to last divisor (i.e. n2) is < 300 MHz
n2 = 4; // to reduce clock further to become MCLK
// the PLL runs between 27*24 and 54*24 MHz (before dividers)
// e.g. 49.152 = 32.768*24 / (n1*n2)
c0 = 32;
c1 = 768;
c2 = 1000;
// Note: c0+c1/c2 must be between 27 and 54
// here: 32.768*24 MHz = 786.4320 MHz
set_audioClock(c0,c1,c2);
sai1_setClock(n1,n2);
int ndiv = nov/2; // MCLK -> 2* BitClock
sai_rxConfig(ndiv, NCH);
sai_setupInput(rx_buffer, NCH, 2*NW*NDAT);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(I2S_PIN,OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(500);
Serial.printf("%d %d %d %d %x %x %x - %x %x - %x\n",rxCount,maxVal,
i2sCount1,i2sCount2,
maxPtr, minPtr, (uint32_t)rx_data2,
(uint32_t)&rx_buffer[0], (uint32_t)&rx_buffer[NBUF],
(uint32_t)dma[dma_ch].CSR);
rxCount=0;
maxVal=0;
i2sCount1=0;
i2sCount2=0;
maxPtr=0;
minPtr=0x40000000;
}