is there any demo code for using I2S?

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

is there any demo code for using I2S?

Jump to solution
16,408 Views
JimCheng
Contributor II

is there any one can share with me the I2S reference code?

1 Solution
5,740 Views
carlos_neri
NXP Employee
NXP Employee

Attached is a sample code that enables SAI module as I2S (stereo) slave in synchronous mode (TX and RX share BCLK and Frame synch). Uses DMA for transfer and the TWR-AUDIO_SGTL Rev B1 (board with 24.576Mhz oscillator).

Adding a couple of more data:

- Configures the SGTL5000 as I2S master and a 48Khz audio

- The application takes the audio from the codec line in and sends it to line out and headphone outputs.

- Works on a TWR-K60D100M

- Enables the codec 5-band graphic equalizer when SW1 is pressed

- Compiled and tested on CW10.2 with latest patches.

View solution in original post

30 Replies
967 Views
brian64
Contributor I

Carlos,

Upon further investigation, I noticed that the output signals are indeed there; they are just very low amplitude (~40mVp-p), and they seem to be on opposite channels between the Line Out and Headphones. I'll play with adjusting the output volume and see if that makes things better.

-Brian

0 Kudos
Reply
967 Views
carlos_neri
NXP Employee
NXP Employee

Brian,

As you noticed, the channels on line-in and line-out are inverted, this is a known issue and documented on the HW errata:

http://cache.freescale.com/files/soft_dev_tools/doc/errata/TWR-AUDIO-SGTL_HWER.pdf?fpsp=1&WT_TYPE=Er...

The samples are not modified by the firmware, hence the output should be the same amplitude, but this would depend on the load as well and the volume on the codec headphone amplifier plus the DAC volume configure. You may want to change those parameters to get the desired output.

0 Kudos
Reply
967 Views
brian64
Contributor I

Carlos,

I figured out the problem...there is a huge attenuator hidden inside the audio jack I have been using for testing! (Sigh.) Thanks a ton for your other help thus far!

-Brian

0 Kudos
Reply
967 Views
carlos_neri
NXP Employee
NXP Employee

Brian,

Good news :smileygrin:

Question, is this from the audio jack on your TWR-AUDIO-SGTL?

Saludos,

Carlos Neri

0 Kudos
Reply
967 Views
BrianMoon
Contributor II

Carlos,

No; the attenuation circuit resides inside an 1/8" audio jack adapter that I used in this case. I believe the TWR-AUDIO-SGTL audio circuit is just fine -- it does not appear to induce much/any attenuation at all.

Thanks again for all your help!

-Brian

967 Views
BrianMoon
Contributor II

Hi Carlos,

Thank you for the nice demo code. Do you have a version that works with the TWR-K60N512?

-Brian

0 Kudos
Reply
967 Views
carlos_neri
NXP Employee
NXP Employee

Brian,

Here is it. This one is not as clean and layered as the last one but hopefully will help you.

It outputs a sine wave or takes the data from the line in and send is to line out depending on SW1.

0 Kudos
Reply
967 Views
BrianMoon
Contributor II

Thank you, Carlos!

I tried the code and had to modify the project a little to get it to run with the embedded OSJTAG configuration. The project seems to indeed output a 2kHz sinusoid on the Line Out port, however I noticed that there are small glitches in the output signal. I also can't tell if, after pressing SW1, the Line In to Line Out feature is working or not. Any hints as to how I might correct the sine output glitches (it seems like something in the buffer management might be off by one or something)? Also, should I expect the Line In to Line Out mechanism to work?

Thank you for any help you can provide!

-Brian

0 Kudos
Reply
967 Views
egoodii
Senior Contributor III

I don't exactly have a demo project, what I do have is my working audio-interface MQX task that processes I2S (as a slave)in TDM mode using DMA.  It keeps per-channel ping-pong buffers that the DMA has de-interleaved, and for each calls an external function to 'do something' for each TX/RX channel.

 

/******************************************************************************
 *
 * FILE NAME: Audio.c
 *
 * DESCRIPTION: Audio interface
 *

#include "Os.h"
#include "Audio.h"
#include "Codec.h"


/******************************************************************************
 *  Module defines
 *****************************************************************************/


