K64 - I2S + DMA

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

K64 - I2S + DMA

Jump to solution
1,685 Views
JHinkle
Senior Contributor I

I need some help.

My I2S appears to be partly working and I can't figure out what wrong.

I have a custom K64 board that I2S is driving a TI audio chip.

I have read and studied AN4369, AN4520, AN4800, and the code associated with AN4369.

My K64 is running at 120 mhz.  I have 2 audio buffers that hold 1024 (L and R) 16 bit audio values.  I have DMA set to load 4 bytes every load sequence and request an interrupt every 1024 events.

For the test, I am using a sample rate of 8000 hz and a MCLK of 12.288 Mhz.  Given I want a frame size of 2 words (4 bytes) - my bit rate is 256000 hz.  I have verified the bit freq and the MCLK freq with the logic probe capture shown below.

I instrumented my DMA IRQ to toggle an output to show when my DMA interrupts are occurring ... last logic line on image below.

I2S.png

Here is my code ..  I hate posting code ... but I can't see my problem

<code>

void Set_Dma_Mux( void )

{

  SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK ;  

  DMAMUX_CHCFG0 = 0;                       // disable the channel to configure it

  DMAMUX_CHCFG0 = DMAMUX_CHCFG_SOURCE(13) ;      //I2S0 Transmit  .. page 95

  DMAMUX_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK ;      // enable the mux

}

void Init_Audio_DMA(void)

{

  // setup the DMA channels ( CH0 = playback  )

  DMA_CR = 0 | DMA_CR_EDBG_MASK; // Stall DMA transfers when debugger is halted (avoid noise)  // no minor loop mapping

  DMA_DCHPRI15 = 0;   // DMA Errors as reset makes priority value equal to chan# -- so make chan 15 priority 0 --- now all unique

  DMA_DCHPRI0 = 15;  // cannot be pre-empeted, can pre-empt, highest priority

  // fill the TCD area

  DMA_TCD0_SADDR          = (dword)Audio1 ; // alternated with Audio_Source_Blk_B

  DMA_TCD0_SOFF           = 4;                              // 2 words offset

  DMA_TCD0_ATTR           = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(1);   // no circular addressing S&D, 16 bit S&D

  DMA_TCD0_NBYTES_MLNO    = 4;                              // 2  16bit sample every minor loop

  DMA_TCD0_SLAST          = 0; // not needed -- IRQ will set new start

  DMA_TCD0_DADDR          = (dword) &I2S0_TDR0;    // the FTM Channel 0 duty value

  DMA_TCD0_DOFF           = 0;

  DMA_TCD0_CITER_ELINKNO  = AUDIO_SIZE;              // total samples

  DMA_TCD0_DLASTSGA       = 0;                       // no final last adjustment ( does not move )

  DMA_TCD0_CSR            = DMA_CSR_INTMAJOR_MASK ;  // interrupt when done

  DMA_TCD0_BITER_ELINKNO  = AUDIO_SIZE;              // no chan links, total samples

  

  // configure DMA_MUX to trigger DMA channel 0

  Set_Dma_Mux();

  // moved to start Audio

  // DMA_ERQ = DMA_ERQ_ERQ0_MASK ; // now enable chan0 for triggers

// Install DMA0 Major loop complete IRQ

   NVIC_SetPriority(DMA0_IRQn, AudioDMA_Priority);

   NVIC_EnableIRQ(DMA0_IRQn);

}

void Init_I2S_Clock(void)

{

// note ... for 11.2896 clock

// use system clock ... 120 mhz ... Delta FQ = 17.623 with FS = 205 and DS = 2179

// use Xtal ... 25 mhz --- Delta 40.591 with FS = 89 and DS = 473

// ???? Xtal is more stable than System Clock????

// I2S_MCR_MICS == 0 --- System Clock -- 120mhz

// I2S_MCR_MICS == 1 --- Xtal --- 25 mhz

  // Select input clock 0 and output enable

    I2S0_MCR = I2S_MCR_MICS(0) | I2S_MCR_MOE_MASK; // page 167 .. MICS selection  ... 0 == SYSTEM CLK!!!! -- 120mhz ... clk divider enabled

      

// reference only ... based on 96 mhz clock

// Divide to get the 11.2896 MHz from 96MHz (96* (2/17))

  // I2S0_MDR = I2S_MDR_FRACT(1) | I2S_MDR_DIVIDE(16);

// Divide to get the 12.2880 MHz from 96MHz (96* (16/125))

  // I2S0_MDR = I2S_MDR_FRACT(15) | I2S_MDR_DIVIDE(124);

  //I2S0_MDR = I2S_MDR_FRACT(205-1) | I2S_MDR_DIVIDE(2179-1); // see above

  I2S0_MDR = I2S_MDR_FRACT(64-1) | I2S_MDR_DIVIDE(625-1); // gives 12.288(48 KHz * 256) .. multiple of 8khz

}

void Init_I2S(void)

