I2S TRX synchronizing with external clock

greentea

Member
I'm using Teensy 4.1 and trying to make a TRX, which converts 2ch monoral signals into 1ch I2S stereo signal.

I set SAI1 module as RX, receiving 2ch monorals from a source,
and SAI2 module as TX, sending 1ch stereo I2S.


Here is the problem I'm stucking on,
I don't understand how to synchronize RX and TX with source's clock (figure 1).
figure1.png

What I understand and have already checked:
- SAI1 module can receive 2ch monoral signals (not I2S) in slave mode (synchronizing with source's clock)
- SAI2 can send 1ch stereo I2S signal in master mode (using internal CCM)

What I don't understand:
- If both RX and TX can be slave mode
- How to synchronize SAI1 and SAI2 module with source's bit clock. Using "bit clock swap"?, Connecting the pin externally?...

Thi is the configration of RX/TX registers.
Code:
  //0:async, 1:sync 
	int rsync = 0;
	int tsync = 1;

  //SAI1 Clock Setting (RX)
	I2S1_TMR = 0;
	//I2S1_TCSR = (1<<25); //Reset
	I2S1_TCR1 = I2S_TCR1_RFW(RX_watermark);
	I2S1_TCR2 = I2S_TCR2_SYNC(tsync) |  | I2S_TCR2_MSEL(1);
	I2S1_TCR3 = I2S_TCR3_TCE;
	I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((16-1)) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FPACK;
	I2S1_TCR5 = I2S_TCR5_WNW((16-1)) | I2S_TCR5_W0W((16-1)) | I2S_TCR5_FBT((16-1));

	I2S1_RMR = 0;
	//I2S1_RCSR = (1<<25); //Reset
	I2S1_RCR1 = I2S_RCR1_RFW(RX_watermark);
	I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_MSEL(1);
	I2S1_RCR3 = I2S_RCR3_RCE;
	I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((16-1)) | I2S_RCR4_MF | I2S_RCR4_FSP | I2S_RCR4_FPACK;
	I2S1_RCR5 = I2S_RCR5_WNW((16-1)) | I2S_RCR5_W0W((16-1)) | I2S_RCR5_FBT((16-1));

	I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FR; //Transmitter Enable, BCLK Enable, FIFO Reset, (FIFO Error Interrupt Enable)
        I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FR | 0x00000100; //Receiver Enable, BCLK Enable, FIFO Reset, FIFO Request Interrupt Enable


  //SAI2 Clock Setting (TX)
	I2S2_TMR = 0;
	//I2S2_TCSR = (1<<25); //Reset
	I2S2_TCR1 = I2S_TCR1_RFW(TX_watermark);
	I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | (I2S_TCR2_BCD | I2S_TCR2_MSEL(1)) | I2S_TCR2_DIV((1)); 
	I2S2_TCR3 = I2S_TCR3_TCE;
	I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((16-1)) | I2S_TCR4_MF | I2S_TCR4_FSP| I2S_TCR4_FSD | I2S_TCR4_FPACK;
	I2S2_TCR5 = I2S_TCR5_WNW((16-1)) | I2S_TCR5_W0W((16-1)) | I2S_TCR5_FBT((16-1));

	I2S2_RMR = 0;
	//I2S2_RCSR = (1<<25); //Reset
	I2S2_RCR1 = I2S_RCR1_RFW(TX_watermark);
	I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | (I2S_RCR2_BCD | I2S_RCR2_MSEL(1)) | I2S_RCR2_DIV((1));
	I2S2_RCR3 = I2S_RCR3_RCE;
	I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((16-1)) | I2S_RCR4_MF | I2S_RCR4_FSP| I2S_RCR4_FSD | I2S_RCR4_FPACK;
	I2S2_RCR5 = I2S_RCR5_WNW((16-1)) | I2S_RCR5_W0W((16-1)) | I2S_RCR5_FBT((16-1)); //RX LRCLK

	I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FR | 0x00000100; //Transmitter Enable, BCLK Enable, FIFO Reset, FIFO Request Interrupt Enable
        I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FR; //Receiver Enable, BCLK Enable, FIFO Reset


  //SAI1 Pin Connection
        IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_11 = 3; //SAI1_RX_BCLK (pin 21)
        IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_10 = 3; //SAI1_RX_SYNC (pin 20)
        IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_15 = 3; //SAI1_RX_Data0 (pin 8)

        IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3
	IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3
        IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // 2=GPIO_B0_15_ALT3

  //SAI2 Pin Connection
        IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_07 = 2;  //SAI2_MCLK (pin 33)
        IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_06 = 2; //SAI2_TX_BCLK (pin 4)
        IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_05 = 2;  //SAI2_TX_SYNC (pin 3)
        IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_04 = 2; //SAI2_TX_DATA (pin 2)


In the first place, Can SAI1 module be used as both RX and TX (figure 2)?
I'm using SAI1(RX) and SAI2(TX) separetely now though...
figure2.png


I'd appreciate if you kindly replied.:)
 
Wouldn't the transmitter require double the clock rate of the receiver, since it has to transmit double the amount of bits in the same amount of time? Or is the sampling rate implicitly halved?
 
THX for your comment, jmarsh.

Acutually, RX doesn't need double clock rate, I think.
Bit Clock rate can be same between RX and TX.

That's because, datas of both 2ch monoral signals and I2S are 16bits of word, while both SYNC clock is 32bits cycles (figure 3).
So Lch and Rch can be contained in one word.
figure3.png
 
Last edited:
Excuse me for the the wrong code above...

This is the true code,
Code:
  //0:async, 1:sync 
	int rsync = 0;
	int tsync = 1;

  //SAI1 Clock Setting (RX)
	I2S1_TMR = 0;
	//I2S1_TCSR = (1<<25); //Reset
	I2S1_TCR1 = I2S_TCR1_RFW(RX_watermark);
	I2S1_TCR2 = I2S_TCR2_SYNC(tsync) |  | I2S_TCR2_MSEL(1) | I2S_TCR2_DIV((1));
	I2S1_TCR3 = I2S_TCR3_TCE_2CH;
	I2S1_TCR4 = I2S_TCR4_FRSZ((1-1)) | I2S_TCR4_SYWD((1-1)) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FPACK;
	I2S1_TCR5 = I2S_TCR5_WNW((1-1)) | I2S_TCR5_W0W((16-1)) | I2S_TCR5_FBT((16-1));

	I2S1_RMR = 0;
	//I2S1_RCSR = (1<<25); //Reset
	I2S1_RCR1 = I2S_RCR1_RFW(RX_watermark);
	I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_MSEL(1) | I2S_RCR2_DIV((1));
	I2S1_RCR3 = I2S_RCR3_RCE_2CH;
	I2S1_RCR4 = I2S_RCR4_FRSZ((1-1)) | I2S_RCR4_SYWD((1-1)) | I2S_RCR4_MF | I2S_RCR4_FSP | I2S_RCR4_FPACK;
	I2S1_RCR5 = I2S_RCR5_WNW((16-1)) | I2S_RCR5_W0W((16-1)) | I2S_RCR5_FBT((16-1));

	I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FR; //Transmitter Enable, BCLK Enable, FIFO Reset, (FIFO Error Interrupt Enable)
        I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FR | 0x00000100; //Receiver Enable, BCLK Enable, FIFO Reset, FIFO Request Interrupt Enable


  //SAI2 Clock Setting (TX)
	I2S2_TMR = 0;
	//I2S2_TCSR = (1<<25); //Reset
	I2S2_TCR1 = I2S_TCR1_RFW(TX_watermark);
	I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | (I2S_TCR2_BCD | I2S_TCR2_MSEL(1)) | I2S_TCR2_DIV((1)); 
	I2S2_TCR3 = I2S_TCR3_TCE;
	I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((16-1)) | I2S_TCR4_MF | I2S_TCR4_FSP| I2S_TCR4_FSD | I2S_TCR4_FPACK;
	I2S2_TCR5 = I2S_TCR5_WNW((16-1)) | I2S_TCR5_W0W((16-1)) | I2S_TCR5_FBT((16-1));

	I2S2_RMR = 0;
	//I2S2_RCSR = (1<<25); //Reset
	I2S2_RCR1 = I2S_RCR1_RFW(TX_watermark);
	I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | (I2S_RCR2_BCD | I2S_RCR2_MSEL(1)) | I2S_RCR2_DIV((1));
	I2S2_RCR3 = I2S_RCR3_RCE;
	I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((16-1)) | I2S_RCR4_MF | I2S_RCR4_FSP| I2S_RCR4_FSD | I2S_RCR4_FPACK;
	I2S2_RCR5 = I2S_RCR5_WNW((16-1)) | I2S_RCR5_W0W((16-1)) | I2S_RCR5_FBT((16-1)); //RX LRCLK

	I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FR | 0x00000100; //Transmitter Enable, BCLK Enable, FIFO Reset, FIFO Request Interrupt Enable
        I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FR; //Receiver Enable, BCLK Enable, FIFO Reset


  //SAI1 Pin Connection
        IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_11 = 3; //SAI1_RX_BCLK (pin 21)
        IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_10 = 3; //SAI1_RX_SYNC (pin 20)
        IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_15 = 3; //SAI1_RX_Data0 (pin 8)

        IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3
	IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3
        IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // 2=GPIO_B0_15_ALT3

  //SAI2 Pin Connection
        IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_07 = 2;  //SAI2_MCLK (pin 33)
        IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_06 = 2; //SAI2_TX_BCLK (pin 4)
        IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_05 = 2;  //SAI2_TX_SYNC (pin 3)
        IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_04 = 2; //SAI2_TX_DATA (pin 2)
 
THX for your comment, jmarsh.

Acutually, RX doesn't need double clock rate, I think.
Bit Clock rate can be same between RX and TX.

That's because, datas of both 2ch monoral signals and I2S are 16bits of word, while both SYNC clock is 32bits cycles (figure 3).
So Lch and Rch can be contained in one word.

Does that mean the original DATA0 and DATA1 lines (from the source) are outputting nothing for half a sync frame?
 
I am assuming you are not using Teensy Audio Library. This looks easy with the audio library perhaps with some audio library modifications.
Only use SAI1 because it is actually well flexible it can have up to 4 input data with 1 data output.
SAI1quadIN.jpg
Code:
/*
   A simple PassThroughQuad hardware test.
   The I2S signals are used in "master" mode.
*/

// Teensy 4.x SAI1 Pinout
// 20  LRCLK Output
// 21  BCLK  Output
// 23  MCLK  Output
// 8   RX    Input
// 6   RX    Input
// 7   TX    Output

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2SQuad        i2s_quad1;      //xy=150,154
AudioMixer4              mixer1;         //xy=404,229
AudioMixer4              mixer;          //xy=408,88
AudioOutputI2S           i2s1;           //xy=629,153
AudioConnection          patchCord1(i2s_quad1, 0, mixer, 0);
AudioConnection          patchCord2(i2s_quad1, 1, mixer, 1);
AudioConnection          patchCord3(i2s_quad1, 2, mixer1, 0);
AudioConnection          patchCord4(i2s_quad1, 3, mixer1, 1);
AudioConnection          patchCord5(mixer1, 0, i2s1, 1);
AudioConnection          patchCord6(mixer, 0, i2s1, 0);
// GUItool: end automatically generated code

void setup() {
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(20);
}

void loop() {
}
 
Thanks for the comment, Chris O.

I am assuming you are not using Teensy Audio Library. This looks easy with the audio library perhaps with some audio library modifications.

That's true Paul's Audio Library is awesome and so easy to use.
But I think that library can be used as "master" mode for both RX and TX.

I'd like to use external clock to synchronize both RX and TX. And I assumed that I should have used "slave" mode for both.
Do you know how to do that???
Though Reference manual says one of them should be set as "async" in RCR2/TCR2 register ("i.MX RT1060 Processor Reference Manual, Rev. 3", page 2007 and 2023).

Only use SAI1 because it is actually well flexible it can have up to 4 input data with 1 data output.
I didn't know that SAI1 module can be used for data input and data output at the same time.
So I don't have to use the SAI2 module, right???
I'll try that, THX!
 
The Teensy audio library can be made to work with slave mode but it does need to be modified.

For example 4 data input slave ( i use this with CoolAudio ADAT V1402 )
Code:
/*************************  slave  ********************************/
void AudioInputI2SOctslave::begin(void)
{
	dma.begin(true); // Allocate the DMA channel first

	AudioOutputI2SOctslave::config_i2sSlave(); // slave config
	
	I2S1_RCR3 = I2S_RCR3_RCE_4CH; // Receive Channel Enable 4
	CORE_PIN8_CONFIG = 3;  //1:RX_DATA0
	CORE_PIN6_CONFIG = 3;  //1:RX_DATA1
	CORE_PIN9_CONFIG = 3;  //1:RX_DATA2
	CORE_PIN32_CONFIG = 3; //1:RX_DATA3
	IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
	IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
	IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874
	IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // GPIO_B0_12_ALT3, pg 875

	dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2);
	dma.TCD->SOFF = 4;
	dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
	dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
		DMA_TCD_NBYTES_MLOFFYES_MLOFF(-16) |
		DMA_TCD_NBYTES_MLOFFYES_NBYTES(8);
	dma.TCD->SLAST = -16;
	dma.TCD->DADDR = i2s_rx_buffer;
	dma.TCD->DOFF = 2;
	dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
	dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
	dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
	dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
	dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);

	I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
	update_responsibility = update_setup();
	dma.enable();
	dma.attachInterrupt(isr);
}