#define TX_DMA_Chan 5
#define RX_DMA_Chan 4
#define TS_Mask 0xFFFFFCE7UL
#define AUDIO_NUM_CHANNELS          4
#define AUDIO_BYTES_PER_SAMPLE      2       // 16-bit samples
#define AUDIO_SAMPLES_PER_BLOCK     160     // 160 samples in each block (5ms @ 32 kHz)

 

/******************************************************************************
 *  Module type definitions
 *****************************************************************************/
//The following structure matchs the TDM stream word order, and is sized to implement
//  full double-buffering (ping-pong) of each stream.  On each DMA interrupt, a counter
//  is set to 0 or 1 to indicate the starting point of the curent working set.
typedef struct
{
    s16_t   samples[AUDIO_SAMPLES_PER_BLOCK];
} audioBlock_t;

/******************************************************************************
 *  Module variables
 *****************************************************************************/
audioCallbackRxTxBlockFunc_f rx_tx_block_callback = NULL;   // Callback function for rx/tx blocks

static volatile audioBlock_t RxDMA_Buf[AUDIO_NUM_CHANNELS][2], TxDMA_Buf[AUDIO_NUM_CHANNELS][2];
static u16_t SampleSetOffset;

/******************************************************************************
 *  Module function prototypes
 *****************************************************************************/
static void_t SSI1Init();


/******************************************************************************
 *  Public functions
 *****************************************************************************/

 

/******************************************************************************
 * NAME: Audio_Task
 *
 * DESCRIPTION:
 *     Audio processing task. Set up the I2C, thence the CoDec, thence SSI in
 *     TDM and DMA to buffer Audio I/O.
 *
 * PARAMETERS:
 *     param - not used.
 *
 * RETURN: none.
 *****************************************************************************/