{

  // NOTE ... MUTE pin init earlier and set to 0 (mute)

 

    // enable system clock to the I2S module

    SIM_SCGC6 |= SIM_SCGC6_I2S_MASK;

  Init_I2S_Clock(); // sets up I2S_MCLK

    PORTE_PCR6 |= PORT_PCR_MUX(0x04);  // SCLK

    PORTE_PCR12 |= PORT_PCR_MUX(0x04);  // BCLK

    PORTE_PCR11 |= PORT_PCR_MUX(0x04);  // LR_CLK

    PORTE_PCR10 |= PORT_PCR_MUX(0x04);  // Data OUT to codex

 

  // turn xmit OFF - return to reset conditions

  I2S0_TCSR = I2S_TCSR_FR_MASK | I2S_TCSR_SR_MASK; // software reset, fifo reset and Xmit disabled

  // wait until off .. if current frame active

  while(I2S0_TCSR & I2S_TCSR_TE_MASK)

    {

            __asm__ __volatile__ ("nop");

    }; // wait until reset is done

    I2S0_TCR1  = I2S_TCR1_TFW(I2S_FRAME_SIZE*2);        // set FIFO watermark .. so frame can load

// note MSEL = 0 for BUS Clk

// = 1 for MCLK

// MCLK == 11.2896

// BLK divide to get to 44100 ...

// BLK for sample rate of 8khz ...  bitclk = 8000 * 16 * 2 = 256000 .. so must divide mclk 12.288mhz / 256000 ... by 48

    I2S0_TCR2  = I2S_TCR2_SYNC(0) | // use asynchronous mode

  I2S_TCR2_MSEL(1) |      // use MCLK

  I2S_TCR2_BCP_MASK  |    // BCLK polarity: Bit clock is active low with drive outputs on falling edge and sample inputs on rising edge.

  I2S_TCR2_BCD_MASK | // BCLK Master out

  I2S_TCR2_DIV(24-1);     // divide internal master clock to generate bit clock divide buy (DIV+1)*2

    I2S0_TCR3  = I2S_TCR3_TCE_MASK;              // transmit data channel is enabled

  I2S0_TCR4  = I2S_TCR4_FRSZ(I2S_FRAME_SIZE-1) |       // frame size in words less 1

  I2S_TCR4_SYWD((I2S_FRAME_SIZE*16)-1) |       // number of bits in frame sync

  I2S_TCR4_MF_MASK | // MSB first

  I2S_TCR4_FSE_MASK |              // Frame sync one bit before the frame

  // sync polarity HI

  I2S_TCR4_FSD_MASK; // WCLK is generated internally (master mode)

  I2S0_TCR5  = I2S_TCR5_W0W(16-1) | // bits per word, first frame

  I2S_TCR5_WNW(16-1) |        // bits per word, nth frame

  I2S_TCR5_FBT(0x0f);        // index of MSB bit

  I2S0_TMR = 0;                           // No word mask

  

} /* end init() */

void Start_I2S_Audio(void)

{

  AudioMutePIN = 1;

  DMA_SERQ = DMA_SERQ_SERQ(0) ; // now enable chan0 for triggers

  I2S0_TCSR = I2S_TCSR_TE_MASK | // enable xmit

  I2S_TCSR_FR_MASK | // fifo reset

  I2S_TCSR_DBGE_MASK | // debug enabled

  I2S_TCSR_BCE_MASK | // bit clock enabled

  I2S_TCSR_FRDE_MASK; // fifo watermark - DMA

}

</code>

I any one is knowledgeable in K64 I2S setup, please have a look and tell me where I've gone wrong.

Thanks in advance for any help or comments.

Joe

0 Kudos
Reply
1 Solution
1,280 Views
JHinkle
Senior Contributor I

Found my issue -- solved.

View solution in original post

0 Kudos
Reply
3 Replies
1,281 Views
JHinkle
Senior Contributor I

Found my issue -- solved.

0 Kudos
Reply
1,280 Views
takmingmarcopan
Contributor II

Well what was the fix?

0 Kudos
Reply
1,280 Views
JHinkle
Senior Contributor I

I'm not grasping something about this I2S device.

Why are there 2 TDR registers? (TRD0 and TDR1)

Why are there 2  I2S0_TXD pins on the device?  (I2S0_TXD0 and I2S0_TXD1)

I'm only using the TRD0 register and the I2S0_TXD0 pin -- I'm expecting a 2 channel (stereo) output.  Is this assumption incorrect?

I tried an experiment to help isolate my issue.

I removed DMA from the picture altogether.   I set a loop where the cpu looks at the I2S_TCSR_FRF flag (fifo watermark says load data) and loads a left channel 16 bit value followed by a right channel 16 bit value.

while(1)

{

      R1 = I2S0_TCSR;

      R2 = I2S0_TFR0;

       if(R1 & I2S_TCSR_FRF_MASK)

       {

             I2S0_TDR0 = 0xff00;

             I2S0_TDR0 = 0x8001;

        }

}

My understanding of how I programmed the I2S was I wanted a frame of 32 bits where the first 16 bits where L channel and the last 16 bits was the R channel.

I captured the I2S0_TFR0 to see what the FIFO pointers are.

I'm puzzled -- since the I2S_TCSR_FRF_MASK is set (watermark level reached) but the I2S0_TFR0 register shows WFP (write fifo pointer) is 9 and RFP (read fifo pointer) is 1.

I2S1.png

As you can see by the capture of the I2S0_TXD0 pin -- the I2S device outputs two of the L channel data (0xff00) and none of the R channel data (0x8001).  The L/R Frame line is not indicating the frame properly (L/R) .. it shows a continuous L -- which is what is being sent. 

Only 2 samples are xmitted (both L channel value) and then nothing else -- why??

Can anyone with Kinetis I2S experience please comment?

Thanks.

Joe

0 Kudos
Reply