void AudioOutputI2SOctslave::config_i2sSlave(void) // slave config
{
	CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);

	// if either transmitter or receiver is enabled, do nothing
	if (I2S1_TCSR & I2S_TCSR_TE) return;
	if (I2S1_RCSR & I2S_RCSR_RE) return;

	// not using MCLK in slave mode - hope that's ok?
	//CORE_PIN23_CONFIG = 3;  // AD_B1_09  ALT3=SAI1_MCLK
	CORE_PIN21_CONFIG = 3;  // AD_B1_11  ALT3=SAI1_RX_BCLK
	CORE_PIN20_CONFIG = 3;  // AD_B1_10  ALT3=SAI1_RX_SYNC (LRCLK)
	IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3, page 868
	IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3, page 872

	// configure transmitter
	I2S1_TMR = 0;
	I2S1_TCR1 = I2S_TCR1_RFW(4);  // watermark
	I2S1_TCR2 = I2S_TCR2_SYNC(1) | I2S_TCR2_BCP;
	I2S1_TCR3 = I2S_TCR3_TCE;
	I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
		| I2S_TCR4_FSE; // | I2S_TCR4_FSP | I2S_RCR4_FSD;
	I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

	// configure receiver
	I2S1_RMR = 0;
	I2S1_RCR1 = I2S_RCR1_RFW(4);
	I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP;
	I2S1_RCR3 = I2S_RCR3_RCE;
	I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
		| I2S_RCR4_FSE; // | I2S_RCR4_FSP; // CoolAudio ADAT V1402
	I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
}

