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.

McBSP DMA with mismated RX and TX word sizes

As they title may explain, I've got a ADC and a DAC both on one McBSP, the ADC has to be 32bit stereo, similar to I2S but without the leading 0.  The DAC is 16bit stereo, right hand justifed with no padding bits.  The clocks for the DAC are derived from the ADC clock via the SRG.  Both sides have a sample rate of 96kHz, both DMA controllers are set to use double indexing, the RX one set to 32 bits, the TX one set to 16 bit.  The TX DMA routine is triggered by a flag that is set in the RX DMA ISR

What is happening is a speed mismatch problem, which as far as I can see shouldn't happen, there is nothing in the documentation that says the McBSPs RX and TX need to run at the same speed.

When I configure the RX side to be 16 bit single indexed and ignore the rest of the data from the ADC everything runs as expected.  If I set the RX side to 32 bits it still runs fine.  As soon as I change it to double indexing which as to be 32 bit the TX side decides it needs to send twice as many frames and I get dead patches in the DAC output. 

I can set the TX part of the McBSP to 32 bits, as the DAC is RH justifed it copes with the leading zeros, but this does not help the timing.

From the test I have run it seems that this only happens when the RX DMA has to handle more data then the TX DMA.

I've attached some of the code.

 SOME SETUP CODE

// set up the MCBSP
    portRegSet(0x4000, 0x2C04);    // SPCR1
    portRegSet(0x0000, 0x2C05);    // SPCR2
    portRegSet(0x0A8F, 0x2C12);    // PCR
    portRegSet(0x01A0, 0x2C06);    // RCR1
    portRegSet(0x00A0, 0x2C07);    // RCR2
    portRegSet(0x01A0, 0x2C08);    // XCR1 16 = 0x0140, 32 = 0x01A0
    portRegSet(0x00A0, 0x2C09);    // XCR2 16 = 0x0040, 32 = 0x00A0
    portRegSet(0x1F00, 0x2C0A);    // SRGR1 16 = 0x0F01, 32 = 0x1F00
    portRegSet(0x103F, 0x2C0B);    // SRGR2 16 = 0x101F, 32 = 0x103F
    portRegSet(0x0000, 0x2C03);    // DXR2

 

 THIS IS THE MAIN LOOP

while(1) {
        // main loop, check to see if some data is ready, if it is, then send it back out
        while(!dataReady);    // wait for data
             
        // we should have already loaded the next output DMA register set
        // so all we need to do is start the transfer
        DMA_FSETH(hDmaTx, DMACCR, ENDPROG, 1);    // starts the next transfer
       
        dataReady = 0;
           
        if(dmaTxBufferNum == 1)
            dmaTxBufferNum = 0;
        else
            dmaTxBufferNum++;
           
                 
        // check to see if we can update the register values
        while(DMA_FGETH(hDmaTx, DMACCR, ENDPROG));
       
        // program the registers for the next transfer
        srcAddrHi = (Uint16)(((Uint32)dmaTxBufferPtr[dmaTxBufferNum]) >> 15) & 0xFFFFu;
        srcAddrLo = (Uint16)(((Uint32)dmaTxBufferPtr[dmaTxBufferNum]) << 1) & 0xFFFFu;
        DMA_RSETH(hDmaTx, DMACSSAU, srcAddrHi);
        DMA_RSETH(hDmaTx, DMACSSAL, srcAddrLo);
    }
}

RX DMA ISR

interrupt void dmaRxISR(void) {
    // we have filled up a buffer
    // swap to the next buffer
    Uint16 dstAddrHi, dstAddrLo;
    Uint16 reg;
       
    if(dmaRxBufferNum == 1)
            dmaRxBufferNum = 0;
        else
            dmaRxBufferNum++;
  
    // signal that data is ready
    dataReady = 1;
  
    dstAddrHi = (Uint16)(((Uint32)dmaRxBufferPtr[dmaRxBufferNum]) >> 15) & 0xFFFFu;
    dstAddrLo = (Uint16)(((Uint32)dmaRxBufferPtr[dmaRxBufferNum]) << 1) & 0xFFFFu;
   
    // make sure we can program the next transfer by checking the ENDPROG bit
    while(DMA_FGETH(hDmaRx,DMACCR,ENDPROG));
   
    // program the next destination address
    DMA_RSETH(hDmaRx, DMACDSAU, dstAddrHi);
    DMA_RSETH(hDmaRx, DMACDSAL, dstAddrLo);
 
    // Set programmation bit to 1, ENDPROG = 1
    DMA_FSETH(hDmaRx, DMACCR, ENDPROG, 1);
}

 

  • Although I've only used matching send/receive word sizes, and I'm not sure I follow everything you're doing, my first thought is that perhaps you're not fully utilizing the DMA interrupts.  Where is your TX DMA ISR?

    Personally, I use the DMA mode which reloads DMA registers at the end of every cycle.  I then use the HALF and BLOCK interrupts from the DMA controller to manage dual buffers similarly to the way your code is doing it manually.  This approach does require that I synchronize my code outside the interrupts so that it can detect when each half of the buffers has competed.  Then again, perhaps I'm just not reading your pseudo code very well.  If I get a chance, I'll dive into your DMA register settings to see if there is an obvious problem.

    I have a hunch that you should run the TX routine from the interrupt, but just start the transmit after the first half of the RX buffers has been received.  From then on, with proper clocking, RX and TX should be in sync, but entirely run from interrupt code.  ... unless I've missed something.

    On another front, I have been trying to separate the RX and TX sides of a McBSP port with little luck.  I have an RX-only situation where I don't really need the TX side, yet I cannot get it to work.  So far, the only workaround is to fake things by putting the chip into SPI mode, which ties the RX and TX halves together, and I've had to rewire my boards to send the CLKR signal to the CLKX pin and the FSR signal to the FSX pin.  I'm still looking for an answer on how to separate RX and TX.  Perhaps you're suffering from a similar problem.

  • Cristian

    Dİd you solve your problem?

    Fozay