This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

SSI emulate I2S on DK-129x

Other Parts Discussed in Thread: TLV320AIC3107EVM-K

Hi everybody, I new here and I need your experience and assistance. I am trying to emulate I2S by SSI  on DK-129x kit and TLV320AIC3107EVM-k. Base on www.ti.com/.../spma042b.pdf

I meet a problem on my code it loop on SoundIntHandle() when playing audio file.

I use SSI2 for I2S chanel 0 and SSI0 for I2c channel 1.

Can you check which problem on my confiure ?

Here is my code configure:

//*****************************************************************************
//
// Definitions of psuedo I2S pins used for the sound driver.
//
//*****************************************************************************
#define I2S_CHAN0_PERIPH        (SYSCTL_PERIPH_SSI2)
#define I2S_CHAN0_GPIO          (SYSCTL_PERIPH_GPIOD)
#define I2S_CHAN0_CLK           (GPIO_PD3_SSI2CLK)
#define I2S_CHAN0_FSS           (GPIO_PD2_SSI2FSS)
#define I2S_CHAN0_RX            (GPIO_PD0_SSI2XDAT1)
#define I2S_CHAN0_TX            (GPIO_PD1_SSI2XDAT0)
#define I2S_CHAN0_PORT          (GPIO_PORTD_BASE)
#define I2S_CHAN0_BASE          (SSI2_BASE)
#define I2S_CHAN0_UDMA_TX       (UDMA_CH13_SSI2TX)
#define I2S_CHAN0_INT           (INT_SSI2)

#define I2S_UDMA_PERIPH         (SYSCTL_PERIPH_UDMA)

#define I2S_CHAN1_PERIPH        (SYSCTL_PERIPH_SSI0)
#define I2S_CHAN1_GPIO          (SYSCTL_PERIPH_GPIOA)
#define I2S_CHAN1_CLK           (GPIO_PA2_SSI0CLK)
#define I2S_CHAN1_RX            (GPIO_PA5_SSI0XDAT1)
#define I2S_CHAN1_TX            (GPIO_PA4_SSI0XDAT0)
#define I2S_CHAN1_PORT          (GPIO_PORTA_BASE)
#define I2S_CHAN1_BASE          (SSI0_BASE)
#define I2S_CHAN1_UDMA_TX       (UDMA_CH11_SSI0TX)
#define I2S_CHAN1_INT           (INT_SSI0)
#define I2S_CHAN1_FSS_GPIO_BASE (GPIO_PORTE_BASE)
#define I2S_CHAN1_FSS           (GPIO_PIN_0)

#define I2S_MASTERCLK_PERIPH    (SYSCTL_PERIPH_TIMER5)
#define I2S_MASTERCLK_GPIO      (SYSCTL_PERIPH_GPIOM)
#define I2S_MASTERCLK_PORT      (GPIO_PM7_T5CCP1)
#define I2S_MASTERCLK_BASE      (GPIO_PORTM_BASE)
#define I2S_MASTERCLK_PIN       (GPIO_PIN_7)
#define I2S_MASTERCLK_TMR       (TIMER_B)
#define I2S_MASTERCLK_TMR_B     (TIMER5_BASE)