SAI1 output slave pin 7
Code:
void AudioOutputI2Sslave::begin(void)
{

	dma.begin(true); // Allocate the DMA channel first

	block_left_1st = NULL;
	block_right_1st = NULL;

	AudioOutputI2Sslave::config_i2s();

#if defined(KINETISK)
	CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
	dma.TCD->SADDR = i2s_tx_buffer;
	dma.TCD->SOFF = 2;
	dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
	dma.TCD->NBYTES_MLNO = 2;
	dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
	dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2);
	dma.TCD->DOFF = 0;
	dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
	dma.TCD->DLASTSGA = 0;
	dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
	dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
	dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
	dma.enable();

	I2S0_TCSR = I2S_TCSR_SR;
	I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;

#elif defined(__IMXRT1062__)
	[COLOR="#FF0000"]CORE_PIN7_CONFIG  = 3;  //1:TX_DATA0[/COLOR]
	dma.TCD->SADDR = i2s_tx_buffer;
	dma.TCD->SOFF = 2;
	dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
	dma.TCD->NBYTES_MLNO = 2;
	dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
	dma.TCD->DOFF = 0;
	dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
	dma.TCD->DLASTSGA = 0;
	dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
	dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2);
	dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
	dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
	dma.enable();

	I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
	I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif

	update_responsibility = update_setup();
	dma.attachInterrupt(isr);
}

