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.

Problem with Configuration of I2S using DMA and Interrupts

I'm looking to develop a system that can read two channels of I2S using DMA and provide interrupts when the DMA has transferred new data.

(i.e. I need a notification when Left and Right channels have received data and DMA has copied it to memory)

I started with the example code "I2S EXAMPLE2 - DMA MODE TEST" which uses DMA, but in polled mode.

I tried modifying this to allow interrupts to be received, but I only get an interrupt when the Right channel has data, nothing on the Left.

Am I using the DMA correctly? Is there a better way of doing this?

[see attached zip for details]

Modified_CSL_I2S_DMAExample_Out.zip

  • Hi Paul,

    If you use the CSL_I2S_DMA_MultipleInstanceExample with the I2S_open ....DMA_INTERRUPT... changes, are you still seeing nothing on the left?

    Also note that there is also an interrupt based example,  CSL_I2S_INTCExample. Any specific reason you modified CSL_I2S_DMAExample?

    Lali

  • Modified2_CSL_I2S_DMAExample_Out.zip

    Hi Lali,

    Thanks for your reply. I chose the "CSL_I2S_DMAExample"  because I only need to read from one I2s port  and I'd like to use DMA for efficiency. The "CSL_I2S_INTCExample" doesnt use DMA and I believe the "CSL_I2S_DMA_MultipleInstanceExample"  reads from multiple ports. So it seemed like this was the closest to what I want to do. I'm just having difficulty understanding how the interrupts work.

    Ideally, I'd like to use DMA to read from the I2S (both left and right channels) and give me an interrupt when complete. I'd also like to have the DMA restart automatically so that I don't need to make any additional calls to receive data. Can you point me to any further examples or documentation that explain how to do this?

    Also, I made some further changes to the example code and it seems that I am getting a "DMA_EVENT" interrupt for every transmit and receive on left and right channels. However I haven't enabled interrupts on the left and right transmit, yet I get interrupts when I transmit (i.e. when I call DMA_start(dmaLeftTxHandle) or DMA_start(dmaRightTxHandle).

    Furthermore, upon an interrupt, when I read the DMAIFR register it shows an interrupt set on 'DMA controller 0 channel 3' every time, regardless of what DMA channel I was setup to send or receive on (e.g. CSL_configDmaForI2s(CSL_DMA_CHAN2))

    Any thoughts?

    Thanks,

    Paul

  • The best way to do this is to use I2S with Ping-Pong DMA mode. It is the most efficient way. The details can be found in SPRUFT2A, page 15. I also can share some code for initialization if you are interested.

    Cheers,
    Stan
  • Hi Stan,
    Thanks for the info. I have started reading about the ping pong mode in SPRUFT2A.
    It looks like this might be what I'm looking for.
    Yes, if you have any code that demonstrates the initialization, that would be very useful to me.
    Thank you in advance,
    Paul
  • Int16 I2S_Initialize(void)
    {
    	CSL_Status status;
    
    	/* Open device with Instance 1 */
    	hI2s = I2S_open(I2S_INSTANCE1, DMA_INTERRUPT, I2S_CHAN_STEREO);
    	if(NULL == hI2s)
    		return I2SCTRL_OPEN_FAIL;
    
    	/* Set the configuration for the I2S Peripheral */
    	hwConfig.dataType		= I2S_STEREO_ENABLE;	    // Stereo, we have two microphones
    	hwConfig.loopBackMode 	        = I2S_LOOPBACK_DISABLE;     // TESTING PURPOSES: loop-back disabled
    	hwConfig.fsPol 			= I2S_FSPOL_LOW;	    // Left channel selected when low
    	hwConfig.clkPol 		= I2S_RISING_EDGE;
    	hwConfig.datadelay 		= I2S_DATADELAY_ONEBIT;	    // Delay one bit
    	hwConfig.datapack		= I2S_DATAPACK_DISABLE;	    // Not supported in 24-bit mode
    	hwConfig.signext 		= I2S_SIGNEXT_DISABLE;	    // Disable sign extend
    	hwConfig.wordLen 		= I2S_WORDLEN_24;	    // Microphone 24-bit data word out
    	hwConfig.i2sMode 		= I2S_MASTER;		    // uC as master, Microphone as a slave
    	hwConfig.dataFormat		= I2S_DATAFORMAT_LJUST;     // Left justified
    	hwConfig.clkDiv 		= I2S_CLKDIV128;            // Gives 24Khz from 100MHz clock
    	hwConfig.fsDiv 			= I2S_FSDIV32;              // 32-bit(24-bit data word) from each mic.
    	hwConfig.FError 		= I2S_FSERROR_DISABLE;
    	hwConfig.OuError 		= I2S_OUERROR_DISABLE;
    
    	/* Configure the I2S peripheral */
    	if(I2S_setup(hI2s, &hwConfig) != I2SCTRL_SOK)
    		return I2SCTRL_SETUP_FAIL;
    
    	CSL_I2S1_REGS->I2SINTMASK &= 0xFFD7;
    
    	I2S_transEnable(hI2s, TRUE);
    
    	DMA_init();
    
        /* Set the reset clock cycle */
    	CSL_FINS(CSL_SYSCTRL_REGS->PSRCR, SYS_PSRCR_COUNT, CSL_DMA_RESET_CLOCK_CYCLE);
        CSL_FINST(CSL_SYSCTRL_REGS->PRCR, SYS_PRCR_DMA_RST, RST);
    
        /* Enable the corresponding DMA clock from PCGCR Registers */
        CSL_FINST(CSL_SYSCTRL_REGS->PCGCR1, SYS_PCGCR1_DMA0CG, ACTIVE);
        CSL_FINST(CSL_SYSCTRL_REGS->PCGCR2, SYS_PCGCR2_DMA1CG, ACTIVE);
        CSL_FINST(CSL_SYSCTRL_REGS->PCGCR2, SYS_PCGCR2_DMA2CG, ACTIVE);
        CSL_FINST(CSL_SYSCTRL_REGS->PCGCR2, SYS_PCGCR2_DMA3CG, ACTIVE);
    
        /* Clear all DMA interrupt flags */
        CSL_SYSCTRL_REGS->DMAIFR = 0xFFFF;
    
        IRQ_clear(DMA_EVENT);
    
        /* Enable all DMA interrupts */
        CSL_SYSCTRL_REGS->DMAIER = 0xFFFF;
    
    #ifdef _RIGHT_I2S_CHAN
    	dmaConfig.pingPongMode	= CSL_DMA_PING_PONG_ENABLE;
    	dmaConfig.autoMode		= CSL_DMA_AUTORELOAD_ENABLE;
    	dmaConfig.burstLen		= CSL_DMA_TXBURST_1WORD;
    	dmaConfig.trigger		= CSL_DMA_EVENT_TRIGGER;
    	dmaConfig.dmaEvt		= CSL_DMA_EVT_I2S1_RX;		// Microphone on I2S1 (CSL_DMA_EVT_I2S1_RX)
    	dmaConfig.dmaInt		= CSL_DMA_INTERRUPT_ENABLE;
    	dmaConfig.chanDir		= CSL_DMA_READ;
    	dmaConfig.trfType		= CSL_DMA_TRANSFER_IO_MEMORY;
    	dmaConfig.dataLen		= DMA_BUFFER_SIZE * 4;		// CPU uses word addr, DMA uses byte addr
    	dmaConfig.srcAddr		= (Uint32)(0x292C);		// I2S1 Receive Right Data 0 Register
    	dmaConfig.destAddr		= (Uint32)readRightBuf;
    
    	dmaRightRxHandle = DMA_open(CSL_DMA_CHAN14, &dmaObj, &status);
    	DMA_config(dmaRightRxHandle, &dmaConfig);
    #endif //_RIGHT_I2S_CHAN
    
    #ifdef _LEFT_I2S_CHAN
    	dmaConfig.pingPongMode	= CSL_DMA_PING_PONG_ENABLE;
    	dmaConfig.autoMode		= CSL_DMA_AUTORELOAD_ENABLE;
    	dmaConfig.burstLen		= CSL_DMA_TXBURST_1WORD;
    	dmaConfig.trigger		= CSL_DMA_EVENT_TRIGGER;
    	dmaConfig.dmaEvt		= CSL_DMA_EVT_I2S1_RX;		// Microphone on I2S1
    	dmaConfig.dmaInt		= CSL_DMA_INTERRUPT_ENABLE;     //CSL_DMA_INTERRUPT_ENABLE;
    	dmaConfig.chanDir		= CSL_DMA_READ;
    	dmaConfig.trfType		= CSL_DMA_TRANSFER_IO_MEMORY;
    	dmaConfig.dataLen		= DMA_BUFFER_SIZE * 4;
    	dmaConfig.srcAddr		= (Uint32)(0x2928);		// I2S Receive Left Data 0 Register
    	dmaConfig.destAddr		= (Uint32)readLeftBuf;
    
    	dmaLeftRxHandle = DMA_open(CSL_DMA_CHAN15, &dmaObj, &status);
    	DMA_config(dmaLeftRxHandle, &dmaConfig);
    #endif //_LEFT_I2S_CHAN
    
    	/* Clear DMA Interrupt Flags */
    	if(IRQ_clear(DMA_EVENT) != I2SCTRL_SOK)
    		return I2SCTRL_FLG_CLEAR_FAIL;
    
    	IRQ_setVecs((Uint32)(&VECSTART));
    	IRQ_plug(DMA_EVENT, &dmaISR);
    
    	/* Enable DMA Interrupt */
    	if(IRQ_enable(DMA_EVENT) != I2SCTRL_SOK)
    		return I2SCTRL_INT_FAIL;
    
    #ifdef _RIGHT_I2S_CHAN
    	/* Start DMA Channel 1 */
    	if(DMA_start(dmaRightRxHandle) != I2SCTRL_SOK)
    		return I2SCTRL_DMA1_START_FAIL;
    #endif //_RIGHT_I2S_CHAN
    
    #ifdef _LEFT_I2S_CHAN
    	/* Start DMA Channel 0 */
    	if(DMA_start(dmaLeftRxHandle) != I2SCTRL_SOK)
    		return I2SCTRL_DMA0_START_FAIL;
    #endif //_LEFT_I2S_CHAN
    
    	return I2SCTRL_SOK;
    }
    
    
    interrupt void dmaISR(void)
    {
    	CSL_Status status;
    
    #ifdef _RIGHT_I2S_CHAN
    	if(DMA_getLastTransferType(dmaRightRxHandle, &status) == FALSE)
    	{
    		IOMGR_DebugPrint("\r\n RPing[0]= %8.8lx", (Uint32)readRightBuf[0]);
    	}
    	else
    	{
    		IOMGR_DebugPrint(" RPong[0]= %8.8lx", (Uint32)readRightBuf[256]);
    	}
    #endif //_RIGHT_I2S_CHAN
    
    
    #ifdef _LEFT_I2S_CHAN
    	if(DMA_getLastTransferType(dmaLeftRxHandle, &status) == FALSE)
    	{
    		IOMGR_DebugPrint(" LPing[0]= %8.8lx", (Uint32)readLeftBuf[0]);
    	}
    	else
    	{
    		IOMGR_DebugPrint(" LPong[0]= %8.8lx", (Uint32)readLeftBuf[256]);
    	}
    #endif //_LEFT_I2S_CHAN
    
    	CSL_SYSCTRL_REGS->DMAIFR = 0xFFFF;
    	IRQ_clear(DMA_EVENT);
    }
    

    Hi Stan,
    The ping pong mode is working well for me. It is so much more efficient.
    However I have 2 microphones channels on the I2S bus and can only get one channel to work at a time.
    Problem as I see it is that there is only on DMA_EVENT interrupt. So I can't figure out how it would be possible to use the DMA to read both Left and Right I2S channels. There must be something simple that I'm missing?
    I'll post my startup code.
    Cheers,
    Paul

  • Did not look into your code because do not have much time, sorry. So you have two microphones wired through the codec right? Firstly, you need to configure codec that one mic goes to the left channel and another mic goes to the right channel of I2S. Secondly, if you need to only collect signals from codec then you want to configure 2 DMA channels. Thirdly, synchronization PING and PONG inside of the ISR. If I have time I will cut some code from my project as a example. 

    For now just compare my ISR:

    interrupt void dma_isr(void)
    {
        int ifrValue;
    	CSL_Status 			status;
    
    	FLAGS.bit.DMA_INT = TRUE;
    
      	ifrValue = CSL_SYSCTRL_REGS->DMAIFR;
    	CSL_SYSCTRL_REGS->DMAIFR |= ifrValue;
    
    	if((ifrValue & 0x0010) == 0x0010)				// DMA channel 4 In	
    	{
    		if ((DMA_getLastTransferType (dmaHandle_in_L, &status)) == TRUE)
    		{
    			FLAGS.bit.Pong_IN = TRUE;
    		}
    		else
    		{
    			FLAGS.bit.Ping_IN = TRUE;
    		}
    	}
    
    }

  • Hi Stan,
    Thanks for your response.

    I'm not actually using a codec. I have two digital microphones on the I2S1 bus.
    I believe I have everything else setup as you describe.

    In my code I have two #ifdef for enabling _RIGHT_I2S_CHAN and _LEFT_I2S_CHAN.
    If I enable them independently then the Ping Pong works fine and I get interrupts with the expected value in the DMAIFR register corresponding to the DMA channel I used. However if I enable both LEFT and RIGHT, I only get interrupts from the last DMA channel that I configured. It is as if the config for the second dma channel overwrites the config for first channel.

    Is it possible to have two separate DMA transfers in to separate ping pong buffers in this way?

    Thanks
    Paul
  • Hm interesting. How these two digital microphones are synchronized over I2S? I mean are you sure that data from the first one goes to a left channel and from the second one to a right channel? If so I do not see any problem to read both microphones at the same time. Also pay attention on master of clock for I2S.  Make sure that there is only one master and that two slaves do not interfere with each other. 

    To answer your question I would like to describe a typical system:

    Lets say two microphones are connected to the codec with I2S bus.  This means that two streams can be captured at the same time over I2S(left and right channels). So you need to configure 2 DMA channels for the input (left and right) and 2 DMA channels for the output (left and right). At this case you have to define 8 DMA buffers:

    1. InputLeftPingBuf

    2. InputRightPingBuf

    3. InputLeftPongBuf

    4. InputRightPongBuf

    5. OutputLeftPingBuf

    6. OutputRightPingBuf

    7. OutputLeftPongBuf

    8. OutputRightPongBuf

    Then lets assume that you just need to copy buffers over without any processing:

    So when ISR comes:

    		if(FLAGS.DMA_INT == TRUE)
    		{
    //==============================================================================
    			if(FLAGS.Ping_IN) // PING
    			{
    //				Left
    				memcpy(&OutputLeftPingBuf, &InputLeftPingBuf, (DMA_BUFFER_SIZE));
    //				Right
    				memcpy(&OutputRightPingBuf, &InputRightPingBuf, (DMA_BUFFER_SIZE));
    
    				FLAGS.Ping_IN = FALSE;
    			}
    //==============================================================================
    			if(FLAGS.Pong_IN) // PONG
    			{
    //				Left
    				memcpy(&OutputLeftPongBuf, &InputLeftPongBuf, (DMA_BUFFER_SIZE));
    //				Right
    				memcpy(&OutputRightPongBuf, &InputRightPongBuf, (DMA_BUFFER_SIZE));
    				
    				FLAGS.Pong_IN = FALSE;
    			}
    //==============================================================================
    
    	    FLAGS.DMA_INT = FALSE;
    		}

    Hope that helps.

    Cheers,

    Stan

  • Hi Stan,
    Thanks for your reply. In my system I don't have a codec. I'm reading from left and right channels. The synchronization is done by the I2s1_FS signal that is connected to both microphones and toggles between the two.
    However, I think that you have answered my question about the DMA interrupts by providing your ISR code.
    I was expecting to get an interrupt when the left channel DMA transfer is complete, and then another, when the right channel DMA transfer is complete. I see in your ISR that you read both left and right buffers at the same time, so I guess that you might only be getting a single interrupt just as I am and then you assume that both channels have completed and copy the data. In my system I will try to do the same. As long as both DMA transfers have completed when I get an interrupt then I should have data ready in both buffers I guess.
    Thanks for you help.
    Paul
  • Hi Paul,

    Glad to help you.

    That is right. Every time when ISR comes both channels are ready for copying and you just need to get which buffer is that Ping or Pong. Make sure that you process data somewhere in the task or in a function that is outside of ISR. Keep DMA ISR as fast as possible.

    Cheers,

    Stan