void_t Audio_Task( u32_t param )
{
   
    // Configure CoDecs
  Codec_Init();
   
    // Now for the Kinetis IIS port & DMA
  SSI1Init();
   
  for(;:smileywink://ever
  {
      uint16_t i;

      _task_block();
  //  GPIOA_PCOR |= 1<<10;       //debug out to 0
   
      if( NULL != rx_tx_block_callback )
      {
          for( i = 0; i < AUDIO_NUM_CHANNELS; i++ )
          {
              rx_tx_block_callback( i, (const audioBlock_t*) &RxDMA_Buf[i][SampleSetOffset], (audioBlock_t*) &TxDMA_Buf[i][SampleSetOffset] );
          }
      }
   // GPIOA_PSOR |= 1<<10;       //debug out back to 1
  }
}


/******************************************************************************
 *  Module functions
 *****************************************************************************/

/*DMA ISR*/

static pointer Pblock_td_ptr;
void BlockDMA( pointer user_isr_ptr  )
{
   //  GPIOA_PCOR |= 1<<10;       //debug out to 0


    DMA_CINT = DMA_CINT_CINT(RX_DMA_Chan);        //Clear this interrupt request

    SampleSetOffset = 0;        //based on current DMA iteration
    if( (DMA_CITER_ELINKNO(RX_DMA_Chan) & DMA_CITER_ELINKNO_CITER_MASK ) > (AUDIO_SAMPLES_PER_BLOCK + 2))
       SampleSetOffset = 1;
    _task_ready(Pblock_td_ptr);
 //   GPIOA_PSOR |= 1<<10;       //debug out back to 1
}

/******************************************************************************
 * NAME: SSI1Init
 *
 * DESCRIPTION:
 *      Initializes the device. Allocates memory for the device data
 *      structure, allocates interrupt vectors and sets interrupt
 *      priority, sets pin routing, sets timing, etc.
 *      This method can be called only once. Before the second call
 *      of Init() the Deinit() must be called first.  
 *
 * PARAMETERS:
 *     param - not used.
 *
 * RETURN: none.
 *****************************************************************************/
static void_t SSI1Init( void_t )
{
  uint32_t temp;
    /* SIM_SCGC6: I2S=1 & DMAMUX=1*/  //Turn on module clocks to bring registers into address space
    SIM_SCGC6 |= (SIM_SCGC6_I2S_MASK | SIM_SCGC6_DMAMUX_MASK) ;                      
    SIM_SCGC7 |= (SIM_SCGC7_DMA_MASK) ;
    /* Interrupt vector(s) priority setting */  //No interrupts for I2S, only DMA RX
    /* NVICIP79: PRI79=0x80 */
    // NVICIP79 = (u8_t)0x80U;                            
    /* NVICISER2: SETENA|=0x8000 */
    // NVICISER2 |= (u32_t)0x8000UL;                      

    PORTE_PCR10 = PORT_PCR_MUX(4);  //TXD
    PORTE_PCR7  = PORT_PCR_MUX(4);  //RXD
    PORTE_PCR12 = PORT_PCR_MUX(4);  //TX_BCLK
    PORTE_PCR11 = PORT_PCR_MUX(4);  //TX_FS
    // PORTE_PCR9  = PORT_PCR_MUX(4);  //RX_BCLK
    //  PORTE_PCR8  = PORT_PCR_MUX(4);  //RX_FS
    // PORTE_PCR6  = PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK ;//MCLK out as I2S MCLK
 

    I2S0_CR = //     Set Configuration register
        /* I2S_CR_SYSCLKEN_MASK | */I2S_CR_SYNCTXFS_MASK | I2S_CR_RFRCLKDIS_MASK | I2S_CR_TFRCLKDIS_MASK
        | I2S_CR_I2SMODE(0)/*1)*/ | I2S_CR_SYN_MASK | I2S_CR_NET_MASK | I2S_CR_SSIEN_MASK;

    I2S0_TMSK = TS_Mask;   /* Set Transmit time slot mask register */

    I2S0_RMSK = TS_Mask;   /* Set Receive time slot mask register */
 
    I2S0_TCR = //    Set Transmit configuration register
        /* I2S_TCR_TFDIR_MASK | I2S_TCR_TXDIR_MASK |*/ I2S_TCR_TXBIT0_MASK | I2S_TCR_TFEN0_MASK | I2S_TCR_TFSL_MASK | I2S_TCR_TEFS_MASK;

    I2S0_TCCR = //    Set Transmit Clock control register
        I2S_TCCR_WL(7) | I2S_TCCR_DC(16-1);
 
    I2S0_RCR = //         Set Receive configuration register
        /*I2S_RCR_RFDIR_MASK | I2S_RCR_RXDIR_MASK |*/ I2S_RCR_RXBIT0_MASK | I2S_RCR_RFEN0_MASK | I2S_RCR_RFSL_MASK | I2S_RCR_REFS_MASK;

    I2S0_RCCR = //    Set Receive Clock control register, actually ignored in SYN mode
        I2S_RCCR_WL(7) | I2S_RCCR_DC(16-1);

    I2S0_FCSR = //   Set FIFO Control/Status register
        I2S_FCSR_RFWM1(AUDIO_NUM_CHANNELS) | I2S_FCSR_TFWM1(AUDIO_NUM_CHANNELS) | I2S_FCSR_RFWM0(AUDIO_NUM_CHANNELS) | I2S_FCSR_TFWM0(AUDIO_NUM_CHANNELS);
 
    I2S0_IER = //     Set Interrupt enable register: DMA only!!
        I2S_IER_RDMAE_MASK | I2S_IER_TDMAE_MASK;// | I2S_IER_ROE0EN_MASK | I2S_IER_TUE0EN_MASK ;

    // I2S_ACNT
    I2S0_ACNT = 0;         /* Clear AC97 control register */
 
    // OSC_CR |= 0X80;
   


    temp = _task_get_id_from_name("Audio");
    Pblock_td_ptr = _task_get_td(temp);

    _int_install_isr( (INT_DMA0+RX_DMA_Chan), BlockDMA, 0);
    _cortex_int_init((INT_DMA0+RX_DMA_Chan), 4, TRUE);
   
    DMA_CR      = DMA_CR_EMLM_MASK | DMA_CR_ERCA_MASK | DMA_CR_EDBG_MASK;  //Round robin DMA
   
    DMA_SADDR(TX_DMA_Chan)          = DMA_SADDR_SADDR(&TxDMA_Buf);
    DMA_SOFF(TX_DMA_Chan)           = DMA_SOFF_SOFF(2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE);      //Jump thru buffers on each transfer
    DMA_ATTR(TX_DMA_Chan)           = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(1);  //All straight 16-bits
                                       //After each minor loop, source moves back to the first buffer, next word
    DMA_NBYTES_MLOFFYES(TX_DMA_Chan)= DMA_NBYTES_MLOFFYES_SMLOE_MASK
                                          | DMA_NBYTES_MLOFFYES_MLOFF(-(AUDIO_NUM_CHANNELS*2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE)+AUDIO_BYTES_PER_SAMPLE)
                                          | DMA_NBYTES_MLOFFYES_NBYTES(AUDIO_NUM_CHANNELS*AUDIO_BYTES_PER_SAMPLE);
    DMA_SLAST(TX_DMA_Chan)          = DMA_SLAST_SLAST(-(2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE
                                    + AUDIO_NUM_CHANNELS*2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE-AUDIO_BYTES_PER_SAMPLE) );  //After Major loop, back to the beginning of each channel buffer
    DMA_DADDR(TX_DMA_Chan)          = DMA_DADDR_DADDR(&I2S0_TX0);   //Send to I2S transmit FIFO
    DMA_DOFF(TX_DMA_Chan)           = DMA_DOFF_DOFF(0);             //Constant address
    DMA_CITER_ELINKNO(TX_DMA_Chan)  = DMA_CITER_ELINKNO_CITER(2*AUDIO_SAMPLES_PER_BLOCK);
    DMA_DLAST_SGA(TX_DMA_Chan)      = DMA_DLAST_SGA_DLASTSGA(0);
    DMA_CSR(TX_DMA_Chan)            = DMA_CSR_BWC(3);                //No TX interrupts, RX req comes last so do all on RX ints
    DMA_BITER_ELINKNO(TX_DMA_Chan)  = DMA_BITER_ELINKNO_BITER(2*AUDIO_SAMPLES_PER_BLOCK);
   
    DMA_SADDR(RX_DMA_Chan)          = DMA_SADDR_SADDR(&I2S0_RX0);   //pull from I2S receive FIFO
    DMA_SOFF(RX_DMA_Chan)           = DMA_SOFF_SOFF(0);
    DMA_ATTR(RX_DMA_Chan)           = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(1);  //All straight 16-bits
                                       //After each minor loop, dest moves back to the first buffer, next word
    DMA_NBYTES_MLOFFYES(RX_DMA_Chan)= DMA_NBYTES_MLOFFYES_DMLOE_MASK
                                          | DMA_NBYTES_MLOFFYES_MLOFF(-(AUDIO_NUM_CHANNELS*2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE)+AUDIO_BYTES_PER_SAMPLE)
                                          | DMA_NBYTES_MLOFFYES_NBYTES(AUDIO_NUM_CHANNELS*AUDIO_BYTES_PER_SAMPLE);
    DMA_SLAST(RX_DMA_Chan)          = DMA_SLAST_SLAST(0);
    DMA_DADDR(RX_DMA_Chan)          = DMA_DADDR_DADDR(&RxDMA_Buf);
    DMA_DOFF(RX_DMA_Chan)           = DMA_DOFF_DOFF(2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE);      //Jump thru buffers on each transfer
    DMA_CITER_ELINKNO(RX_DMA_Chan)  = DMA_CITER_ELINKNO_CITER(2*AUDIO_SAMPLES_PER_BLOCK);
    DMA_DLAST_SGA(RX_DMA_Chan)      = DMA_DLAST_SGA_DLASTSGA(-(2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE
                                    + AUDIO_NUM_CHANNELS*2*AUDIO_SAMPLES_PER_BLOCK*AUDIO_BYTES_PER_SAMPLE-AUDIO_BYTES_PER_SAMPLE) );  //After Major loop, back to the beginning of each channel buffer
    DMA_CSR(RX_DMA_Chan)            = DMA_CSR_BWC(3) | DMA_CSR_INTHALF_MASK | DMA_CSR_INTMAJOR_MASK;
    DMA_BITER_ELINKNO(RX_DMA_Chan)  = DMA_BITER_ELINKNO_BITER(2*AUDIO_SAMPLES_PER_BLOCK);
   
    DMAMUX_CHCFG(TX_DMA_Chan) = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(15);
    DMAMUX_CHCFG(RX_DMA_Chan) = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(14);
   
    DMA_SERQ    = DMA_SERQ_SERQ(RX_DMA_Chan);         //All set to go, enable DMA channel(s)!
    DMA_SERQ    = DMA_SERQ_SERQ(TX_DMA_Chan);
   
    I2S0_CR |= I2S_CR_RE_MASK | I2S_CR_TE_MASK;          /* Enable the IIS device */
}

/******************************************************************************
 *  End of file
 *****************************************************************************/

967 Views
JimCheng
Contributor II

Dear Egoodii,

 

Many thanks for your help. i'll try your codes.

0 Kudos
Reply