void AudioOutputI2Sslave::config_i2s(void)
{
#if defined(KINETISK)
	SIM_SCGC6 |= SIM_SCGC6_I2S;
	SIM_SCGC7 |= SIM_SCGC7_DMA;
	SIM_SCGC6 |= SIM_SCGC6_DMAMUX;

	// if either transmitter or receiver is enabled, do nothing
	if (I2S0_TCSR & I2S_TCSR_TE) return;
	if (I2S0_RCSR & I2S_RCSR_RE) return;

	// Select input clock 0
	// Configure to input the bit-clock from pin, bypasses the MCLK divider
	I2S0_MCR = I2S_MCR_MICS(0);
	I2S0_MDR = 0;

	// configure transmitter
	I2S0_TMR = 0;
	I2S0_TCR1 = I2S_TCR1_TFW(1);  // watermark at half fifo size
	I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP;

	I2S0_TCR3 = I2S_TCR3_TCE;
	I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
		| I2S_TCR4_FSE | I2S_TCR4_FSP;

	I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

	// configure receiver (sync'd to transmitter clocks)
	I2S0_RMR = 0;
	I2S0_RCR1 = I2S_RCR1_RFW(1);
	I2S0_RCR2 = I2S_RCR2_SYNC(1) | I2S_TCR2_BCP;

	I2S0_RCR3 = I2S_RCR3_RCE;
	I2S0_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
		| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;

	I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);

	// configure pin mux for 3 clock signals
	CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
	CORE_PIN9_CONFIG  = PORT_PCR_MUX(6); // pin  9, PTC3, I2S0_TX_BCLK
	CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK

#elif defined(__IMXRT1062__)

	CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);

	// if either transmitter or receiver is enabled, do nothing
	if (I2S1_TCSR & I2S_TCSR_TE) return;
	if (I2S1_RCSR & I2S_RCSR_RE) return;

	// not using MCLK in slave mode - hope that's ok?
	//CORE_PIN23_CONFIG = 3;  // AD_B1_09  ALT3=SAI1_MCLK
	CORE_PIN21_CONFIG = 3;  // AD_B1_11  ALT3=SAI1_RX_BCLK
	CORE_PIN20_CONFIG = 3;  // AD_B1_10  ALT3=SAI1_RX_SYNC
	IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3, page 868
	IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3, page 872

	// configure transmitter
	I2S1_TMR = 0;
	I2S1_TCR1 = I2S_TCR1_RFW(1);  // watermark at half fifo size
	I2S1_TCR2 = I2S_TCR2_SYNC(1) | I2S_TCR2_BCP;
	I2S1_TCR3 = I2S_TCR3_TCE;
	I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
		| I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_RCR4_FSD;
	I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

	// configure receiver
	I2S1_RMR = 0;
	I2S1_RCR1 = I2S_RCR1_RFW(1);
	I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP;
	I2S1_RCR3 = I2S_RCR3_RCE;
	I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
		| I2S_RCR4_FSE | I2S_RCR4_FSP;
	I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);

#endif
}
 
Finally, I made it with the Audio library "AudioInputI2Sslave" and "AudioOutputI2Sslave".
figure4.jpg

It was mush simpler than I thoght, thanks to the Paul's library.

This is the code of the simple converter from I2S into I2S.
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

#define I2S_TCR4_FSD 1;
#define I2S_RCR4_FSD 1;

// GUItool: begin automatically generated code
AudioInputI2Sslave       i2sslave1;      //xy=431.8004398345947,324.090913772583
AudioOutputI2Sslave      i2sslave2;      //xy=604.8004150390625,324.09088134765625
AudioConnection          patchCord1(i2sslave1, 0, i2sslave2, 0);
AudioConnection          patchCord2(i2sslave1, 1, i2sslave2, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=531,394
// GUItool: end automatically generated code


void setup() {
  //Set Clocks Output
  IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_02 = 3; //TX_BCLK (pin 36)
  IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_03 = 3; //TX_SYNC (pin 37)
  I2S1_TCR2 |= I2S_TCR2_BCD;
  I2S1_TCR4 |= I2S_TCR4_FSD;

  AudioMemory(20);
  sgtl5000_1.enable();
  sgtl5000_1.unmuteHeadphone();
  sgtl5000_1.volume(0.5);
}

void loop() {
  Serial.print("#");
  delay(1000);
}

It's not the converter from 2ch monorals into I2S,
but I believe I can arrange this for my programs.

Thank you so much for the hint, Crhis O!:eek:
 
Back
Top