//*****************************************************************************
//
//! Initialize the sound driver.
//!
//! This function initializes the hardware components of the LM4F board,
//! necessary for audio playback. 
//!
//! \return None.
//
//*****************************************************************************
void
SoundInit(void)
{
    //
    // Set the current active buffer to zero.
    //
    g_ulPlaying = 0;
    //
    // Initialize the DAC.
    //
    DACInit();
    //
    // Enable and reset the necessary peripherals.
    //
    SysCtlPeripheralEnable(I2S_CHAN1_PERIPH);
    SysCtlPeripheralEnable(I2S_CHAN0_PERIPH);
    SysCtlPeripheralEnable(I2S_CHAN1_FSS_GPIO_BASE);
    SysCtlPeripheralEnable(I2S_UDMA_PERIPH);
    SysCtlPeripheralEnable(I2S_CHAN0_GPIO);
    SysCtlPeripheralEnable(I2S_CHAN1_GPIO);    
    SysCtlPeripheralReset(I2S_CHAN0_GPIO);
    SysCtlPeripheralReset(I2S_CHAN1_GPIO);
    

    //
    // Set up Timer to periodically call WavePlayContinue().
    // Timer is enabled when playback is started.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
    TimerConfigure(TIMER2_BASE, TIMER_CFG_PERIODIC);

    //
    // Set up the pin mux for  both SSI ports.
    //
    GPIOPinConfigure(I2S_CHAN1_CLK);
    GPIOPinConfigure(I2S_CHAN1_FSS);
    GPIOPinConfigure(I2S_CHAN1_RX);
    GPIOPinConfigure(I2S_CHAN1_TX);
    GPIOPinConfigure(I2S_CHAN0_RX);
    GPIOPinConfigure(I2S_CHAN0_TX);
    GPIOPinConfigure(I2S_CHAN0_CLK);
    GPIOPinConfigure(I2S_CHAN0_FSS);
    GPIOPinTypeGPIOOutput(I2S_CHAN1_FSS_GPIO_BASE, I2S_CHAN1_FSS);
    

    //
    // Select alternate functions for all of the SSI pins.
    //
    GPIOPinTypeSSI(I2S_CHAN0_PORT,
                   I2S_MASTERCLK_PIN|GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_0);
    
    GPIOPinTypeSSI(I2S_CHAN1_PORT, 
                   I2S_MASTERCLK_PIN|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5);
    

    //
    // Set up the DMA.
    //
    ROM_uDMAControlBaseSet(&sDMAControlTable[0]);
    ROM_uDMAEnable();

    

    //
    // Configure the SSI clock (CCP timer).
    //
    setupCCP();

    //
    // Clear out all pending interrupts.
    // SSI_TXFF: TX FIFO half full or less
    //
    SSIIntClear(I2S_CHAN0_BASE, SSI_TXFF );
    SSIIntClear(I2S_CHAN1_BASE, SSI_TXFF );

    //
    // Enables individual SSI interrupt source.
    // We'll use SSI1's empty interrupt status to drive the FIFO transfers
    // for both SSI1 and SSI3. This interrupt occurs whenever the TX FIFO
    // is half full or less. 
    //
    SSIIntEnable(I2S_CHAN0_BASE, SSI_TXFF);
    //SSIIntEnable(I2S_CHAN1_BASE, SSI_TXFF);

    //
    // Disable all uDMA attributes.
    //
    uDMAChannelAttributeDisable(I2S_CHAN0_UDMA_TX, UDMA_ATTR_ALL);
    uDMAChannelAttributeDisable(I2S_CHAN1_UDMA_TX, UDMA_ATTR_ALL);

    //
    // Configure the synchronous serial interfaces, SSI1 and SSI3.
    // Clock each at the system clock rate of 50MHz.
    // Use the Motorola Mode 2 protocol.
    // As the DAC is the master, set the SSI as a slave device.
    // Set the bit rate to 1M.
    // Set the width of the data transfers to the maximum : 16bits.
    //
    SSIConfigSetExpClk(I2S_CHAN0_BASE, g_ui32SysClock, SSI_FRF_MOTO_MODE_2,
                       SSI_MODE_SLAVE, 1000000, 16);
    SSIConfigSetExpClk(I2S_CHAN1_BASE, g_ui32SysClock, SSI_FRF_MOTO_MODE_2,
                       SSI_MODE_SLAVE, 1000000, 16);
    //
    // Enable the SSI controller.
    //
    SSIEnable(I2S_CHAN0_BASE);
    SSIEnable(I2S_CHAN1_BASE);

    //
    // Enable SSI DMA transmit operation.
    //
    SSIDMAEnable(I2S_CHAN0_BASE, SSI_DMA_TX);
    SSIDMAEnable(I2S_CHAN1_BASE, SSI_DMA_TX);
}
//*****************************************************************************
//
// Interrupt handler for the SSI sound driver.
//
// This interrupt function is called by the processor due to an interrupt from
// the SSI peripheral. uDMA is used in ping-pong mode to keep sound buffer
// data flowing to the dual-SSI audio output.  As each buffer transfer is
// complete, the client callback function that was specified in the call
// to SoundBufferPlay() will be called.  The client can then take action
// to start the next buffer playing.
//
// This function is called by the interrupt system and should not be
// called directly from application code.
//
//*****************************************************************************
void
SoundIntHandler(void)
{
    unsigned long ulStatus;
    unsigned long *pulTemp;

    //
    // Get the interrupt status and clear any pending interrupts.
    //
    ulStatus = SSIIntStatus(I2S_CHAN0_BASE, 1);
    SSIIntClear(I2S_CHAN0_BASE, ulStatus);  

    //
    // Handle the TX channel interrupt
    //
    if(HWREGBITW(&g_ulDMAFlags, FLAG_TX_PENDING))
    {
        //
        // If the TX DMA is done, then call the callback if present.
        //
        if(ROM_uDMAChannelModeGet(I2S_CHAN0_UDMA_TX | UDMA_PRI_SELECT) ==
           UDMA_MODE_STOP)
        {
            //
            // Save a temp pointer so that the current pointer can be set to
            // zero before calling the callback.
            //
            pulTemp = g_sOutBuffers[0].pulData;

            //
            // If at the mid point then refill the first half of the buffer.
            //
            if((g_sOutBuffers[0].pfnBufferCallback) &&
               (g_sOutBuffers[0].pulData != 0))
            {
                g_sOutBuffers[0].pulData = 0;
                g_sOutBuffers[0].pfnBufferCallback(pulTemp, BUFFER_EVENT_FREE);
            }
        }

        //
        // If the TX DMA is done, then call the callback if present.
        //
        if(ROM_uDMAChannelModeGet(I2S_CHAN0_UDMA_TX | UDMA_ALT_SELECT) ==
           UDMA_MODE_STOP)
        {
            //
            // Save a temporary pointer so that the current pointer can be set
            // to zero before calling the callback.
            //
            pulTemp = g_sOutBuffers[1].pulData;

            //
            // If at the mid point then refill the first half of the buffer.
            //
            if((g_sOutBuffers[1].pfnBufferCallback) &&
               (g_sOutBuffers[1].pulData != 0))
            {
                g_sOutBuffers[1].pulData = 0;
                g_sOutBuffers[1].pfnBufferCallback(pulTemp, BUFFER_EVENT_FREE);
            }
        }

        //
        // If no more buffers are pending then clear the flag.
        //
        if((g_sOutBuffers[0].pulData == 0) && (g_sOutBuffers[1].pulData == 0))
        {
            HWREGBITW(&g_ulDMAFlags, FLAG_TX_PENDING) = 0;
        }
    }
}

  • Note that (many) ARM MCUs handle I2S (properly) - no performance reducing "emulation" required. Even if/should you succeed - be aware of the "less than Stellar" results you'll achieve.

    ARM MCUs offer a rich playing field - limiting device selection may not (always) best serve you...
  • Hi cb1,
    Thank your reply, but on my project we need use Tm4c129, and right now TI doesn't support I2C on Tm4C.
    Can you check configure code for device which I add above?
  • Hello Huy,

    Did you check the errata? SSI when using DMA has an issue due to which the Interrupt Handler may be called continuously.

    Regards
    Amit
  • Greetings Huy,

    Huy Huynh99 said:
    we need use Tm4c129

    That's unfortunate - is it not?   And, "Why is that" - that (limited) selection seems NOT, "Match made in heaven" for your "I2S" objective.

    Let's list:

    • NO dedicated I2S peripheral
    • Instead a, Software Emulation only, "cobble"
    • AND a delightful visit by (overlapping) Errata  

    Do not "facts in evidence" argue (strongly) for course correction?   Sailing w/in 30° of the prevailing wind (even minus sail errata) is unlikely to propel you toward your destination...