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.

C5515 I2S DMA event triggering



Hi Guys,

I'm wondering if anyone can help me figure this out. I understand that it is possible to get I2S receive events to trigger the DMA, but I am wondering what the purpose of this is.

I am hoping that this means it is somehow possible to create a situation where an interrupt is only generated once, say, 16 stereo samples have been collected, but I'm not sure how or if this is possible.

I know that it is possible to set a start address and end address for a transfer, but presumably this would be a continuous process and would start and end after just the first I2SnRx event.

I also know that it is possible to post increment the destination address after the transfer is complete, but I'm not sure how you would get this to wrap round, or would you simply reconfigure it once the 16th sample has been receieved?

My ideal situation is that I have a ping-pong buffered transfer of samples from the I2S device to some place in memory that only triggers an interrupt each time a given number of samples has been collected and this process would automatically loop. Is this possible, or nearly possible with some resetting involved?

  • Just to answer a few of your questions:

    1. The I2s events are internal events the i2s module sends to the DMA controller for initiating another DMA transfer. These events are independent of the interrupt flags.

    2. I'd recommend using the DSP chip support library routines for what your'e trying to accomplish. See SPRU133 on the ti website to download it. You should be able to configure the DMA to transfer a specified number of bytes by configuring the CSL_DMA_Config -> dateLen parameters and use the DMA_Config() routine to get 'r done. THere's a csl_i2s_dmaExample.c as well as other i/o port examples using DMA.

    3. Yes, I'd recommend using the ping-pong buffers with the autoreload feature to accomplish your mission.

  • Thanks for the reply.

    1. I was aware of this - what im trying to do is relieve the 1:1 correspondance of stereo samples to interrupts - a single stereo sample event driven DMA transfer would not help this as I would still need to service the interrupt driven by the DMA transfer completing.

    2/3 - I'm still not sure how to get the DMA to do this with multiple samples - from what I can see, the 16 (or whatever) samples would already have to be present as, as far as I can tell, you can only get the DMA controller to loop once per event where I need it to do something like the following pseudo code tries to illustrate:

    while(1){

      for(sample = 0; sample < 16; sample++)

      {

        wait for sample driven event;       // to trigger the DMA transfer

        take new stereo sample and store in one of ping-pong buffers (e.g. buf_A_left[sample], buf_A_right[sample)]       

        // note how this transfer repeats itself, but to 16/32 different destinations AND with a wait  between them for a new sample (which I do not believe is possible without having individual sample transfers from the DMA and resetting it each time, which is of no benefit)

      }

      trigger DMA interrupt for 16 stereo samples now stored in buffer;           //this interrupt would contain the code where the cpu is set to handle the new data, perhaps here I can simply have the DMA post incrementing the destination and each 16 times it executes I can reset the DMA controller

      swap ping-pong buffers used;

    } //repeat ad infinitum

     

    Obviously this is the behaviour of the DMA controller and not executed code as such, but im trying to describe the desired behaviour. 

    I don't know if my understanding of the DMA controller is flawed here - but has anyone tried this kind of thing, where the cpu only needs to get involved, with an interrupt or otherwise, once a given block of samples is ready?

  • Your desired algorithm is quite possible and might be implemented as follows:

    // pseudo code for reading in 16 x 32 bit samples from the left channel of the i2s2 port

    #define kMAX_SENSOR_WORDS 16  // Important must even for DMA

    typedef struct  SampleData_tag {

      uint16_t buf[kMAX_SENSOR_SAMPLES];

    } SampleData_t;

    #pragma DATA_ALIGN(mSampleLPingPongBuf, 2)  // align to a 32 bit boundary for DMA

    static SampleData_t mSampleLPingPongBuf[2]; // 2 for ping-pong buffer scheme (handled in software)

    static uint16_t mLBufIdx;

    // setup the DMA channel as follows for DMA_CHAN4

     dmaConfig.pingPongMode = CSL_DMA_PING_PONG_DISABLE;  // software isr will do the ping pong buffer management
     dmaConfig.autoMode     = CSL_DMA_AUTORELOAD_DISABLE;    // software will initiate each transfer
     dmaConfig.burstLen     = CSL_DMA_TXBURST_1WORD;  // 1x 16 bit transfer for i2s event
     dmaConfig.trigger      = CSL_DMA_EVENT_TRIGGER;
     dmaConfig.dmaEvt       = CSL_DMA_EVT_I2S2_RX;
     dmaConfig.dmaInt       = CSL_DMA_INTERRUPT_ENABLE;
     dmaConfig.chanDir      = CSL_DMA_READ;
     dmaConfig.trfType      = CSL_DMA_TRANSFER_IO_MEMORY;
     dmaConfig.dataLen      = kMAX_SENSOR_WORDS*2; // convert to bytes (must be a multiple of 4)
     dmaConfig.srcAddr      = (Uint32)&(hI2s->hwRegs->I2SRXLT0);   // 0x2A28 for the i2s2 receive regiter
     dmaConfig.destAddr     = (Uint32)mSampleLPingPongBuf;
     dmaLeftRxHandle = CSL_configDmaForI2s(CSL_DMA_CHAN4);

    mLBufIdx = 1; // set index to 1 so it's the first one we'll kick off when the dma interrupt comes in

    // setup the peripheral port in this case the i2s2 port

    // setup the DMA interrupt

     

    // DMA isr

    interrupt void dma_isr(void)
    {
       int ifrValue = CSL_SYSCTRL_REGS->DMAIFR;

      if (CSL_SYS_DMAIFR_DMA1CH0IF_MASK & ifrValue) {

         // kick off the next DMA (Note we don't call the DMA_Config routines because their just too heavyweight !)

        // reset the source address to point to the I2SRXLT0 register
        /** shift value to change cpu addr for DMA    */
        CSL_DMA1_REGS->DMACH0DSAL =  ((Uint16) &mDmaI2S2PingPongBufRx[mpingIdx]) << CSL_DMA_ADDR_SHIFT;
        CSL_DMA1_REGS->DMACH0SSAL = (Uint16) &CSL_I2S2_REGS->I2SRXLT0;  // same as &(hI2s->hwRegs->I2SRXLT0)
        CSL_DMA1_REGS->DMACH0TCR2 |= CSL_DMA_DMATCR2_DMASTART_SYNC_MASK;
        // acknowledge & clear the interrupt for the current DMA transfer that just completed
        CSL_SYSCTRL_REGS->DMAIFR = CSL_SYS_DMAIFR_DMA1CH0IF_MASK; 

        mpingIdx = !mpingIdx;   

        // mpingIdx  now is set to the buffer that was just filled by the DMA

         doWork(&mSampleLPingPongBuf[mpingIdx ] ,  kMAX_SENSOR_SAMPLES);  // process the n samples that were filled in by the DMA

     }

    }

     

    Hope this helps.

     

  • Thanks for the extensive response.

    Whilst I'm still trying to get my head completely around this code - I'm still unsure if this can be advantageous over simply using the I2S ISR to put the samples where I need them and increment a counter to keep track of how many samples collected.

    What I was really hoping to do was to reduce the number of interrupts being produced from 1:1 to 1:16 samples - Perhaps the best I can do is 1:2 by packing the samples. Surely it is inefficient to set the DMA to transfer only a very few words since the instructions used to set it up may consume the same amount of cpu time?

    Please correct me if I am mistaken as I am still not very far up the learning curve, but I really can't see how having the DMA triggered by I2S events can be beneficial as surely you have to service the DMA interrupt once complete in a similar way to the way you would have had to service the I2S interrupt. Perhaps it is only beneficial when you have many I2S devices and you want all the samples to be collected automatically, but surely you will still have up to 4 DMA event triggers and subsequent interrupts, unless you poll them in software...