// 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_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 NCH 2
#define NDAT 128
#define NBITS 32
int32_t rx_buffer[2*NCH*NDAT];
int32_t *rx_data1 = rx_buffer;
int32_t *rx_data2 = rx_buffer+(NCH*NDAT);
void sai_rxConfig(int ndiv)
{
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 = I2S_RCR3_RCE; // single rx channel
//
I2S1->RX.CR4 = I2S_RCR4_FRSZ(NCH-1)
| I2S_RCR4_SYWD(NBITS-1)
| I2S_RCR4_FSD
| I2S_RCR4_MF
;
I2S1->RX.CR5 = I2S_RCR5_WNW(NBITS-1) | I2S_RCR5_W0W(NBITS-1) | I2S_RCR5_FBT(NBITS-1);
}
void sai_rx_isr(void); // forward declaration
void sai_setupInput(void * buffer, int ndat)
{
//
CCM_CCGR5 |= CCM_CCGR5_DMA(CCM_CCGR_ON);
DMA_CR = DMA_CR_GRP0PRI | DMA_CR_EMLM | DMA_CR_EDBG;
DMAMUX_CHCFG0 = 0x00000000;
DMAMUX_CHCFG0 = (DMAMUX_SOURCE_SAI1_RX & 0x7F) | DMAMUX_CHCFG_ENBL;
int nb = (NBITS/8);
//
int ch = 0;
DMA_CERQ = ch;
DMA_CERR = ch;
DMA_CEEI = ch;
DMA_CINT = ch;
DMA_TCD0_CSR = 0;
//
DMA_TCD0_SADDR = (void *)&I2S1_RDR0;
DMA_TCD0_SOFF = 0;
DMA_TCD0_ATTR = DMA_TCD_ATTR_SSIZE(nb/2) | DMA_TCD_ATTR_DSIZE(nb/2);
DMA_TCD0_NBYTES = nb;
DMA_TCD0_SLAST = 0;
DMA_TCD0_DADDR = buffer;
DMA_TCD0_DOFF = nb;
DMA_TCD0_CITER = ndat;
DMA_TCD0_BITER = ndat;
DMA_TCD0_DLASTSGA = -nb*ndat;
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 = ch;
DMA_TCD0_CSR |= DMA_TCD_CSR_START;
}
#define I2S_PIN 3
void sai_rxProcessing(void * taddr); // forward declaration
void sai_rx_isr(void)
{ uint32_t daddr, taddr;
//
DMA_CINT = 0;
//
daddr = (uint32_t) (DMA_TCD0_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;
//
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;
//
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*(NCH*NBITS);
int nov = 4; // factor of oversampling MCKL/BCKL
int fs_mclk = nov*bit_clk; // here 49.152 MHz
//
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);
sai_setupInput(rx_buffer, (2*NCH*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\n",rxCount,maxVal);
rxCount=0;
maxVal=0;
}